diff --git a/CMakeLists.txt b/CMakeLists.txt
index e9255bff6ac523e8bfdf61fe42044e4366d3993f..4b302fe373681cdc862851e871c53d3842d0d842 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -573,6 +573,7 @@ target_link_libraries(
   pugs
   PugsMesh
   PugsAlgebra
+  PugsAnalysis
   PugsUtils
   PugsLanguage
   PugsLanguageAST
@@ -606,7 +607,7 @@ if(${CMAKE_VERSION} VERSION_LESS "3.13.0")
     LIBRARY DESTINATION lib
     ARCHIVE DESTINATION lib)
 else()
-  install(TARGETS pugs  PugsAlgebra PugsUtils PugsLanguage PugsLanguageAST PugsLanguageModules PugsLanguageAlgorithms  PugsLanguageUtils PugsMesh PugsScheme PugsOutput
+  install(TARGETS pugs PugsAlgebra PugsAnalysis PugsUtils PugsLanguage PugsLanguageAST PugsLanguageModules PugsLanguageAlgorithms  PugsLanguageUtils PugsMesh PugsScheme PugsOutput
 
     RUNTIME DESTINATION bin
     LIBRARY DESTINATION lib
diff --git a/packages/CLI11/.all-contributorsrc b/packages/CLI11/.all-contributorsrc
index f699ad2b03d93291886734c05d338cadd23269b0..7de67853e063fa7ea75d853506c59ea571d265ef 100644
--- a/packages/CLI11/.all-contributorsrc
+++ b/packages/CLI11/.all-contributorsrc
@@ -440,6 +440,42 @@
       "contributions": [
         "doc"
       ]
+    },
+    {
+      "login": "paddy-hack",
+      "name": "Olaf Meeuwissen",
+      "avatar_url": "https://avatars.githubusercontent.com/u/6804372?v=4",
+      "profile": "https://github.com/paddy-hack",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "dryleev",
+      "name": "dryleev",
+      "avatar_url": "https://avatars.githubusercontent.com/u/83670813?v=4",
+      "profile": "https://github.com/dryleev",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "AnticliMaxtic",
+      "name": "Max",
+      "avatar_url": "https://avatars.githubusercontent.com/u/43995389?v=4",
+      "profile": "https://github.com/AnticliMaxtic",
+      "contributions": [
+        "code"
+      ]
+    },
+    {
+      "login": "alexdewar",
+      "name": "Alex Dewar",
+      "avatar_url": "https://avatars.githubusercontent.com/u/23149834?v=4",
+      "profile": "https://profiles.sussex.ac.uk/p281168-alex-dewar/publications",
+      "contributions": [
+        "code"
+      ]
     }
   ],
   "contributorsPerLine": 7,
diff --git a/packages/CLI11/.appveyor.yml b/packages/CLI11/.appveyor.yml
index a4ae11883dc71dfee74dd77f83747897ba4d01d3..59bb8326b6a00dcb870df92f866cd9f50f210d8e 100644
--- a/packages/CLI11/.appveyor.yml
+++ b/packages/CLI11/.appveyor.yml
@@ -1,8 +1,8 @@
-version: 1.9.1.{build}
+version: 2.1.2.{build}
 
 branches:
   only:
-    - master
+    - main
     - v1
 
 install:
diff --git a/packages/CLI11/.ci/azure-build.yml b/packages/CLI11/.ci/azure-build.yml
index 06d60cec91733405a0e03e1c84ef899e59001248..f51620c52b512f376e0663da65ba7360951b9415 100644
--- a/packages/CLI11/.ci/azure-build.yml
+++ b/packages/CLI11/.ci/azure-build.yml
@@ -8,4 +8,3 @@ steps:
 - script: cmake --build .
   displayName: 'Build'
   workingDirectory: build
-
diff --git a/packages/CLI11/.ci/azure-test.yml b/packages/CLI11/.ci/azure-test.yml
index ec1d1f55633b2ee51630d1ce042f6728a74e780e..cf89a99957a02488a4208fac757cdc454168678a 100644
--- a/packages/CLI11/.ci/azure-test.yml
+++ b/packages/CLI11/.ci/azure-test.yml
@@ -8,5 +8,3 @@ steps:
   inputs:
     testResultsFormat: 'cTest'
     testResultsFiles: '**/Test.xml'
-
-
diff --git a/packages/CLI11/.ci/build_doxygen.sh b/packages/CLI11/.ci/build_doxygen.sh
index 4474f696ab76f493077f834873b4a55f5bab0246..bd877d53eff538b421943a44ed0f246c0a5b5b1b 100644
--- a/packages/CLI11/.ci/build_doxygen.sh
+++ b/packages/CLI11/.ci/build_doxygen.sh
@@ -23,4 +23,3 @@ export PATH="${DEPS_DIR}/doxygen/build/bin:${PATH}"
 cd "${TRAVIS_BUILD_DIR}"
 
 set +evx
-
diff --git a/packages/CLI11/.ci/make_and_test.sh b/packages/CLI11/.ci/make_and_test.sh
index af8de340f53ac65911ab305c0949aaf793804fde..07df22e881c04a2e5dd70cd7702b3159a765390b 100755
--- a/packages/CLI11/.ci/make_and_test.sh
+++ b/packages/CLI11/.ci/make_and_test.sh
@@ -1,14 +1,14 @@
 #!/usr/bin/env bash
 echo -en "travis_fold:start:script.build\\r"
 echo "Building..."
-STD=$1
+STD="$1"
 shift
 set -evx
 
 
 mkdir -p build
 cd build
-cmake .. -DCLI11_WARNINGS_AS_ERRORS=ON -DCLI11_SINGLE_FILE=ON -DCMAKE_CXX_STANDARD=$STD -DCLI11_SINGLE_FILE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER_LAUNCHER=ccache $@
+cmake .. -DCLI11_WARNINGS_AS_ERRORS=ON -DCLI11_SINGLE_FILE=ON -DCMAKE_CXX_STANDARD="$STD" -DCLI11_SINGLE_FILE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_CXX_COMPILER_LAUNCHER=ccache $@
 cmake --build . -- -j2
 
 set +evx
diff --git a/packages/CLI11/.ci/run_codecov.sh b/packages/CLI11/.ci/run_codecov.sh
index 28d149a530571c3eb8d58dec4c2ac61284e504a9..fbc199ca579d50b6bdcbd9cd899b6db7c6ab7e0c 100755
--- a/packages/CLI11/.ci/run_codecov.sh
+++ b/packages/CLI11/.ci/run_codecov.sh
@@ -4,7 +4,7 @@ echo -en "travis_fold:start:script.build\\r"
 echo "Building..."
 set -evx
 
-cd ${TRAVIS_BUILD_DIR}
+cd "${TRAVIS_BUILD_DIR}"
 mkdir -p build
 cd build
 cmake .. -DCLI11_SINGLE_FILE_TESTS=OFF -DCLI11_EXAMPLES=OFF -DCMAKE_BUILD_TYPE=Coverage
diff --git a/packages/CLI11/.clang-tidy b/packages/CLI11/.clang-tidy
index 09875c87a6b7adbc0aa8f5b0ba872493f2c82e63..075f35dc58ed5cfc91a62048bdbd91c319718685 100644
--- a/packages/CLI11/.clang-tidy
+++ b/packages/CLI11/.clang-tidy
@@ -22,4 +22,3 @@ HeaderFilterRegex: '.*hpp'
 CheckOptions:
 - key:             google-readability-braces-around-statements.ShortStatementLines
   value:           '3'
-
diff --git a/packages/CLI11/.cmake-format.yaml b/packages/CLI11/.cmake-format.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..543ddf19f87ff18972f60b3a7329431eb3d81964
--- /dev/null
+++ b/packages/CLI11/.cmake-format.yaml
@@ -0,0 +1,6 @@
+format:
+  line_width: 99
+
+# Causes a few issues - can be solved later, possibly.
+markup:
+  enable_markup: false
diff --git a/packages/CLI11/.github/CONTRIBUTING.md b/packages/CLI11/.github/CONTRIBUTING.md
index fcee45d43671f3facfd2a133e075fcbd3300f757..2f44c9da7d1a6d849dc94d81aa24200c86fd6442 100644
--- a/packages/CLI11/.github/CONTRIBUTING.md
+++ b/packages/CLI11/.github/CONTRIBUTING.md
@@ -1,8 +1,11 @@
+# Contributing
+
 Thanks for considering to write a Pull Request (PR) for CLI11! Here are a few guidelines to get you started:
 
 Make sure you are comfortable with the license; all contributions are licensed under the original license.
 
 ## Adding functionality
+
 Make sure any new functions you add are are:
 
 * Documented by `///` documentation for Doxygen
@@ -12,7 +15,7 @@ Make sure any new functions you add are are:
 
 In general, make sure the addition is well thought out and does not increase the complexity of CLI11 needlessly.
 
-## Things you should know:
+## Things you should know
 
 * Once you make the PR, tests will run to make sure your code works on all supported platforms
 * The test coverage is also measured, and that should remain 100%
@@ -20,10 +23,9 @@ In general, make sure the addition is well thought out and does not increase the
 * Everything must pass clang-tidy as well, run with `-DCLI11_CLANG_TIDY=ON` (if you set `-DCLI11_CLANG_TIDY_OPTIONS="-fix"`, make sure you use a single threaded build process, or just build one example target).
 * Your changes must also conform to most of the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html) rules checked by [cpplint](https://github.com/cpplint/cpplint). For unused cpplint filters and justifications, see [CPPLINT.cfg](/CPPLINT.cfg).
 
-
 ## Pre-commit
 
-Format is handled by pre-commit. You should install it:
+Format is handled by pre-commit. You should install it (or use [pipx](https://pypa.github.io/pipx/)):
 
 ```bash
 python3 -m pip install pre-commit
@@ -31,13 +33,12 @@ python3 -m pip install pre-commit
 
 Then, you can run it on the items you've added to your staging area, or all files:
 
-```
+```bash
 pre-commit run
 # OR
 pre-commit run --all-files
 ```
 
-
 And, if you want to always use it, you can install it as a git hook (hence the name, pre-commit):
 
 ```bash
@@ -72,9 +73,11 @@ yarn all-contributors add username code,bug
 Remember to replace the emoji in the readme, being careful not to replace the ones in all-contributors if any overlap.
 
 Steps:
+
 * Update changelog if needed
 * Update the version in `.appveyor.yml` and `include/CLI/Version.hpp`.
-* Find and replace in README:
-    * Replace " 🆕" and "🆕 " with "" (ignores the description line)
-    * Check for `\/\/$` (vi syntax) to catch leftover `// 🆕`
-    * Replace "🚧" with "🆕" (manually ignore the description line)
+* Find and replace in README (new minor/major release only):
+  * Replace " 🆕" and "🆕 " with "" (ignores the description line)
+  * Check for `\/\/$` (vi syntax) to catch leftover `// 🆕`
+  * Replace "🚧" with "🆕" (manually ignore the description line)
+* Make a release in the GitHub UI, use a name such as "Version X.Y(.Z): Title"
diff --git a/packages/CLI11/.github/actions/quick_cmake/action.yml b/packages/CLI11/.github/actions/quick_cmake/action.yml
index da721a78c32e275b7f8c93e7a8457b1d009925fc..28a82780c77e531a783e311cada57f78a4a71e13 100644
--- a/packages/CLI11/.github/actions/quick_cmake/action.yml
+++ b/packages/CLI11/.github/actions/quick_cmake/action.yml
@@ -5,14 +5,21 @@ inputs:
     description: 'Other arguments'
     required: false
     default: ''
+  cmake-version:
+    description: 'The CMake version to run'
+    required: true
 
 runs:
   using: composite
   steps:
+    - name: CMake ${{ inputs.cmake-version }}
+      uses: jwlawson/actions-setup-cmake@v1.11
+      with:
+        cmake-version: "${{ inputs.cmake-version }}"
     - run: |
         mkdir -p build-tmp
         touch build-tmp/tmp
         rm -r build-tmp/*
-        (cd build-tmp && cmake .. ${{ inputs.args}})
+        (cd build-tmp && cmake .. ${{ inputs.args }})
         rm -r build-tmp
       shell: bash
diff --git a/packages/CLI11/.github/workflows/build.yml b/packages/CLI11/.github/workflows/build.yml
index d3f8043a5440de0aee244565809397a5950a50e4..a6f250409d9d2736f19a6adf26b84a9507e400ca 100644
--- a/packages/CLI11/.github/workflows/build.yml
+++ b/packages/CLI11/.github/workflows/build.yml
@@ -2,13 +2,11 @@ name: Build
 on:
   push:
     branches:
-      - master
+      - main
       - v*
     tags:
       - "*"
   pull_request:
-    branches:
-      - master
 
 jobs:
   single-header:
@@ -37,10 +35,13 @@ jobs:
     - name: Make header
       run: cmake --build build --target CLI11-generate-single-file
 
+    - name: Copy file to main folder
+      run: cp build/include/CLI11.hpp CLI11.hpp
+
     - uses: actions/upload-artifact@v2
       with:
         name: CLI11.hpp
-        path: build/include/CLI11.hpp
+        path: CLI11.hpp
 
     - uses: actions/upload-artifact@v2
       with:
diff --git a/packages/CLI11/.github/workflows/tests.yml b/packages/CLI11/.github/workflows/tests.yml
index 60f10f9623eaf0218344a63d60737eea92d5ace2..a48b4e367526e2d34ba8aa30353d7df784c65aef 100644
--- a/packages/CLI11/.github/workflows/tests.yml
+++ b/packages/CLI11/.github/workflows/tests.yml
@@ -2,21 +2,11 @@ name: Tests
 on:
   push:
     branches:
-      - master
+      - main
       - v*
   pull_request:
-    branches:
-      - master
 
 jobs:
-  pre-commit:
-    name: Formatting
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v2
-    - uses: actions/setup-python@v2
-    - uses: pre-commit/action@v2.0.2
-
   cuda-build:
     name: CUDA build only
     runs-on: ubuntu-latest
@@ -27,156 +17,152 @@ jobs:
         submodules: true
     - name: Add wget
       run: apt-get update && apt-get install -y wget
-    - name: Setup cmake
-      uses: jwlawson/actions-setup-cmake@v1.8
+    - name: Get cmake
+      uses: jwlawson/actions-setup-cmake@v1.11
     - name: Configure
       run: cmake -S . -B build -DCLI11_CUDA_TESTS=ON
     - name: Build
       run: cmake --build build -j2
 
+
+  boost-build:
+    name: Boost build
+    runs-on: ubuntu-latest
+    container: zouzias/boost:1.76.0
+    steps:
+    - uses: actions/checkout@v1
+      with:
+        submodules: true
+    - name: Add deps
+      run: apt-get update && apt-get install make
+    - name: Get CMake
+      uses: jwlawson/actions-setup-cmake@v1.11
+    - name: Configure
+      run: cmake -S . -B build -DCLI11_BOOST=ON
+    - name: Build
+      run: cmake --build build -j2
+    - name: Run tests
+      run: ctest --output-on-failure
+      working-directory: build
+
   cmake-config:
     name: CMake config check
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v2
 
-    - name: CMake 3.4
-      uses: jwlawson/actions-setup-cmake@v1.8
+    - name: Check CMake 3.4
       with:
         cmake-version: "3.4"
-    - name: Check CMake 3.4
       uses: ./.github/actions/quick_cmake
 
-    - name: CMake 3.5
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.5"
     - name: Check CMake 3.5
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.5"
       if: success() || failure()
 
-    - name: CMake 3.6
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.6"
     - name: Check CMake 3.6
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.6"
       if: success() || failure()
 
-    - name: CMake 3.7
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.7"
     - name: Check CMake 3.7
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.7"
       if: success() || failure()
 
-    - name: CMake 3.8
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.8"
     - name: Check CMake 3.8
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.8"
       if: success() || failure()
 
-    - name: CMake 3.9
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.9"
     - name: Check CMake 3.9
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.9"
       if: success() || failure()
 
-    - name: CMake 3.10
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.10"
     - name: Check CMake 3.10
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.10"
       if: success() || failure()
 
-    - name: CMake 3.11
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.11"
     - name: Check CMake 3.11 (full)
       uses: ./.github/actions/quick_cmake
       with:
+        cmake-version: "3.11"
         args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
       if: success() || failure()
 
-    - name: CMake 3.12
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.12"
     - name: Check CMake 3.12
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.12"
       if: success() || failure()
 
-    - name: CMake 3.13
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.13"
     - name: Check CMake 3.13
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.13"
       if: success() || failure()
 
-    - name: CMake 3.14
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.14"
     - name: Check CMake 3.14
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.14"
       if: success() || failure()
 
-    - name: CMake 3.15
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.15"
     - name: Check CMake 3.15
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.15"
       if: success() || failure()
 
-    - name: CMake 3.16
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.16"
     - name: Check CMake 3.16
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.16"
       if: success() || failure()
 
-    - name: CMake 3.17
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.17"
     - name: Check CMake 3.17
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.17"
       if: success() || failure()
 
-    - name: CMake 3.18
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.18"
     - name: Check CMake 3.18
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.18"
       if: success() || failure()
 
-    - name: CMake 3.19
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.19"
-    - name: Check CMake 3.19 (full)
+    - name: Check CMake 3.19
       uses: ./.github/actions/quick_cmake
       with:
-        args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
+        cmake-version: "3.19"
       if: success() || failure()
 
-    - name: CMake 3.20
-      uses: jwlawson/actions-setup-cmake@v1.8
-      with:
-        cmake-version: "3.20"
     - name: Check CMake 3.20
       uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.20"
       if: success() || failure()
 
+    - name: Check CMake 3.21 (full)
+      uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.21"
+        args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
+      if: success() || failure()
 
+    - name: Check CMake 3.22 (full)
+      uses: ./.github/actions/quick_cmake
+      with:
+        cmake-version: "3.22"
+        args: -DCLI11_SANITIZERS=ON -DCLI11_BUILD_EXAMPLES_JSON=ON
+      if: success() || failure()
diff --git a/packages/CLI11/.gitrepo b/packages/CLI11/.gitrepo
index 732e03b962d7b98a783a3ae9f93d8ac9232673e5..9e19e3887de346a0389ceac098b60a6dd619ed04 100644
--- a/packages/CLI11/.gitrepo
+++ b/packages/CLI11/.gitrepo
@@ -5,8 +5,8 @@
 ;
 [subrepo]
 	remote = git@github.com:CLIUtils/CLI11.git
-	branch = master
-	commit = 4af78beef777e313814b4daff70e2da9171a385a
-	parent = 015d2fd5332b347d28c47c8dfe3f401382724178
-	cmdver = 0.4.3
+	branch = main
+	commit = 70f8072f9dd2292fd0b9f9e5f58e279f60483ed3
+	parent = ae4cfe8875caf314c7f66eec2e6d09d5ee321e6a
 	method = merge
+	cmdver = 0.4.3
diff --git a/packages/CLI11/.pre-commit-config.yaml b/packages/CLI11/.pre-commit-config.yaml
index f48801a6e65cb8c7d64f86de091d41f348f19a00..84135427c6baa312b9911497c4b687a9bcfcacf9 100644
--- a/packages/CLI11/.pre-commit-config.yaml
+++ b/packages/CLI11/.pre-commit-config.yaml
@@ -1,29 +1,63 @@
+ci:
+  autoupdate_commit_msg: "chore(deps): pre-commit.ci autoupdate"
+  autofix_commit_msg: "style: pre-commit.ci fixes"
+
 
 repos:
 - repo: https://github.com/psf/black
-  rev: 20.8b1
+  rev: 21.9b0
   hooks:
   - id: black
 
 - repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: v3.4.0
+  rev: v4.0.1
   hooks:
   - id: check-added-large-files
-  - id: mixed-line-ending
-  - id: trailing-whitespace
-  - id: check-merge-conflict
   - id: check-case-conflict
+  - id: check-merge-conflict
   - id: check-symlinks
   - id: check-yaml
+  - id: end-of-file-fixer
+  - id: mixed-line-ending
+  - id: trailing-whitespace
+
+- repo: https://github.com/pre-commit/mirrors-clang-format
+  rev: v13.0.0
+  hooks:
+   - id: clang-format
+
+- repo: https://github.com/cheshirekow/cmake-format-precommit
+  rev: v0.6.13
+  hooks:
+  - id: cmake-format
+    additional_dependencies: [pyyaml]
+
+- repo: https://github.com/markdownlint/markdownlint
+  rev: v0.11.0
+  hooks:
+  - id: markdownlint
+    args: ["--style=scripts/mdlint_style.rb"]
 
 - repo: local
   hooks:
-  - id: docker-clang-format
-    name: Docker Clang Format
-    language: docker_image
-    types:
-    - c++
-    entry: unibeautify/clang-format:latest
-    args:
-    - -style=file
-    - -i
+  - id: remarklint
+    name: remarklint
+    language: node
+    entry: remark
+    types: [markdown]
+    args: ["--frail", "--quiet"]
+    additional_dependencies: [remark, remark-lint, remark-cli, remark-preset-lint-recommended, remark-lint-list-item-indent, remark-lint-no-undefined-references]
+
+- repo: local
+  hooks:
+  - id: disallow-caps
+    name: Disallow improper capitalization
+    language: pygrep
+    entry: PyBind|Numpy|Cmake|CCache|PyTest|Github
+    exclude: .pre-commit-config.yaml
+
+- repo: https://github.com/codespell-project/codespell
+  rev: v2.1.0
+  hooks:
+  - id: codespell
+    args: ["-L", "atleast,ans,doub,inout"]
diff --git a/packages/CLI11/.pre-commit-nodocker.yaml b/packages/CLI11/.pre-commit-nodocker.yaml
deleted file mode 100644
index 5fcaf3e53ef45d86dd497fe7240e46a2023d8a5b..0000000000000000000000000000000000000000
--- a/packages/CLI11/.pre-commit-nodocker.yaml
+++ /dev/null
@@ -1,27 +0,0 @@
-
-repos:
-- repo: https://github.com/psf/black
-  rev: 19.3b0
-  hooks:
-  - id: black
-- repo: https://github.com/pre-commit/pre-commit-hooks
-  rev: v2.3.0
-  hooks:
-  - id: check-added-large-files
-  - id: mixed-line-ending
-  - id: trailing-whitespace
-  - id: check-merge-conflict
-  - id: check-case-conflict
-  - id: check-symlinks
-  - id: check-yaml
-- repo: local
-  hooks:
-  - id: clang-format
-    name: Clang Format
-    language: system
-    types:
-    - c++
-    entry: clang-format
-    args:
-    - -style=file
-    - -i
diff --git a/packages/CLI11/.remarkrc b/packages/CLI11/.remarkrc
new file mode 100644
index 0000000000000000000000000000000000000000..73cad832882f877af531bafa42808d99dadfd9ac
--- /dev/null
+++ b/packages/CLI11/.remarkrc
@@ -0,0 +1,7 @@
+{
+  "plugins": [
+    "remark-preset-lint-recommended",
+    ["remark-lint-list-item-indent", "space"],
+    ["remark-lint-no-undefined-references", {"allow": ["^1"]}]
+  ]
+}
diff --git a/packages/CLI11/.travis.yml b/packages/CLI11/.travis.yml
deleted file mode 100644
index d352aed96d47a9169595813dca8fa8ff2899a69e..0000000000000000000000000000000000000000
--- a/packages/CLI11/.travis.yml
+++ /dev/null
@@ -1,122 +0,0 @@
-language: cpp
-dist: trusty
-
-# Exclude ghpages,
-# but even better, don't build branch and PR, just PR
-# Include tags starting with v and a digit
-branches:
-  only:
-  - master
-  - /^v\d/
-
-cache:
-  apt: true
-  directories:
-  - "${TRAVIS_BUILD_DIR}/deps/doxygen"
-
-matrix:
-  include:
-    # Default clang
-  - compiler: clang
-    script:
-    - .ci/make_and_test.sh 11
-    - .ci/make_and_test.sh 14
-    - .ci/make_and_test.sh 17
-
-    # Docs and clang 3.5
-  - compiler: clang
-    language: node_js
-    node_js: "7.4.0"
-    env:
-    - DEPLOY_MAT=yes
-    addons:
-      apt:
-        packages:
-        - clang-3.5
-    install:
-    - export CC=clang-3.5
-    - export CXX=clang++-3.5
-    - npm install gitbook-cli -g
-    - gitbook fetch 3.2.3
-    - gitbook install book
-    script:
-    - .ci/make_and_test.sh 11
-    after_success:
-    - export DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
-    - . .ci/build_doxygen.sh
-    - doxygen docs/Doxyfile
-    - gitbook build book html/book
-
-    # GCC 7 and coverage (8 does not support lcov, wait till 9 and new lcov)
-  - compiler: gcc
-    dist: bionic
-    addons:
-      apt:
-        packages:
-        - curl
-        - lcov
-    install:
-    - DEPS_DIR="${TRAVIS_BUILD_DIR}/deps"
-    - cd $TRAVIS_BUILD_DIR
-    - ". .ci/build_lcov.sh"
-    - ".ci/run_codecov.sh"
-    script:
-    - .ci/make_and_test.sh 11 -DCLI11_EXAMPLE_JSON=ON
-    - .ci/make_and_test.sh 14 -DCLI11_EXAMPLE_JSON=ON
-    - .ci/make_and_test.sh 17 -DCLI11_EXAMPLE_JSON=ON
-
-    # GCC 4.8 and Conan
-  - compiler: gcc
-    dist: bionic
-    addons:
-      apt:
-        packages:
-        - python3-pip
-        - python3-setuptools
-    install:
-    - python3 -VV
-    - python3 -m pip install --user conan
-    - conan user
-    script:
-    - .ci/make_and_test.sh 11
-    after_success:
-    - conan create . cliutils/stable
-    - |
-      if [ "${TRAVIS_TAG}" ]
-      then
-        conan remote add origin https://api.bintray.com/conan/cliutils/CLI11
-        conan user -p ${BINFROG_API_KEY} -r origin henryiii
-        conan upload "*" -c -r origin --all
-      fi
-
-
-install: skip
-
-script:
-- .ci/make_and_test.sh 11
-- .ci/make_and_test.sh 14
-
-
-deploy:
-- provider: pages
-  skip_cleanup: true
-  github_token: ${GH_REPO_TOKEN}
-  keep_history: false
-  local_dir: ${TRAVIS_BUILD_DIR}/html
-  on:
-    branch: master
-    condition: "$DEPLOY_MAT = yes"
-
-notifications:
-  webhooks:
-    urls:
-    - https://webhooks.gitter.im/e/bbdb3befce4c00448d24
-    on_success: change
-    on_failure: always
-    on_start: never
-
-env:
-  global:
-  - secure: cY0OI609iTAxLRYuYQnNMi+H6n0dBwioTAoFXGGRTnngw2V9om3UmY5eUu4HQEQsQZovHdYpNhlSgRmdwQ4UqSp3FGyrwobf0kzacV4bVnMDeXDmHt8RzE5wP/LwDd8elNF6RRYjElY99f0k0FyXVd0fIvuVkGKQECNLOtEk0jQo+4YTh7dhuCxRhBYgTbNiRL6UJynfrcK0YN+DQ+8CJNupu2VxgaEpCSngTfvDHLcddcrXwpvn3MPc3FsDUbtN389ZCIe41qqIL0ATv46DQaTw4FOevyVfRyrBOznONoGCVeAYKL6VBdrk01Fh6aytF5zgI3hKaKobgEn+QFfzR6l68c6APvqA0Qv39iLjuh6KbdIV2YsqXfyt6FBgqP2xZuNEZW1jZ8LxUOLl2I40UEh87nFutvnSbfIzN+FcLrajm2H2jV2kZGNKAMx+4qxkZuXSre4JPkENfJm2WNFAKlqPt4ZSEQarkDYzZPcEr2I9fbGjQYVJICoN4LikCv9K5z7ujpTxCTNbVpQWZcEOT6QQBc6Vml/N/NKAIl9o2OeTLiXCmT31+KQMeO492KYNQ6VmkeqrVhGExOUcJdNyDJV9C+3mSekb3Sq78SneYRKDechkWbMl0ol07wGTdBwQQwgaorjRyn07x1rDxpPr3z19/+eubnpPUW4UQ5MYsjs=
-  - secure: G6H5HA9pPUgsd96A+uvTxbLjR1rcT9NtxsknIkFDfzGDpffn6wVX+kCIQLf9zFDnQnsfYA/4piiuoBN5U5C7HQrh9UCvBVptXjWviea0Y7CRbMJZpw2rPvXWQtrFNzYkaV7kdJ5B0Mmvh6rcH/I8gKFrkdjF7i7sfzWdFWRU5QXfxXOk2n+xCXX6uFemxHH9850XEjVtnU7YYUebQFaoTYLLy05nlt9JaEF84wfJljY/SJX7I9gpNLtizE9MpJylnrwUeL66OqFievmjL3/bWpPUBjUF0WdtXYlVDja7O582FQDs94ofgqeGieGIMQ0VuovpbQOJSdjs5XHZwu2ce6HZxtOhJJqw6xEwbq43ZdofAlJ5GUEOgrr+j25zIDkdzOhliDKJtw5ysmmTUKEcZ36iWbCE0YP/IC42yOV9oOP6UkgbuwpVDdxAFRgLZLahW9Ok+c1PlzIauPxv+jIEI4rSEEJRKZG2JK3TXUdhd58mHBfQMNjKQMF+Y2wCCGjfMO0q4SgvBhYyb4oBTxEqnc2Pzh2DJdNzRFsV7ktsQSRglHGVI+1XTmQ+2kbBzNOQBLjOuRvDZENUhyxPKGZDHyAOMlVvYm8vvWebM1/F3YgDb/tPh33+EGSvpKkCZ5nUxB5e605H6gdYlNKNhuWKlEKTo2/kF0D39gAUCIcGbzw=
-  - CCACHE_CPP2: yes
diff --git a/packages/CLI11/CHANGELOG.md b/packages/CLI11/CHANGELOG.md
index d2a59b4786a836a07801228e5371e095b42c5e23..e35ca38676833f3891fbbd8ea8f0ed8a0808588c 100644
--- a/packages/CLI11/CHANGELOG.md
+++ b/packages/CLI11/CHANGELOG.md
@@ -1,28 +1,92 @@
-## Version 2.0: In progress
+# Changelog
+
+## Version 2.1: Names and callbacks
+
+The name restrictions for options and subcommands are now much looser, allowing
+a wider variety of characters than before, even spaces can be used (use quotes
+to include a space in most shells). The default configuration parser was
+improved, allowing your configuration to sit in a larger file. And option
+callbacks have a few new settings, allowing them to be run even if the option
+is not passed, or every time the option is parsed.
+
+* Option/subcommand name restrictions have been relaxed. Most characters are now allowed. [#627][]
+* The config parser can accept streams, specify a specific section, and inline comment characters are supported [#630][]
+* `force_callback` & `trigger_on_parse` added, allowing a callback to always run on parse even if not present or every time the option is parsed [#631][]
+* Bugfix(cmake): Only add `CONFIGURE_DEPENDS` if CLI11 is the main project [#633][]
+* Bugfix(cmake): Ensure the cmake/pkg-config files install to a arch independent path [#635][]
+* Bugfix: The single header file generation was missing the include guard. [#620][]
+
+[#620]: https://github.com/CLIUtils/CLI11/pull/620
+[#627]: https://github.com/CLIUtils/CLI11/pull/627
+[#630]: https://github.com/CLIUtils/CLI11/pull/630
+[#631]: https://github.com/CLIUtils/CLI11/pull/631
+[#633]: https://github.com/CLIUtils/CLI11/pull/633
+[#635]: https://github.com/CLIUtils/CLI11/pull/635
+
+### Version 2.1.1: Quick Windows fix
+
+* A collision with `min`/`max` macros on Windows has been fixed. [#642][]
+* Tests pass with Boost again [#646][]
+* Running the pre-commit hooks in development no longer requires docker for clang-format [#647][]
+
+[#642]: https://github.com/CLIUtils/CLI11/pull/642
+[#646]: https://github.com/CLIUtils/CLI11/pull/646
+[#647]: https://github.com/CLIUtils/CLI11/pull/647
+
+## Version 2.1.2: Better subproject builds
+
+* Use `main` for the main branch of the repository [#657][]
+* Bugfix(cmake): Enforce at least C++11 when using CMake target [#656][]
+* Build: Don't run doxygen and CTest includes if a submodule [#656][]
+* Build: Avoid a warning on CMake 3.22 [#656][]
+* Build: Support compiling the tests with an external copy of Catch2 [#653][]
+
+[#653]: https://github.com/CLIUtils/CLI11/pull/653
+[#656]: https://github.com/CLIUtils/CLI11/pull/656
+[#657]: https://github.com/CLIUtils/CLI11/pull/657
+
+## Version 2.0: Simplification
+
+This version focuses on cleaning up deprecated functionality, and some minor
+default changes. The config processing is TOML compliant now. Atomics and
+complex numbers are directly supported, along with other container
+improvements. A new version flag option has finally been added. Subcommands are
+significantly improved with new features and bugfixes for corner cases.  This
+release contains a lot of backend cleanup, including a complete overhaul of the
+testing system and single file generation system.
 
 * Built-in config format is TOML compliant now [#435][]
-    * Support multiline TOML [#528][]
-* Support short/positional options in config mode [#443][]
-* More powerful containers, `%%` separator [#423][]
-* Add a version flag easily [#452][]
-* Support atomic types [#520][]
+  * Support multiline TOML [#528][]
+  * Support for configurable quotes [#599][]
+  * Support short/positional options in config mode [#443][]
+* More powerful containers, support for `%%` separator [#423][]
+* Support atomic types [#520][] and complex types natively [#423][]
 * Add a type validator `CLI::TypeValidator<TYPE>` [#526][]
+* Add a version flag easily [#452][], with help message [#601][]
 * Support `->silent()` on subcommands. [#529][]
 * Add alias section to help for subcommands [#545][]
-* Redesigned MakeSingleFiles to have a higher level of manual control, to support future features. [#546][]
-* Moved testing from GTest to Catch2 [#574][]
-
+* Allow quotes to specify a program name [#605][]
+* Backend: redesigned MakeSingleFiles to have a higher level of manual control, to support future features. [#546][]
+* Backend: moved testing from GTest to Catch2 [#574][]
+* Bugfix: avoid duplicated and missed calls to the final callback [#584][]
+* Bugfix: support embedded newlines in more places [#592][]
 * Bugfix: avoid listing helpall as a required flag [#530][]
 * Bugfix: avoid a clash with WINDOWS define [#563][]
-
-* Removed deprecated set commands, use validators instead. [#565][]
-
+* Bugfix: the help flag didn't get processed when a config file was required [#606][]
+* Bugfix: fix description of non-configurable subcommands in config [#604][]
 * Build: support pkg-config [#523][]
 
+> ### Converting from CLI11 1.9
+>
+> * Removed deprecated set commands, use validators instead. [#565][]
+> * The final "defaulted" bool has been removed, use `->capture_default_str()`
+>   instead. Use `app.option_defaults()->always_capture_default()` to set this for
+>   all future options. [#597][]
+> * Use `add_option` on a complex number instead of `add_complex`, which has been removed.
 
+[#423]: https://github.com/CLIUtils/CLI11/pull/423
 [#435]: https://github.com/CLIUtils/CLI11/pull/435
 [#443]: https://github.com/CLIUtils/CLI11/pull/443
-[#423]: https://github.com/CLIUtils/CLI11/pull/423
 [#452]: https://github.com/CLIUtils/CLI11/pull/452
 [#520]: https://github.com/CLIUtils/CLI11/pull/520
 [#523]: https://github.com/CLIUtils/CLI11/pull/523
@@ -35,30 +99,14 @@
 [#563]: https://github.com/CLIUtils/CLI11/pull/563
 [#565]: https://github.com/CLIUtils/CLI11/pull/565
 [#574]: https://github.com/CLIUtils/CLI11/pull/574
-
-
-
-
-### Version 1.9.1: Backporting fixes
-
-This is a patch version that backports fixes from the development of 2.0.
-
-* Support relative inclusion [#475][]
-* Fix cases where spaces in paths could break CMake support [#471][]
-* Fix an issue with string conversion [#421][]
-* Cross-compiling improvement for Conan.io [#430][]
-* Fix option group default propagation [#450][]
-* Fix for C++20 [#459][]
-* Support compiling with RTTI off [#461][]
-
-[#421]: https://github.com/CLIUtils/CLI11/pull/421
-[#430]: https://github.com/CLIUtils/CLI11/pull/430
-[#450]: https://github.com/CLIUtils/CLI11/pull/450
-[#459]: https://github.com/CLIUtils/CLI11/pull/459
-[#461]: https://github.com/CLIUtils/CLI11/pull/461
-[#471]: https://github.com/CLIUtils/CLI11/pull/471
-[#475]: https://github.com/CLIUtils/CLI11/pull/475
-
+[#584]: https://github.com/CLIUtils/CLI11/pull/584
+[#592]: https://github.com/CLIUtils/CLI11/pull/592
+[#597]: https://github.com/CLIUtils/CLI11/pull/597
+[#599]: https://github.com/CLIUtils/CLI11/pull/599
+[#601]: https://github.com/CLIUtils/CLI11/pull/601
+[#604]: https://github.com/CLIUtils/CLI11/pull/604
+[#605]: https://github.com/CLIUtils/CLI11/pull/605
+[#606]: https://github.com/CLIUtils/CLI11/pull/606
 
 ## Version 1.9: Config files and cleanup
 
@@ -98,11 +146,11 @@ configuration options were added to facilitate a wider variety of apps.  GCC
 * Build: GCC 4.7 is no longer supported, due mostly to GoogleTest. GCC 4.8+ is now required. [#160][]
 * Build: Restructured significant portions of CMake build system [#394][]
 
-> ### Converting from CLI11 1.8:
+> ### Converting from CLI11 1.8
 >
 > * Some deprecated methods dropped
->     - `add_set*` should be replaced with `->check`/`->transform` and `CLI::IsMember` since 1.8
->     - `get_defaultval` was replaced by `get_default_str`  in 1.8
+>   * `add_set*` should be replaced with `->check`/`->transform` and `CLI::IsMember` since 1.8
+>   * `get_defaultval` was replaced by `get_default_str`  in 1.8
 > * The true/false 4th argument to `add_option` is expected to be removed in 2.0, use `->capture_default_str()` since 1.8
 
 [#160]: https://github.com/CLIUtils/CLI11/pull/160
@@ -118,7 +166,6 @@ configuration options were added to facilitate a wider variety of apps.  GCC
 [#307]: https://github.com/CLIUtils/CLI11/pull/307
 [#309]: https://github.com/CLIUtils/CLI11/pull/309
 [#310]: https://github.com/CLIUtils/CLI11/pull/310
-[#312]: https://github.com/CLIUtils/CLI11/pull/312
 [#313]: https://github.com/CLIUtils/CLI11/pull/313
 [#317]: https://github.com/CLIUtils/CLI11/pull/317
 [#318]: https://github.com/CLIUtils/CLI11/pull/318
@@ -126,6 +173,7 @@ configuration options were added to facilitate a wider variety of apps.  GCC
 [#325]: https://github.com/CLIUtils/CLI11/pull/325
 [#333]: https://github.com/CLIUtils/CLI11/pull/333
 [#336]: https://github.com/CLIUtils/CLI11/pull/336
+[#341]: https://github.com/CLIUtils/CLI11/pull/341
 [#342]: https://github.com/CLIUtils/CLI11/pull/342
 [#348]: https://github.com/CLIUtils/CLI11/pull/348
 [#349]: https://github.com/CLIUtils/CLI11/pull/349
@@ -136,42 +184,63 @@ configuration options were added to facilitate a wider variety of apps.  GCC
 [#360]: https://github.com/CLIUtils/CLI11/pull/360
 [#362]: https://github.com/CLIUtils/CLI11/pull/362
 [#365]: https://github.com/CLIUtils/CLI11/pull/365
+[#370]: https://github.com/CLIUtils/CLI11/pull/370
 [#373]: https://github.com/CLIUtils/CLI11/pull/373
 [#374]: https://github.com/CLIUtils/CLI11/pull/374
 [#382]: https://github.com/CLIUtils/CLI11/pull/382
+[#387]: https://github.com/CLIUtils/CLI11/pull/387
 [#390]: https://github.com/CLIUtils/CLI11/pull/390
 [#394]: https://github.com/CLIUtils/CLI11/pull/394
 [#400]: https://github.com/CLIUtils/CLI11/pull/400
 
+### Version 1.9.1: Backporting fixes
+
+This is a patch version that backports fixes from the development of 2.0.
+
+* Support relative inclusion [#475][]
+* Fix cases where spaces in paths could break CMake support [#471][]
+* Fix an issue with string conversion [#421][]
+* Cross-compiling improvement for Conan.io [#430][]
+* Fix option group default propagation [#450][]
+* Fix for C++20 [#459][]
+* Support compiling with RTTI off [#461][]
+
+[#421]: https://github.com/CLIUtils/CLI11/pull/421
+[#430]: https://github.com/CLIUtils/CLI11/pull/430
+[#450]: https://github.com/CLIUtils/CLI11/pull/450
+[#459]: https://github.com/CLIUtils/CLI11/pull/459
+[#461]: https://github.com/CLIUtils/CLI11/pull/461
+[#471]: https://github.com/CLIUtils/CLI11/pull/471
+[#475]: https://github.com/CLIUtils/CLI11/pull/475
 
 ## Version 1.8: Transformers, default strings, and flags
 
 Set handling has been completely replaced by a new backend that works as a Validator or Transformer. This provides a single interface instead of the 16 different functions in App. It also allows ordered collections to be used, custom functions for filtering, and better help and error messages. You can also use a collection of pairs (like `std::map`) to transform the match into an output. Also new are inverted flags, which can cancel or reduce the count of flags, and can also support general flag types. A new `add_option_fn` lets you more easily program CLI11 options with the types you choose. Vector options now support a custom separator. Apps can now be composed with unnamed subcommand support. The final bool "defaults" flag when creating options has been replaced by `->capture_default_str()` (ending an old limitation in construction made this possible); the old method is still available but may be removed in future versions.
 
-* Replaced default help capture: `.add_option("name", value, "", True)` becomes `.add_option("name", value)->capture_default_str()` [#242]
-* Added `.always_capture_default()` [#242]
-* New `CLI::IsMember` validator replaces set validation [#222]
-* IsMember also supports container of pairs, transform allows modification of result [#228]
-* Added new Transformers, `CLI::AsNumberWithUnit` and `CLI::AsSizeValue` [#253]
-* Much more powerful flags with different values [#211], general types [#235]
-* `add_option` now supports bool due to unified bool handling [#211]
-* Support for composable unnamed subcommands [#216]
-* Reparsing is better supported with `.remaining_for_passthrough()` [#265]
-* Custom vector separator using `->delimiter(char)` [#209], [#221], [#240]
-* Validators added for IP4 addresses and positive numbers [#210] and numbers [#262]
-* Minimum required Boost for optional Optionals has been corrected to 1.61 [#226]
-* Positionals can stop options from being parsed with `app.positionals_at_end()` [#223]
-* Added `validate_positionals` [#262]
-* Positional parsing is much more powerful [#251], duplicates supported []#247]
-* Validators can be negated with `!` [#230], and now handle tname functions [#228]
-* Better enum support and streaming helper [#233] and [#228]
-* Cleanup for shadow warnings [#232]
-* Better alignment on multiline descriptions [#269]
-* Better support for aarch64 [#266]
-* Respect `BUILD_TESTING` only if CLI11 is the main project; otherwise, `CLI11_TESTING` must be used [#277]
-* Drop auto-detection of experimental optional and boost::optional; must be enabled explicitly (too fragile) [#277] [#279]
-
-> ### Converting from CLI11 1.7:
+* Replaced default help capture: `.add_option("name", value, "", True)` becomes `.add_option("name", value)->capture_default_str()` [#242][]
+* Added `.always_capture_default()` [#242][]
+* New `CLI::IsMember` validator replaces set validation [#222][]
+* `IsMember` also supports container of pairs, transform allows modification of result [#228][]
+* Added new Transformers, `CLI::AsNumberWithUnit` and `CLI::AsSizeValue` [#253][]
+* Much more powerful flags with different values [#211][], general types [#235][]
+* `add_option` now supports bool due to unified bool handling [#211][]
+* Support for composable unnamed subcommands [#216][]
+* Reparsing is better supported with `.remaining_for_passthrough()` [#265][]
+* Custom vector separator using `->delimiter(char)` [#209][], [#221][], [#240][]
+* Validators added for IP4 addresses and positive numbers [#210][] and numbers [#262][]
+* Minimum required Boost for optional Optionals has been corrected to 1.61 [#226][]
+* Positionals can stop options from being parsed with `app.positionals_at_end()` [#223][]
+* Added `validate_positionals` [#262][]
+* Positional parsing is much more powerful [#251][], duplicates supported [#247][]
+* Validators can be negated with `!` [#230][], and now handle tname functions [#228][]
+* Better enum support and streaming helper [#233][] and [#228][]
+* Cleanup for shadow warnings [#232][]
+* Better alignment on multiline descriptions [#269][]
+* Better support for aarch64 [#266][]
+* Respect `BUILD_TESTING` only if CLI11 is the main project; otherwise, `CLI11_TESTING` must be used [#277][]
+* Drop auto-detection of experimental optional and boost::optional; must be enabled explicitly (too fragile) [#277][] [#279][]
+
+> ### Converting from CLI11 1.7
 >
 > * `.add_option(..., true)` should be replaced by `.add_option(...)->capture_default_str()` or `app.option_defaults()->always_capture_default()` can be used
 > * `app.add_set("--name", value, {"choice1", "choice2"})` should become `app.add_option("--name", value)->check(CLI::IsMember({"choice1", "choice2"}))`
@@ -207,47 +276,33 @@ Set handling has been completely replaced by a new backend that works as a Valid
 [#277]: https://github.com/CLIUtils/CLI11/pull/277
 [#279]: https://github.com/CLIUtils/CLI11/pull/279
 
-
-## Version 1.7.1: Quick patch
-
-This version provides a quick patch for a (correct) warning from GCC 8 for the windows options code.
-
-
-* Fix for Windows style option parsing [#201]
-* Improve `add_subcommand` when throwing an exception [#204]
-* Better metadata for Conan package [#202]
-
-[#201]: https://github.com/CLIUtils/CLI11/pull/201
-[#202]: https://github.com/CLIUtils/CLI11/pull/202
-[#204]: https://github.com/CLIUtils/CLI11/pull/204
-
 ## Version 1.7: Parse breakup
 
 The parsing procedure now maps much more sensibly to complex, nested subcommand structures. Each phase of the parsing happens on all subcommands before moving on with the next phase of the parse. This allows several features, like required environment variables, to work properly even through subcommand boundaries.
 Passing the same subcommand multiple times is better supported. Several new features were added as well, including Windows style option support, parsing strings directly, and ignoring underscores in names. Adding a set that you plan to change later must now be done with `add_mutable_set`.
 
-* Support Windows style options with `->allow_windows_style_options`. [#187] On by default on Windows. [#190]
-* Added `parse(string)` to split up and parse a command-line style string directly. [#186]
-* Added `ignore_underscore` and related functions, to ignore underscores when matching names. [#185]
-* The default INI Config will now add quotes to strings with spaces [#195]
-* The default message now will mention the help-all flag also if present [#197]
-* Added `->description` to set Option descriptions [#199]
-* Mutating sets (introduced in Version 1.6) now have a clear add method, `add_mutable_set*`, since the set reference should not expire [#200]
-* Subcommands now track how many times they were parsed in a parsing process. `count()` with no arguments will return the number of times a subcommand was encountered. [#179]
-* Parsing is now done in phases: `shortcurcuits`, `ini`, `env`, `callbacks`, and `requirements`; all subcommands complete a phase before moving on. [#179]
-* Calling parse multiple times is now officially supported without `clear` (automatic). [#179]
-* Dropped the mostly undocumented `short_circuit` property, as help flag parsing is a bit more complex, and the default callback behavior of options now works properly. [#179]
-* Use the standard `BUILD_TESTING` over `CLI11_TESTING` if defined [#183]
-* Cleanup warnings [#191]
-* Remove deprecated names: `set_footer`, `set_name`, `set_callback`, and `set_type_name`. Use without the `set_` instead. [#192]
-
-> ### Converting from CLI11 1.6:
+* Support Windows style options with `->allow_windows_style_options`. [#187][] On by default on Windows. [#190][]
+* Added `parse(string)` to split up and parse a command-line style string directly. [#186][]
+* Added `ignore_underscore` and related functions, to ignore underscores when matching names. [#185][]
+* The default INI Config will now add quotes to strings with spaces [#195][]
+* The default message now will mention the help-all flag also if present [#197][]
+* Added `->description` to set Option descriptions [#199][]
+* Mutating sets (introduced in Version 1.6) now have a clear add method, `add_mutable_set*`, since the set reference should not expire [#200][]
+* Subcommands now track how many times they were parsed in a parsing process. `count()` with no arguments will return the number of times a subcommand was encountered. [#178][]
+* Parsing is now done in phases: `shortcurcuits`, `ini`, `env`, `callbacks`, and `requirements`; all subcommands complete a phase before moving on. [#178][]
+* Calling parse multiple times is now officially supported without `clear` (automatic). [#178][]
+* Dropped the mostly undocumented `short_circuit` property, as help flag parsing is a bit more complex, and the default callback behavior of options now works properly. [#179][]
+* Use the standard `BUILD_TESTING` over `CLI11_TESTING` if defined [#183][]
+* Cleanup warnings [#191][]
+* Remove deprecated names: `set_footer`, `set_name`, `set_callback`, and `set_type_name`. Use without the `set_` instead. [#192][]
+
+> ### Converting from CLI11 1.6
 >
 > * `->short_circuit()` is no longer needed, just remove it if you were using it - raising an exception will happen in the proper place now without it.
 > * `->add_set*` becomes `->add_mutable_set*` if you were using the editable set feature
 > * `footer`, `name`, `callback`, and `type_name` must be used instead of the `set_*` versions (deprecated previously).
 
-[#179]: https://github.com/CLIUtils/CLI11/pull/179
+[#178]: https://github.com/CLIUtils/CLI11/pull/178
 [#183]: https://github.com/CLIUtils/CLI11/pull/183
 [#185]: https://github.com/CLIUtils/CLI11/pull/185
 [#186]: https://github.com/CLIUtils/CLI11/pull/186
@@ -260,48 +315,21 @@ Passing the same subcommand multiple times is better supported. Several new feat
 [#199]: https://github.com/CLIUtils/CLI11/pull/199
 [#200]: https://github.com/CLIUtils/CLI11/pull/200
 
-## Version 1.6.2: Help-all
-
-This version fixes some formatting bugs with help-all. It also adds fixes for several warnings, including an experimental optional error on Clang 7. Several smaller fixes.
-
-* Fixed help-all formatting [#163]
-    * Printing help-all on nested command now fixed (App)
-    * Missing space after help-all restored (Default formatter)
-    * More detail printed on help all (Default formatter)
-    * Help-all subcommands get indented with inner blank lines removed (Default formatter)
-    * `detail::find_and_replace` added to utilities
-* Fixed CMake install as subproject with `CLI11_INSTALL` flag. [#156]
-* Fixed warning about local variable hiding class member with MSVC [#157]
-* Fixed compile error with default settings on Clang 7 and libc++ [#158]
-* Fixed special case of `--help` on subcommands (general fix planned for 1.7) [#168]
-* Removing an option with links  [#179]
+### Version 1.7.1: Quick patch
 
-[#156]: https://github.com/CLIUtils/CLI11/issues/156
-[#157]: https://github.com/CLIUtils/CLI11/issues/157
-[#158]: https://github.com/CLIUtils/CLI11/issues/158
-[#163]: https://github.com/CLIUtils/CLI11/pull/163
-[#168]: https://github.com/CLIUtils/CLI11/issues/168
-[#179]: https://github.com/CLIUtils/CLI11/pull/179
-
-
-## Version 1.6.1: Platform fixes
-
-This version provides a few fixes for special cases, such as mixing with `Windows.h` and better defaults
-for systems like Hunter. The one new feature is the ability to produce "branded" single file output for
-providing custom namespaces or custom macro names.
+This version provides a quick patch for a (correct) warning from GCC 8 for the windows options code.
 
-* Added fix and test for including Windows.h [#145]
-* No longer build single file by default if main project, supports systems stuck on Python 2.6 [#149], [#151]
-* Branding support for single file output [#150]
+* Fix for Windows style option parsing [#201][]
+* Improve `add_subcommand` when throwing an exception [#204][]
+* Better metadata for Conan package [#202][]
 
-[#145]: https://github.com/CLIUtils/CLI11/pull/145
-[#149]: https://github.com/CLIUtils/CLI11/pull/149
-[#150]: https://github.com/CLIUtils/CLI11/pull/150
-[#151]: https://github.com/CLIUtils/CLI11/pull/151
+[#201]: https://github.com/CLIUtils/CLI11/pull/201
+[#202]: https://github.com/CLIUtils/CLI11/pull/202
+[#204]: https://github.com/CLIUtils/CLI11/pull/204
 
 ## Version 1.6: Formatting help
 
-Added a new formatting system [#109]. You can now set the formatter on Apps. This has also simplified the internals of Apps and Options a bit by separating most formatting code.
+Added a new formatting system [#109][]. You can now set the formatter on Apps. This has also simplified the internals of Apps and Options a bit by separating most formatting code.
 
 * Added `CLI::Formatter` and `formatter` slot for apps, inherited.
 * `FormatterBase` is the minimum required.
@@ -317,10 +345,9 @@ Changes to the help system (most normal users will not notice this):
 * Protected function `_has_help_positional` removed.
 * `format_help` can now be chained.
 * Added getters for the missing parts of options (help no longer uses any private parts).
-* Help flags now use new `short_circuit` property to simplify parsing. [#121]
-
+* Help flags now use new `short_circuit` property to simplify parsing. [#121][]
 
-New for Config file reading and writing [#121]:
+New for Config file reading and writing [#121][]:
 
 * Overridable, bidirectional Config.
 * ConfigINI provided and used by default.
@@ -328,10 +355,9 @@ New for Config file reading and writing [#121]:
 * Has `config_formatter()` and `get_config_formatter()`.
 * Dropped prefix argument from `config_to_str`.
 * Added `ConfigItem`.
-* Added an example of a custom config format using [nlohmann/json]. [#138]
+* Added an example of a custom config format using [nlohmann/json][]. [#138][]
 
-
-Validators are now much more powerful [#118], all built in validators upgraded to the new form:
+Validators are now much more powerful [#118][], all built in validators upgraded to the new form:
 
 * A subclass of `CLI::Validator` is now also accepted.
 * They now can set the type name to things like `PATH` and `INT in [1-4]`.
@@ -340,29 +366,29 @@ Validators are now much more powerful [#118], all built in validators upgraded t
 
 Other changes:
 
-* Fixing `parse(args)`'s `args` setting and ordering after parse. [#141]
-* Replaced `set_custom_option` with `type_name` and `type_size` instead of `set_custom_option`. Methods return `this`. [#136]
-* Dropped `set_` on Option's `type_name`, `default_str`, and `default_val`. [#136]
-* Removed `set_` from App's `failure_message`, `footer`, `callback`, and `name`. [#136]
-* Fixed support `N<-1` for `type_size`. [#140]
-* Added `->each()` to make adding custom callbacks easier. [#126]
-* Allow empty options `add_option("-n",{})` to be edited later with `each` [#142]
+* Fixing `parse(args)`'s `args` setting and ordering after parse. [#141][]
+* Replaced `set_custom_option` with `type_name` and `type_size` instead of `set_custom_option`. Methods return `this`. [#136][]
+* Dropped `set_` on Option's `type_name`, `default_str`, and `default_val`. [#136][]
+* Removed `set_` from App's `failure_message`, `footer`, `callback`, and `name`. [#136][]
+* Fixed support `N<-1` for `type_size`. [#140][]
+* Added `->each()` to make adding custom callbacks easier. [#126][]
+* Allow empty options `add_option("-n",{})` to be edited later with `each` [#142][]
 * Added filter argument to `get_subcommands`, `get_options`; use empty filter `{}` to avoid filtering.
 * Added `get_groups()` to get groups.
-* Better support for manual options with `get_option`, `set_results`, and `empty`. [#119]
-* `lname` and `sname` have getters, added `const get_parent`. [#120]
-* Using `add_set` will now capture L-values for sets, allowing further modification. [#113]
+* Better support for manual options with `get_option`, `set_results`, and `empty`. [#119][]
+* `lname` and `sname` have getters, added `const get_parent`. [#120][]
+* Using `add_set` will now capture L-values for sets, allowing further modification. [#113][]
 * Dropped duplicate way to run `get_type_name` (`get_typeval`).
-* Removed `requires` in favor of `needs` (deprecated in last version). [#112]
-* Const added to argv. [#126]
+* Removed `requires` in favor of `needs` (deprecated in last version). [#112][]
+* Const added to argv. [#126][]
 
 Backend and testing changes:
 
-* Internally, `type_name` is now a lambda function; for sets, this reads the set live. [#116]
-* Cleaner tests without `app.reset()` (and `reset` is now `clear`). [#141]
-* Better CMake policy handling. [#110]
-* Includes are properly sorted. [#120]
-* Testing (only) now uses submodules. [#111]
+* Internally, `type_name` is now a lambda function; for sets, this reads the set live. [#116][]
+* Cleaner tests without `app.reset()` (and `reset` is now `clear`). [#141][]
+* Better CMake policy handling. [#110][]
+* Includes are properly sorted. [#120][]
+* Testing (only) now uses submodules. [#111][]
 
 [#109]: https://github.com/CLIUtils/CLI11/pull/109
 [#110]: https://github.com/CLIUtils/CLI11/pull/110
@@ -375,7 +401,7 @@ Backend and testing changes:
 [#120]: https://github.com/CLIUtils/CLI11/pull/120
 [#121]: https://github.com/CLIUtils/CLI11/pull/121
 [#126]: https://github.com/CLIUtils/CLI11/pull/126
-[#127]: https://github.com/CLIUtils/CLI11/pull/127
+[#136]: https://github.com/CLIUtils/CLI11/pull/136
 [#138]: https://github.com/CLIUtils/CLI11/pull/138
 [#140]: https://github.com/CLIUtils/CLI11/pull/140
 [#141]: https://github.com/CLIUtils/CLI11/pull/141
@@ -383,33 +409,43 @@ Backend and testing changes:
 
 [nlohmann/json]: https://github.com/nlohmann/json
 
-### Version 1.5.4: Optionals
-This version fixes the optional search in the single file version; some macros were not yet defined when it did the search. You can define the `CLI11_*_OPTIONAL` macros to 0 if needed to eliminate the search.
-
-### Version 1.5.3: Compiler compatibility
-This version fixes older AppleClang compilers by removing the optimization for casting. The minimum version of Boost Optional supported has been clarified to be 1.58. CUDA 7.0 NVCC is now supported.
+### Version 1.6.1: Platform fixes
 
-### Version 1.5.2: LICENSE in single header mode
+This version provides a few fixes for special cases, such as mixing with `Windows.h` and better defaults
+for systems like Hunter. The one new feature is the ability to produce "branded" single file output for
+providing custom namespaces or custom macro names.
 
-This is a quick patch release that makes LICENSE part of the single header file, making it easier to include. Minor cleanup from codacy. No significant code changes from 1.5.1.
+* Added fix and test for including Windows.h [#145][]
+* No longer build single file by default if main project, supports systems stuck on Python 2.6 [#149][], [#151][]
+* Branding support for single file output [#150][]
 
-### Version 1.5.1: Access
+[#145]: https://github.com/CLIUtils/CLI11/pull/145
+[#149]: https://github.com/CLIUtils/CLI11/pull/149
+[#150]: https://github.com/CLIUtils/CLI11/pull/150
+[#151]: https://github.com/CLIUtils/CLI11/pull/151
 
-This patch release adds better access to the App programmatically, to assist with writing custom converters to other formats. It also improves the help output, and uses a new feature in CLI11 1.5 to fix an old "quirk" in the way unlimited options and positionals interact.
+### Version 1.6.2: Help-all
 
-* Make mixing unlimited positionals and options more intuitive [#102]
-* Add missing getters `get_options` and `get_description` to App [#105]
-* The app name now can be set, and will override the auto name if present [#105]
-* Add `(REQUIRED)` for required options [#104]
-* Print simple name for Needs/Excludes [#104]
-* Use Needs instead of Requires in help print [#104]
-* Groups now are listed in the original definition order [#106]
+This version fixes some formatting bugs with help-all. It also adds fixes for several warnings, including an experimental optional error on Clang 7. Several smaller fixes.
 
-[#102]: https://github.com/CLIUtils/CLI11/issues/102
-[#104]: https://github.com/CLIUtils/CLI11/pull/104
-[#105]: https://github.com/CLIUtils/CLI11/pull/105
-[#106]: https://github.com/CLIUtils/CLI11/pull/106
+* Fixed help-all formatting [#163][]
+  * Printing help-all on nested command now fixed (App)
+  * Missing space after help-all restored (Default formatter)
+  * More detail printed on help all (Default formatter)
+  * Help-all subcommands get indented with inner blank lines removed (Default formatter)
+  * `detail::find_and_replace` added to utilities
+* Fixed CMake install as subproject with `CLI11_INSTALL` flag. [#156][]
+* Fixed warning about local variable hiding class member with MSVC [#157][]
+* Fixed compile error with default settings on Clang 7 and libc++ [#158][]
+* Fixed special case of `--help` on subcommands (general fix planned for 1.7) [#168][]
+* Removing an option with links  [#179][]
 
+[#156]: https://github.com/CLIUtils/CLI11/issues/156
+[#157]: https://github.com/CLIUtils/CLI11/issues/157
+[#158]: https://github.com/CLIUtils/CLI11/issues/158
+[#163]: https://github.com/CLIUtils/CLI11/pull/163
+[#168]: https://github.com/CLIUtils/CLI11/issues/168
+[#179]: https://github.com/CLIUtils/CLI11/pull/179
 
 ## Version 1.5: Optionals
 
@@ -417,24 +453,24 @@ This version introduced support for optionals, along with clarification and exam
 
 Note: This is the final release with `requires`, please switch to `needs`.
 
-* Fix unlimited short options eating two values before checking for positionals when no space present [#90]
-* Symmetric exclude text when excluding options, exclude can be called multiple times [#64]
-* Support for `std::optional`, `std::experimental::optional`, and `boost::optional` added if `__has_include` is supported [#95]
-* All macros/CMake variables now start with `CLI11_` instead of just `CLI_` [#95]
-* The internal stream was not being cleared before use in some cases. Fixed. [#95]
-* Using an enum now requires explicit conversion overload [#97]
-* The separator `--` now is removed when it ends unlimited arguments [#100]
+* Fix unlimited short options eating two values before checking for positionals when no space present [#90][]
+* Symmetric exclude text when excluding options, exclude can be called multiple times [#64][]
+* Support for `std::optional`, `std::experimental::optional`, and `boost::optional` added if `__has_include` is supported [#95][]
+* All macros/CMake variables now start with `CLI11_` instead of just `CLI_` [#95][]
+* The internal stream was not being cleared before use in some cases. Fixed. [#95][]
+* Using an enum now requires explicit conversion overload [#97][]
+* The separator `--` now is removed when it ends unlimited arguments [#100][]
 
 Other, non-user facing changes:
 
-* Added `Macros.hpp` with better C++ mode discovery [#95]
+* Added `Macros.hpp` with better C++ mode discovery [#95][]
 * Deprecated macros added for all platforms
-* C++17 is now tested on supported platforms [#95]
-* Informational printout now added to CTest [#95]
-* Better single file generation [#95]
+* C++17 is now tested on supported platforms [#95][]
+* Informational printout now added to CTest [#95][]
+* Better single file generation [#95][]
 * Added support for GTest on MSVC 2017 (but not in C++17 mode, will need next version of GTest)
-* Types now have a specific size, separate from the expected number - cleaner and more powerful internally [#92]
-* Examples now run as part of testing [#99]
+* Types now have a specific size, separate from the expected number - cleaner and more powerful internally [#92][]
+* Examples now run as part of testing [#99][]
 
 [#64]: https://github.com/CLIUtils/CLI11/issues/64
 [#90]: https://github.com/CLIUtils/CLI11/issues/90
@@ -444,22 +480,50 @@ Other, non-user facing changes:
 [#99]: https://github.com/CLIUtils/CLI11/pull/99
 [#100]: https://github.com/CLIUtils/CLI11/pull/100
 
+### Version 1.5.1: Access
+
+This patch release adds better access to the App programmatically, to assist with writing custom converters to other formats. It also improves the help output, and uses a new feature in CLI11 1.5 to fix an old "quirk" in the way unlimited options and positionals interact.
+
+* Make mixing unlimited positionals and options more intuitive [#102][]
+* Add missing getters `get_options` and `get_description` to App [#105][]
+* The app name now can be set, and will override the auto name if present [#105][]
+* Add `(REQUIRED)` for required options [#104][]
+* Print simple name for Needs/Excludes [#104][]
+* Use Needs instead of Requires in help print [#104][]
+* Groups now are listed in the original definition order [#106][]
+
+[#102]: https://github.com/CLIUtils/CLI11/issues/102
+[#104]: https://github.com/CLIUtils/CLI11/pull/104
+[#105]: https://github.com/CLIUtils/CLI11/pull/105
+[#106]: https://github.com/CLIUtils/CLI11/pull/106
+
+### Version 1.5.2: LICENSE in single header mode
+
+This is a quick patch release that makes LICENSE part of the single header file, making it easier to include. Minor cleanup from codacy. No significant code changes from 1.5.1.
+
+### Version 1.5.3: Compiler compatibility
+
+This version fixes older AppleClang compilers by removing the optimization for casting. The minimum version of Boost Optional supported has been clarified to be 1.58. CUDA 7.0 NVCC is now supported.
+
+### Version 1.5.4: Optionals
+
+This version fixes the optional search in the single file version; some macros were not yet defined when it did the search. You can define the `CLI11_*_OPTIONAL` macros to 0 if needed to eliminate the search.
 
 ## Version 1.4: More feedback
 
 This version adds lots of smaller fixes and additions after the refactor in version 1.3. More ways to download and use CLI11 in CMake have been added. INI files have improved support.
 
-* Lexical cast is now more strict than before [#68] and fails on overflow [#84]
+* Lexical cast is now more strict than before [#68][] and fails on overflow [#84][]
 * Added `get_parent()` to access the parent from a subcommand
-* Added `ExistingPath` validator  [#73]
-* `app.allow_ini_extras()` added to allow extras in INI files [#70]
+* Added `ExistingPath` validator  [#73][]
+* `app.allow_ini_extras()` added to allow extras in INI files [#70][]
 * Multiline INI comments now supported
-* Descriptions can now be written with `config_to_str` [#66]
-* Double printing of error message fixed [#77]
-* Renamed `requires` to `needs` to avoid C++20 keyword [#75], [#82]
-* MakeSingleHeader now works if outside of git [#78]
-* Adding install support for CMake [#79], improved support for `find_package` [#83], [#84]
-* Added support for Conan.io [#83]
+* Descriptions can now be written with `config_to_str` [#66][]
+* Double printing of error message fixed [#77][]
+* Renamed `requires` to `needs` to avoid C++20 keyword [#75][], [#82][]
+* MakeSingleHeader now works if outside of git [#78][]
+* Adding install support for CMake [#79][], improved support for `find_package` [#83][], [#84][]
+* Added support for Conan.io [#83][]
 
 [#70]: https://github.com/CLIUtils/CLI11/issues/70
 [#75]: https://github.com/CLIUtils/CLI11/issues/75
@@ -509,7 +573,7 @@ favorite CLI programs. Error messages and help messages are better and more flex
 * Single internal arg parse function [#56](https://github.com/CLIUtils/CLI11/pull/56)
 * Allow options to be disabled from INI file, rename `add_config` to `set_config` [#60](https://github.com/CLIUtils/CLI11/pull/60)
 
-> ### Converting from CLI11 1.2:
+> ### Converting from CLI11 1.2
 >
 > * `app.parse` no longer returns a vector. Instead, use `app.remaining(true)`.
 > * `"hidden"` is no longer a special group name, instead use `""`
@@ -562,7 +626,7 @@ This release focused on cleaning up the most exotic compiler warnings, fixing a
 
 ## Version 0.8: CLIUtils
 
-This release moved the repository to the CLIUtils master organization.
+This release moved the repository to the CLIUtils main organization.
 
 * Moved to CLIUtils on GitHub
 * Fixed docs build and a few links
@@ -597,7 +661,7 @@ Lots of cleanup and docs additions made it into this release. Parsing is simpler
 * `->ignore_case()` added to subcommands, options, and `add_set_ignore_case`. Subcommands inherit setting from parent App on creation.
 * Subcommands now can be "chained", that is, left over arguments can now include subcommands that then get parsed. Subcommands are now a list (`get_subcommands`). Added `got_subcommand(App_or_name)` to check for subcommands.
 * Added `.allow_extras()` to disable error on failure. Parse returns a vector of leftover options. Renamed error to `ExtrasError`, and now triggers on extra options too.
-* Added `require_subcommand` to `App`, to simplify forcing subcommands. Do **not** do `add_subcommand()->require_subcommand`, since that is the subcommand, not the master `App`.
+* Added `require_subcommand` to `App`, to simplify forcing subcommands. Do **not** do `add_subcommand()->require_subcommand`, since that is the subcommand, not the main `App`.
 * Added printout of ini file text given parsed options, skips flags.
 * Support for quotes and spaces in ini files
 * Fixes to allow support for Windows (added Appveyor) (Uses `-`, not `/` syntax)
diff --git a/packages/CLI11/CLI11.CPack.Description.txt b/packages/CLI11/CLI11.CPack.Description.txt
index 1fd074a4b86d84a8d59234648edc8c75533dfaa4..9a018919cf715d4f02e8fa52be4b7ebace6ee202 100644
--- a/packages/CLI11/CLI11.CPack.Description.txt
+++ b/packages/CLI11/CLI11.CPack.Description.txt
@@ -1,2 +1 @@
 CLI11 provides all the features you expect in a powerful command line parser, with a beautiful, minimal syntax and no dependencies beyond C++11. It is header only, and comes in a single file form for easy inclusion in projects. It is easy to use for small projects, but powerful enough for complex command line projects, and can be customized for frameworks.
-
diff --git a/packages/CLI11/CLI11.hpp.in b/packages/CLI11/CLI11.hpp.in
index 8de9c532e300e14edf6c66550eea60431d724b1f..9439a4300340b110681be4d38e05da15451e7bf7 100644
--- a/packages/CLI11/CLI11.hpp.in
+++ b/packages/CLI11/CLI11.hpp.in
@@ -5,7 +5,7 @@
 // This is a standalone header file generated by MakeSingleHeader.py in CLI11/scripts
 // from: {git}
 //
-// CLI11 {version} Copyright (c) 2017-2020 University of Cincinnati, developed by Henry
+// CLI11 {version} Copyright (c) 2017-2021 University of Cincinnati, developed by Henry
 // Schreiner under NSF AWARD 1414736. All rights reserved.
 //
 // Redistribution and use in source and binary forms of CLI11, with or without
@@ -31,6 +31,8 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
+#pragma once
+
 // Standard combined includes:
 {public_includes}
 
diff --git a/packages/CLI11/CMakeLists.txt b/packages/CLI11/CMakeLists.txt
index ee0228ee5b20206c523c09325e1f8a3921e397b8..1f4313ff59743a81a75ce1f5e15d3afa29c7c5ed 100644
--- a/packages/CLI11/CMakeLists.txt
+++ b/packages/CLI11/CMakeLists.txt
@@ -2,43 +2,57 @@ cmake_minimum_required(VERSION 3.4)
 # Note: this is a header only library. If you have an older CMake than 3.4,
 # just add the CLI11/include directory and that's all you need to do.
 
-# Make sure users don't get warnings on a tested (3.4 to 3.16) version
+# Make sure users don't get warnings on a tested (3.4 to 3.22) version
 # of CMake. For most of the policies, the new version is better (hence the change).
-# We don't use the 3.4...3.17 syntax because of a bug in an older MSVC's
+# We don't use the 3.4...3.21 syntax because of a bug in an older MSVC's
 # built-in and modified CMake 3.11
-if(${CMAKE_VERSION} VERSION_LESS 3.17)
-    cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
+if(${CMAKE_VERSION} VERSION_LESS 3.22)
+  cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
 else()
-    cmake_policy(VERSION 3.17)
+  cmake_policy(VERSION 3.22)
 endif()
 
 set(VERSION_REGEX "#define CLI11_VERSION[ \t]+\"(.+)\"")
 
 # Read in the line containing the version
-file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/Version.hpp"
-    VERSION_STRING REGEX ${VERSION_REGEX})
+file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/Version.hpp" VERSION_STRING
+     REGEX ${VERSION_REGEX})
 
 # Pick out just the version
 string(REGEX REPLACE ${VERSION_REGEX} "\\1" VERSION_STRING "${VERSION_STRING}")
 
 # Add the project
-project(CLI11 LANGUAGES CXX VERSION ${VERSION_STRING})
+project(
+  CLI11
+  LANGUAGES CXX
+  VERSION ${VERSION_STRING})
 
 # Print the version number of CMake if this is the main project
 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
-    message(STATUS "CMake ${CMAKE_VERSION}")
+  message(STATUS "CMake ${CMAKE_VERSION}")
+
+  find_package(Doxygen)
+
+  if(CMAKE_VERSION VERSION_LESS 3.10)
+    message(STATUS "CMake 3.10+ adds Doxygen support. Update CMake to build documentation")
+  elseif(NOT Doxygen_FOUND)
+    message(STATUS "Doxygen not found, building docs has been disabled")
+  endif()
+
+  include(CTest)
+else()
+  if(NOT DEFINED BUILD_TESTING)
+    set(BUILD_TESTING OFF)
+  endif()
 endif()
 
 include(CMakeDependentOption)
 include(GNUInstallDirs)
-include(CTest)
 
 if(NOT CMAKE_VERSION VERSION_LESS 3.11)
-    include(FetchContent)
+  include(FetchContent)
 endif()
 
-find_package(Doxygen)
-
 list(APPEND force-libcxx "CMAKE_CXX_COMPILER_ID STREQUAL \"Clang\"")
 list(APPEND force-libcxx "CMAKE_SYSTEM_NAME STREQUAL \"Linux\"")
 list(APPEND force-libcxx "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME")
@@ -49,83 +63,70 @@ list(APPEND build-docs "Doxygen_FOUND")
 
 # Necessary to support paths with spaces, see #457
 if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/docs")
-    set(docs_EXIST TRUE)
+  set(docs_EXIST TRUE)
 else()
-    set(docs_EXIST FALSE)
+  set(docs_EXIST FALSE)
 endif()
 list(APPEND build-docs "docs_EXIST")
 
 if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples")
-    set(examples_EXIST TRUE)
+  set(examples_EXIST TRUE)
 else()
-    set(examples_EXIST FALSE)
+  set(examples_EXIST FALSE)
 endif()
 
 option(CLI11_WARNINGS_AS_ERRORS "Turn all warnings into errors (for CI)")
 option(CLI11_SINGLE_FILE "Generate a single header file")
-cmake_dependent_option(CLI11_SANITIZERS
-    "Download the sanitizers CMake config" OFF
-    "NOT CMAKE_VERSION VERSION_LESS 3.11" OFF)
+cmake_dependent_option(CLI11_SANITIZERS "Download the sanitizers CMake config" OFF
+                       "NOT CMAKE_VERSION VERSION_LESS 3.11" OFF)
 
-cmake_dependent_option(CLI11_BUILD_DOCS
-    "Build CLI11 documentation" ON
-    "${build-docs}" OFF)
+cmake_dependent_option(CLI11_BUILD_DOCS "Build CLI11 documentation" ON "${build-docs}" OFF)
 
-cmake_dependent_option(CLI11_BUILD_TESTS
-    "Build CLI11 tests" ON
-    "BUILD_TESTING;CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
+cmake_dependent_option(CLI11_BUILD_TESTS "Build CLI11 tests" ON
+                       "BUILD_TESTING;CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
 
-cmake_dependent_option(CLI11_BUILD_EXAMPLES
-    "Build CLI11 examples" ON
-    "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME;${examples_EXIST}" OFF)
+cmake_dependent_option(CLI11_BUILD_EXAMPLES "Build CLI11 examples" ON
+                       "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME;${examples_EXIST}" OFF)
 
-cmake_dependent_option(CLI11_BUILD_EXAMPLES_JSON
-    "Build CLI11 json example" OFF
-    "CLI11_BUILD_EXAMPLES;NOT CMAKE_VERSION VERSION_LESS 3.11" OFF)
+cmake_dependent_option(CLI11_BUILD_EXAMPLES_JSON "Build CLI11 json example" OFF
+                       "CLI11_BUILD_EXAMPLES;NOT CMAKE_VERSION VERSION_LESS 3.11" OFF)
 
-cmake_dependent_option(CLI11_SINGLE_FILE_TESTS
-    "Duplicate all the tests for a single file build" OFF
-    "BUILD_TESTING;CLI11_SINGLE_FILE" OFF)
+cmake_dependent_option(CLI11_SINGLE_FILE_TESTS "Duplicate all the tests for a single file build"
+                       OFF "BUILD_TESTING;CLI11_SINGLE_FILE" OFF)
 
-cmake_dependent_option(CLI11_INSTALL
-    "Install the CLI11 folder to include during install process" ON
-    "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
+cmake_dependent_option(CLI11_INSTALL "Install the CLI11 folder to include during install process"
+                       ON "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
 
-cmake_dependent_option(CLI11_FORCE_LIBCXX
-    "Force clang to use libc++ instead of libstdc++ (Linux only)" OFF
-    "${force-libcxx}" OFF)
+cmake_dependent_option(
+  CLI11_FORCE_LIBCXX "Force clang to use libc++ instead of libstdc++ (Linux only)" OFF
+  "${force-libcxx}" OFF)
 
-cmake_dependent_option(CLI11_CUDA_TESTS
-    "Build the tests with NVCC to check for warnings there - requires CMake 3.9+" OFF
-    "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
+cmake_dependent_option(
+  CLI11_CUDA_TESTS "Build the tests with NVCC to check for warnings there - requires CMake 3.9+"
+  OFF "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME" OFF)
 
-cmake_dependent_option(CLI11_CLANG_TIDY
-    "Look for and use Clang-Tidy" OFF
-    "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME;NOT CMAKE_VERSION VERSION_LESS 3.6" OFF)
-set(CLI11_CLANG_TIDY_OPTIONS "" CACHE STRING "Clang tidy options, such as -fix, semicolon separated")
+cmake_dependent_option(
+  CLI11_CLANG_TIDY "Look for and use Clang-Tidy" OFF
+  "CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME;NOT CMAKE_VERSION VERSION_LESS 3.6" OFF)
+set(CLI11_CLANG_TIDY_OPTIONS
+    ""
+    CACHE STRING "Clang tidy options, such as -fix, semicolon separated")
 
 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND NOT DEFINED CMAKE_CXX_STANDARD)
-    set(CMAKE_CXX_STANDARD 11)
+  set(CMAKE_CXX_STANDARD 11)
 endif()
 
 if(NOT DEFINED CMAKE_CXX_EXTENSIONS)
-    set(CMAKE_CXX_EXTENSIONS OFF)
+  set(CMAKE_CXX_EXTENSIONS OFF)
 endif()
 
 if(NOT DEFINED CMAKE_CXX_STANDARD_REQUIRED)
-    set(CMAKE_CXX_STANDARD_REQUIRED ON)
+  set(CMAKE_CXX_STANDARD_REQUIRED ON)
 endif()
 
-
 # Allow IDE's to group targets into folders
 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME)
-    set_property(GLOBAL PROPERTY USE_FOLDERS ON)
-endif()
-
-if(CMAKE_VERSION VERSION_LESS 3.10)
-    message(STATUS "CMake 3.10+ adds Doxygen support. Update CMake to build documentation")
-elseif(NOT Doxygen_FOUND)
-    message(STATUS "Doxygen not found, building docs has been disabled")
+  set_property(GLOBAL PROPERTY USE_FOLDERS ON)
 endif()
 
 # Special target that adds warnings. Is not exported.
@@ -135,141 +136,146 @@ set(unix-warnings -Wall -Wextra -pedantic -Wshadow -Wsign-conversion -Wswitch-en
 
 # Buggy in GCC 4.8
 if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9)
-    list(APPEND unix-warnings -Weffc++)
+  list(APPEND unix-warnings -Weffc++)
 endif()
 
-target_compile_options(CLI11_warnings
-   INTERFACE
-     $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>
-     $<$<CXX_COMPILER_ID:MSVC>:/W4 $<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:/WX>>
-     $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:${unix-warnings} $<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:-Werror>>)
-
-
- if(NOT CMAKE_VERSION VERSION_LESS 3.13)
-     target_link_options(CLI11_warnings
-         INTERFACE
-         $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>)
- endif()
+target_compile_options(
+  CLI11_warnings
+  INTERFACE $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>
+            $<$<CXX_COMPILER_ID:MSVC>:/W4
+            $<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:/WX>>
+            $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:${unix-warnings}
+            $<$<BOOL:${CLI11_WARNINGS_AS_ERRORS}>:-Werror>>)
 
+if(NOT CMAKE_VERSION VERSION_LESS 3.13)
+  target_link_options(CLI11_warnings INTERFACE $<$<BOOL:${CLI11_FORCE_LIBCXX}>:-stdlib=libc++>)
+endif()
 
 # Allow IDE's to group targets into folders
 add_library(CLI11 INTERFACE)
 add_library(CLI11::CLI11 ALIAS CLI11) # for add_subdirectory calls
 
 # Duplicated because CMake adds the current source dir if you don't.
-target_include_directories(CLI11 INTERFACE
-     $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
-     $<INSTALL_INTERFACE:include>)
-
+target_include_directories(CLI11 INTERFACE $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
+                                           $<INSTALL_INTERFACE:include>)
+
+if(CMAKE_VERSION VERSION_LESS 3.8)
+  # This might not be a complete list
+  target_compile_features(
+    CLI11
+    INTERFACE cxx_lambdas
+              cxx_nullptr
+              cxx_override
+              cxx_range_for
+              cxx_right_angle_brackets
+              cxx_strong_enums
+              cxx_constexpr
+              cxx_auto_type)
+else()
+  target_compile_features(CLI11 INTERFACE cxx_std_11)
+endif()
 
 # To see in IDE, headers must be listed for target
 set(header-patterns "${PROJECT_SOURCE_DIR}/include/CLI/*")
-if(NOT CMAKE_VERSION VERSION_LESS 3.12)
+if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND NOT CMAKE_VERSION VERSION_LESS 3.12)
   list(INSERT header-patterns 0 CONFIGURE_DEPENDS)
 endif()
 
 file(GLOB CLI11_headers ${header-patterns})
 
 # Allow tests to be run on CUDA
- if(CLI11_CUDA_TESTS)
-     enable_language(CUDA)
-
-     # Print out warning and error numbers
-     set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcudafe --display_error_number")
- endif()
+if(CLI11_CUDA_TESTS)
+  enable_language(CUDA)
 
+  # Print out warning and error numbers
+  set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcudafe --display_error_number")
+endif()
 
 # Prepare Clang-Tidy
 if(CLI11_CLANG_TIDY)
-    find_program(
-        CLANG_TIDY_EXE
-        NAMES "clang-tidy"
-        DOC "Path to clang-tidy executable"
-        REQUIRED
-        )
-
-    set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" ${CLI11_CLANG_TIDY_OPTIONS})
-endif()
+  find_program(
+    CLANG_TIDY_EXE
+    NAMES "clang-tidy"
+    DOC "Path to clang-tidy executable" REQUIRED)
 
+  set(DO_CLANG_TIDY "${CLANG_TIDY_EXE}" ${CLI11_CLANG_TIDY_OPTIONS})
+endif()
 
 # This folder should be installed
 if(CLI11_INSTALL)
-    install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/"
-        DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
+  install(DIRECTORY "${PROJECT_SOURCE_DIR}/include/" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}")
 
-    # Make an export target
-    install(TARGETS CLI11 EXPORT CLI11Targets)
+  # Make an export target
+  install(TARGETS CLI11 EXPORT CLI11Targets)
 
-    # Use find_package on the installed package
-    # Since we have no custom code, we can directly write this
-    # to Config.cmake (otherwise we'd have a custom config and would
-    # import Targets.cmake
+  # Use find_package on the installed package
+  # Since we have no custom code, we can directly write this
+  # to Config.cmake (otherwise we'd have a custom config and would
+  # import Targets.cmake
 
-    # Add the version in a CMake readable way
-    configure_file("cmake/CLI11ConfigVersion.cmake.in"
-       "CLI11ConfigVersion.cmake" @ONLY)
+  # Add the version in a CMake readable way
+  configure_file("cmake/CLI11ConfigVersion.cmake.in" "CLI11ConfigVersion.cmake" @ONLY)
 
-    # Make version available in the install
-    install(FILES "${PROJECT_BINARY_DIR}/CLI11ConfigVersion.cmake"
-        DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CLI11")
+  # Make version available in the install
+  install(FILES "${PROJECT_BINARY_DIR}/CLI11ConfigVersion.cmake"
+          DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/CLI11")
 
-    # Install the export target as a file
-    install(EXPORT CLI11Targets
-        FILE CLI11Config.cmake
-        NAMESPACE CLI11::
-        DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/CLI11")
+  # Install the export target as a file
+  install(
+    EXPORT CLI11Targets
+    FILE CLI11Config.cmake
+    NAMESPACE CLI11::
+    DESTINATION "${CMAKE_INSTALL_DATADIR}/cmake/CLI11")
 
-    # Use find_package on the installed package
-    export(TARGETS CLI11
-        NAMESPACE CLI11::
-        FILE CLI11Targets.cmake)
+  # Use find_package on the installed package
+  export(
+    TARGETS CLI11
+    NAMESPACE CLI11::
+    FILE CLI11Targets.cmake)
 
-    include(cmake/CLI11GeneratePkgConfig.cmake)
+  include(cmake/CLI11GeneratePkgConfig.cmake)
 
-    # Register in the user cmake package registry
-    export(PACKAGE CLI11)
+  # Register in the user cmake package registry
+  export(PACKAGE CLI11)
 endif()
 
 if(CLI11_SINGLE_FILE)
-    # Single file test
-    if(CMAKE_VERSION VERSION_LESS 3.12)
-        find_package(PythonInterp REQUIRED)
-        add_executable(Python::Interpreter IMPORTED)
-        set_target_properties(Python::Interpreter
-            PROPERTIES
-            IMPORTED_LOCATION "${PYTHON_EXECUTABLE}"
-            VERSION "${PYTHON_VERSION_STRING}")
-    else()
-        find_package(Python COMPONENTS Interpreter REQUIRED)
-    endif()
-
-    file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include")
-    add_custom_command(OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
-        COMMAND Python::Interpreter
-        "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py"
-        ${CLI11_headers}
-        --main "${CMAKE_CURRENT_SOURCE_DIR}/CLI11.hpp.in"
-        --output "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
-        --version "${CLI11_VERSION}"
-        DEPENDS
-        "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp"
-        ${CLI11_headers})
-    add_custom_target(CLI11-generate-single-file ALL
-        DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp")
-    set_property(TARGET CLI11-generate-single-file PROPERTY FOLDER "Scripts")
-    install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
-        DESTINATION include)
-    add_library(CLI11_SINGLE INTERFACE)
-    target_link_libraries(CLI11_SINGLE INTERFACE CLI11)
-    add_dependencies(CLI11_SINGLE CLI11-generate-single-file)
-    target_compile_definitions(CLI11_SINGLE INTERFACE -DCLI11_SINGLE_FILE)
-    target_include_directories(CLI11_SINGLE INTERFACE
-        $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/>
-        $<INSTALL_INTERFACE:include>)
+  # Single file test
+  if(CMAKE_VERSION VERSION_LESS 3.12)
+    find_package(PythonInterp REQUIRED)
+    add_executable(Python::Interpreter IMPORTED)
+    set_target_properties(Python::Interpreter PROPERTIES IMPORTED_LOCATION "${PYTHON_EXECUTABLE}"
+                                                         VERSION "${PYTHON_VERSION_STRING}")
+  else()
+    find_package(
+      Python
+      COMPONENTS Interpreter
+      REQUIRED)
+  endif()
+
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/include")
+  add_custom_command(
+    OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp"
+    COMMAND
+      Python::Interpreter "${CMAKE_CURRENT_SOURCE_DIR}/scripts/MakeSingleHeader.py"
+      ${CLI11_headers} --main "${CMAKE_CURRENT_SOURCE_DIR}/CLI11.hpp.in" --output
+      "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" --version "${CLI11_VERSION}"
+    DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/CLI/CLI.hpp" ${CLI11_headers})
+  add_custom_target(CLI11-generate-single-file ALL
+                    DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp")
+  set_property(TARGET CLI11-generate-single-file PROPERTY FOLDER "Scripts")
+  install(FILES "${CMAKE_CURRENT_BINARY_DIR}/include/CLI11.hpp" DESTINATION include)
+  add_library(CLI11_SINGLE INTERFACE)
+  target_link_libraries(CLI11_SINGLE INTERFACE CLI11)
+  add_dependencies(CLI11_SINGLE CLI11-generate-single-file)
+  target_compile_definitions(CLI11_SINGLE INTERFACE -DCLI11_SINGLE_FILE)
+  target_include_directories(
+    CLI11_SINGLE INTERFACE $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include/>
+                           $<INSTALL_INTERFACE:include>)
 endif()
 
-
 if(CLI11_BUILD_TESTS)
+  include(CTest)
   add_subdirectory(tests)
 endif()
 
@@ -283,7 +289,7 @@ endif()
 
 # From a build system, this might not be included.
 if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/book")
-    add_subdirectory(book)
+  add_subdirectory(book)
 endif()
 
 # Packaging support
@@ -316,12 +322,10 @@ set(CPACK_SOURCE_IGNORE_FILES
     .swp
     /.all-contributorsrc
     /.appveyor.yml
-    /.pre-commit.*yaml
-)
+    /.pre-commit.*yaml)
 
 set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE "all")
 set(CPACK_DEBIAN_COMPRESSION_TYPE "xz")
 set(CPACK_DEBIAN_PACKAGE_NAME "libcli11-dev")
 
 include(CPack)
-
diff --git a/packages/CLI11/CPPLINT.cfg b/packages/CLI11/CPPLINT.cfg
index 0a1758da0e5b703d4677bcaf60d171455c4aee66..24dd86524af1957a994dba4fe53b65f4406d20ad 100644
--- a/packages/CLI11/CPPLINT.cfg
+++ b/packages/CLI11/CPPLINT.cfg
@@ -11,4 +11,3 @@ filter=-runtime/references  # Requires fundamental change of API, don't see need
 filter=-whitespace/blank_line  # Unnecessarily strict with blank lines that otherwise help with readability
 filter=-whitespace/indent  # Requires strange 3-space indent of private/protected/public markers
 filter=-whitespace/parens,-whitespace/braces  # Conflict with clang-format
-
diff --git a/packages/CLI11/README.md b/packages/CLI11/README.md
index 846d44ec7310e567fa32897088c03d659d1e3b56..ad1ac1e97b63de4b8c4501b74831c149c3633d21 100644
--- a/packages/CLI11/README.md
+++ b/packages/CLI11/README.md
@@ -2,18 +2,20 @@
 
 ![CLI11 Logo](./docs/CLI11_300.png)
 
-[![Build Status Linux and macOS][travis-badge]][travis]
-[![Build Status Windows][appveyor-badge]][appveyor]
 [![Build Status Azure][azure-badge]][azure]
 [![Actions Status][actions-badge]][actions-link]
+[![Build Status AppVeyor][appveyor-badge]][appveyor]
 [![Code Coverage][codecov-badge]][codecov]
 [![Codacy Badge][codacy-badge]][codacy-link]
-[![Join the chat at https://gitter.im/CLI11gitter/Lobby][gitter-badge]][gitter]
 [![License: BSD][license-badge]](./LICENSE)
-[![Latest release][releases-badge]][github releases]
 [![DOI][doi-badge]][doi-link]
+
+[![Gitter chat][gitter-badge]][gitter]
+[![Latest GHA release][releases-badge]][github releases]
+[![Latest release][repology-badge]][repology]
 [![Conan.io][conan-badge]][conan-link]
-[![Try CLI11 1.9 online][wandbox-badge]][wandbox-link]
+[![Conda Version][conda-badge]][conda-link]
+[![Try CLI11 2.1 online][wandbox-badge]][wandbox-link]
 
 [What's new](./CHANGELOG.md) •
 [Documentation][gitbook] •
@@ -23,48 +25,48 @@ CLI11 is a command line parser for C++11 and beyond that provides a rich feature
 
 ## Table of Contents
 
--   [Background](#background)
-    -   [Introduction](#introduction)
-    -   [Why write another CLI parser?](#why-write-another-cli-parser)
-    -   [Other parsers](#other-parsers)
-    -   [Features not supported by this library](#features-not-supported-by-this-library)
--   [Install](#install)
--   [Usage](#usage)
-    -   [Adding options](#adding-options)
-        -   [Option types](#option-types)
-        -   [Example](#example)
-        -   [Option options](#option-options)
-        -   [Validators](#validators)
-            -   [Transforming Validators](#transforming-validators)
-            -   [Validator operations](#validator-operations)
-            -   [Custom Validators](#custom-validators)
-            -   [Querying Validators](#querying-validators)
-        -   [Getting Results](#getting-results)
-    -   [Subcommands](#subcommands)
-        -   [Subcommand options](#subcommand-options)
-        -   [Option groups](#option-groups)
-        -   [Callbacks](#callbacks)
-    -   [Configuration file](#configuration-file)
-    -   [Inheriting defaults](#inheriting-defaults)
-    -   [Formatting](#formatting)
-    -   [Subclassing](#subclassing)
-    -   [How it works](#how-it-works)
-    -   [Utilities](#utilities)
-    -   [Other libraries](#other-libraries)
--   [API](#api)
--   [Examples](#Examples)
--   [Contribute](#contribute)
--   [License](#license)
-
-Features that were added in the last released major version are marked with "🆕". Features only available in master are marked with "🚧".
+* [Background](#background)
+  * [Introduction](#introduction)
+  * [Why write another CLI parser?](#why-write-another-cli-parser)
+  * [Other parsers](#other-parsers)
+  * [Features not supported by this library](#features-not-supported-by-this-library)
+* [Install](#install)
+* [Usage](#usage)
+  * [Adding options](#adding-options)
+    * [Option types](#option-types)
+    * [Example](#example)
+    * [Option options](#option-options)
+    * [Validators](#validators)
+      * [Transforming Validators](#transforming-validators)
+      * [Validator operations](#validator-operations)
+      * [Custom Validators](#custom-validators)
+      * [Querying Validators](#querying-validators)
+      * [Getting Results](#getting-results)
+  * [Subcommands](#subcommands)
+    * [Subcommand options](#subcommand-options)
+    * [Option groups](#option-groups)
+    * [Callbacks](#callbacks)
+  * [Configuration file](#configuration-file)
+  * [Inheriting defaults](#inheriting-defaults)
+  * [Formatting](#formatting)
+  * [Subclassing](#subclassing)
+  * [How it works](#how-it-works)
+  * [Utilities](#utilities)
+  * [Other libraries](#other-libraries)
+* [API](#api)
+* [Examples](#Examples)
+* [Contribute](#contribute)
+* [License](#license)
+
+Features that were added in the last released major version are marked with "🆕". Features only available in main are marked with "🚧".
 
 ## Background
 
 ### Introduction
 
 CLI11 provides all the features you expect in a powerful command line parser, with a beautiful, minimal syntax and no dependencies beyond C++11. It is header only, and comes in a single file form for easy inclusion in projects. It is easy to use for small projects, but powerful enough for complex command line projects, and can be customized for frameworks.
-It is tested on [Travis][], [AppVeyor][], [Azure][], and [GitHub Actions][actions-link], and is used by the [GooFit GPU fitting framework][goofit]. It was inspired by [`plumbum.cli`][plumbum] for Python. CLI11 has a user friendly introduction in this README, a more in-depth tutorial [GitBook][], as well as [API documentation][api-docs] generated by Travis.
-See the [changelog](./CHANGELOG.md) or [GitHub Releases][] for details for current and past releases. Also see the [Version 1.0 post][], [Version 1.3 post][], or [Version 1.6 post][] for more information.
+It is tested on [Azure][] and [GitHub Actions][actions-link], and was originally used by the [GooFit GPU fitting framework][goofit]. It was inspired by [`plumbum.cli`][plumbum] for Python. CLI11 has a user friendly introduction in this README, a more in-depth tutorial [GitBook][], as well as [API documentation][api-docs] generated by Travis.
+See the [changelog](./CHANGELOG.md) or [GitHub Releases][] for details for current and past releases. Also see the [Version 1.0 post][], [Version 1.3 post][], [Version 1.6 post][], or [Version 2.0 post][] for more information.
 
 You can be notified when new releases are made by subscribing to <https://github.com/CLIUtils/CLI11/releases.atom> on an RSS reader, like Feedly, or use the releases mode of the GitHub watching tool.
 
@@ -72,21 +74,21 @@ You can be notified when new releases are made by subscribing to <https://github
 
 An acceptable CLI parser library should be all of the following:
 
--   Easy to include (i.e., header only, one file if possible, **no external requirements**).
--   Short, simple syntax: This is one of the main reasons to use a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability.
--   C++11 or better: Should work with GCC 4.8+ (default on CentOS/RHEL 7), Clang 3.4+, AppleClang 7+, NVCC 7.0+, or MSVC 2015+.
--   Work on Linux, macOS, and Windows.
--   Well tested using [Travis][] (Linux) and [AppVeyor][] (Windows) or [Azure][] (all three). "Well" is defined as having good coverage measured by [CodeCov][].
--   Clear help printing.
--   Nice error messages.
--   Standard shell idioms supported naturally, like grouping flags, a positional separator, etc.
--   Easy to execute, with help, parse errors, etc. providing correct exit and details.
--   Easy to extend as part of a framework that provides "applications" to users.
--   Usable subcommand syntax, with support for multiple subcommands, nested subcommands, option groups, and optional fallthrough (explained later).
--   Ability to add a configuration file (`ini` or `TOML`🆕 format), and produce it as well.
--   Produce real values that can be used directly in code, not something you have pay compute time to look up, for HPC applications.
--   Work with standard types, simple custom types, and extensible to exotic types.
--   Permissively licensed.
+* Easy to include (i.e., header only, one file if possible, **no external requirements**).
+* Short, simple syntax: This is one of the main reasons to use a CLI parser, it should make variables from the command line nearly as easy to define as any other variables. If most of your program is hidden in CLI parsing, this is a problem for readability.
+* C++11 or better: Should work with GCC 4.8+ (default on CentOS/RHEL 7), Clang 3.4+, AppleClang 7+, NVCC 7.0+, or MSVC 2015+.
+* Work on Linux, macOS, and Windows.
+* Well tested on all common platforms and compilers. "Well" is defined as having good coverage measured by [CodeCov][].
+* Clear help printing.
+* Nice error messages.
+* Standard shell idioms supported naturally, like grouping flags, a positional separator, etc.
+* Easy to execute, with help, parse errors, etc. providing correct exit and details.
+* Easy to extend as part of a framework that provides "applications" to users.
+* Usable subcommand syntax, with support for multiple subcommands, nested subcommands, option groups, and optional fallthrough (explained later).
+* Ability to add a configuration file (`TOML`, `INI`, or custom format), and produce it as well.
+* Produce real values that can be used directly in code, not something you have pay compute time to look up, for HPC applications.
+* Work with standard types, simple custom types, and extensible to exotic types.
+* Permissively licensed.
 
 ### Other parsers
 
@@ -126,43 +128,59 @@ So, this library was designed to provide a great syntax, good compiler compatibi
 
 There are some other possible "features" that are intentionally not supported by this library:
 
--   Non-standard variations on syntax, like `-long` options. This is non-standard and should be avoided, so that is enforced by this library.
--   Completion of partial options, such as Python's `argparse` supplies for incomplete arguments. It's better not to guess. Most third party command line parsers for python actually reimplement command line parsing rather than using argparse because of this perceived design flaw.
--   Autocomplete: This might eventually be added to both Plumbum and CLI11, but it is not supported yet.
--   Wide strings / unicode: Since this uses the standard library only, it might be hard to properly implement, but I would be open to suggestions in how to do this.
+* Non-standard variations on syntax, like `-long` options. This is non-standard and should be avoided, so that is enforced by this library.
+* Completion of partial options, such as Python's `argparse` supplies for incomplete arguments. It's better not to guess. Most third party command line parsers for python actually reimplement command line parsing rather than using argparse because of this perceived design flaw (recent versions do have an option to disable it).
+* Autocomplete: This might eventually be added to both Plumbum and CLI11, but it is not supported yet.
+* Wide strings / unicode: Since this uses the standard library only, it might be hard to properly implement, but I would be open to suggestions in how to do this.
 
 ## Install
 
 To use, there are several methods:
 
-1.  All-in-one local header: Copy `CLI11.hpp` from the [most recent release][github releases] into your include directory, and you are set. This is combined from the source files  for every release. This includes the entire command parser library, but does not include separate utilities (like `Timer`, `AutoTimer`). The utilities are completely self contained and can be copied separately.
-2.  All-in-one global header: Like above, but copying the file to a shared folder location like `/opt/CLI11`. Then, the C++ include path has to be extended to point at this folder. With CMake, use `include_directories(/opt/CLI11)`
-3.  Local headers and target: Use `CLI/*.hpp` files. You could check out the repository as a git submodule, for example. With CMake, you can use `add_subdirectory` and the `CLI11::CLI11` interface target when linking. If not using a submodule, you must ensure that the copied files are located inside the same tree directory than your current project, to prevent an error with CMake and `add_subdirectory`.
-4.  Global headers: Use `CLI/*.hpp` files stored in a shared folder. You could check out the git repository in a system-wide folder, for example `/opt/`. With CMake, you could add to the include path via:
+* All-in-one local header: Copy `CLI11.hpp` from the [most recent release][github releases] into your include directory, and you are set. This is combined from the source files  for every release. This includes the entire command parser library, but does not include separate utilities (like `Timer`, `AutoTimer`). The utilities are completely self contained and can be copied separately.
+* All-in-one global header: Like above, but copying the file to a shared folder location like `/opt/CLI11`. Then, the C++ include path has to be extended to point at this folder. With CMake, use `include_directories(/opt/CLI11)`
+* Local headers and target: Use `CLI/*.hpp` files. You could check out the repository as a git submodule, for example. With CMake, you can use `add_subdirectory` and the `CLI11::CLI11` interface target when linking. If not using a submodule, you must ensure that the copied files are located inside the same tree directory than your current project, to prevent an error with CMake and `add_subdirectory`.
+* Global headers: Use `CLI/*.hpp` files stored in a shared folder. You could check out the git repository to a system-wide folder, for example `/opt/`. With CMake, you could add to the include path via:
+
 ```bash
 if(NOT DEFINED CLI11_DIR)
 set (CLI11_DIR "/opt/CLI11" CACHE STRING "CLI11 git repository")
 endif()
 include_directories(${CLI11_DIR}/include)
 ```
+
 And then in the source code (adding several headers might be needed to prevent linker errors):
+
 ```cpp
 #include "CLI/App.hpp"
 #include "CLI/Formatter.hpp"
 #include "CLI/Config.hpp"
 ```
-5.  Global headers and target: configuring and installing the project is required for linking CLI11 to your project in the same way as you would do with any other external library. With CMake, this step allows using `find_package(CLI11 CONFIG REQUIRED)` and then using the `CLI11::CLI11` target when linking. If `CMAKE_INSTALL_PREFIX` was changed during install to a specific folder like `/opt/CLI11`, then you have to pass `-DCLI11_DIR=/opt/CLI11` when building your current project. You can also use [Conan.io][conan-link] or [Hunter][].
+
+* Global headers and target: configuring and installing the project is required for linking CLI11 to your project in the same way as you would do with any other external library. With CMake, this step allows using `find_package(CLI11 CONFIG REQUIRED)` and then using the `CLI11::CLI11` target when linking. If `CMAKE_INSTALL_PREFIX` was changed during install to a specific folder like `/opt/CLI11`, then you have to pass `-DCLI11_DIR=/opt/CLI11` when building your current project. You can also use [Conan.io][conan-link] or [Hunter][].
     (These are just conveniences to allow you to use your favorite method of managing packages; it's just header only so including the correct path and
     using C++11 is all you really need.)
+* Via FetchContent in CMake 3.14+ (or 3.11+ with more work): you can add this with fetch-content, then use the `CLI11::CLI11` target as above, and CMake will download the project in the configure stage:
+
+```cmake
+include(FetchContent)
+FetchContent_Declare(
+  cli11
+  GIT_REPOSITORY https://github.com/CLIUtils/CLI11
+  GIT_TAG        v2.1.2
+)
+
+FetchContent_MakeAvailable(cli11)
+```
+
+It is highly recommended that you use the git hash for `GIT_TAG` instead of a tag or branch, as that will both be more secure, as well as faster to reconfigure - CMake will not have to reach out to the internet to see if the tag moved. You can also download just the single header file from the releases using `file(DOWNLOAD`.
 
 To build the tests, checkout the repository and use CMake:
 
 ```bash
-mkdir build
-cd build
-cmake ..
-make
-GTEST_COLOR=1 CTEST_OUTPUT_ON_FAILURE=1 make test
+cmake -S . -B build
+cmake --build build
+CTEST_OUTPUT_ON_FAILURE=1 cmake --build build -t test
 ```
 
 <details><summary>Note: Special instructions for GCC 8</summary><p>
@@ -224,18 +242,16 @@ While all options internally are the same type, there are several ways to add an
 app.add_option(option_name, help_str="")
 
 app.add_option(option_name,
-               variable_to_bind_to, // bool, char(see note)🚧, int, float, vector, enum, std::atomic 🚧, or string-like, or anything with a defined conversion from a string or that takes an int 🆕, double 🆕, or string in a constructor. Also allowed are tuples 🆕, std::array 🆕 or std::pair 🆕. Also supported are complex numbers🚧, wrapper types🚧, and containers besides vector🚧 of any other supported type.
+               variable_to_bind_to, // bool, char(see note), int, float, vector, enum, std::atomic, or string-like, or anything with a defined conversion from a string or that takes an int, double, or string in a constructor. Also allowed are tuples, std::array or std::pair. Also supported are complex numbers, wrapper types, and containers besides vectorof any other supported type.
                help_string="")
 
 app.add_option_function<type>(option_name,
                function <void(const type &value)>, // type can be any type supported by add_option
                help_string="")
 
-app.add_complex(... // Special case: support for complex numbers ⚠️. Complex numbers are now fully supported in the add_option so this function is redundant.
-
 // char as an option type is supported before 2.0 but in 2.0 it defaulted to allowing single non numerical characters in addition to the numeric values.
 
-// 🆕 There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type.  The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version.  If XC is a std::pair and T is some non pair type.  Then a two argument constructor for T is called to assign the value.  For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type
+// There is a template overload which takes two template parameters the first is the type of object to assign the value to, the second is the conversion type.  The conversion type should have a known way to convert from a string, such as any of the types that work in the non-template version.  If XC is a std::pair and T is some non pair type.  Then a two argument constructor for T is called to assign the value.  For tuples or other multi element types, XC must be a single type or a tuple like object of the same size as the assignment type
 app.add_option<typename T, typename XC>(option_name,
                T &output, // output must be assignable or constructible from a value of type XC
                help_string="")
@@ -245,7 +261,7 @@ app.add_flag(option_name,
              help_string="")
 
 app.add_flag(option_name,
-             variable_to_bind_to, // bool, int, float, complex, containers, enum, std::atomic 🚧, or string-like, or any singular object with a defined conversion from a string like add_option
+             variable_to_bind_to, // bool, int, float, complex, containers, enum, std::atomic, or string-like, or any singular object with a defined conversion from a string like add_option
              help_string="")
 
 app.add_flag_function(option_name,
@@ -260,32 +276,38 @@ App* subcom = app.add_subcommand(name, description);
 Option_group *app.add_option_group(name,description);
 ```
 
-An option name must start with a alphabetic character, underscore, a number, '?', or '@'. For long options, after the first character '.', and '-' are also valid characters. For the `add_flag*` functions '{' has special meaning. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on the help line for its positional form.
+An option name may start with any character except ('-', ' ', '\n', and '!') 🆕. For long options, after the first character all characters are allowed except ('=',':','{',' ', '\n')🆕. For the `add_flag*` functions '{' and '!' have special meaning which is why they are not allowed. Names are given as a comma separated string, with the dash or dashes. An option or flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on the help line for its positional form.
 
 The `add_option_function<type>(...` function will typically require the template parameter be given unless a `std::function` object with an exact match is passed.  The type can be any type supported by the `add_option` function. The function should throw an error (`CLI::ConversionError` or `CLI::ValidationError` possibly) if the value is not valid.
 
-🆕 The two parameter template overload can be used in cases where you want to restrict the input such as
-```
+The two parameter template overload can be used in cases where you want to restrict the input such as
+
+```cpp
 double val
 app.add_option<double,unsigned int>("-v",val);
 ```
+
 which would first verify the input is convertible to an `unsigned int` before assigning it.  Or using some variant type
-```
+
+```cpp
 using vtype=std::variant<int, double, std::string>;
  vtype v1;
 app.add_option<vtype,std:string>("--vs",v1);
 app.add_option<vtype,int>("--vi",v1);
 app.add_option<vtype,double>("--vf",v1);
 ```
+
 otherwise the output would default to a string.  The `add_option` can be used with any integral or floating point types, enumerations, or strings.  Or any type that takes an int, double, or std\::string in an assignment operator or constructor.  If an object can take multiple varieties of those, std::string takes precedence, then double then int.    To better control which one is used or to use another type for the underlying conversions use the two parameter template to directly specify the conversion type.
 
 Types such as (std or boost) `optional<int>`, `optional<double>`, and `optional<string>` and any other wrapper types are supported directly. For purposes of CLI11 wrapper types are those which `value_type` definition.  See [CLI11 Advanced Topics/Custom Converters][] for information on how you can add your own converters for additional types.
 
 Vector types can also be used in the two parameter template overload
-```
+
+```cpp
 std::vector<double> v1;
 app.add_option<std::vector<double>,int>("--vs",v1);
 ```
+
 would load a vector of doubles but ensure all values can be represented as integers.
 
 Automatic direct capture of the default string is disabled when using the two parameter template.  Use `set_default_str(...)` or `->default_function(std::string())` to set the default string or capture function directly for these cases.
@@ -308,76 +330,79 @@ The default value can be any value. For example if you wished to define a numeri
 app.add_flag("-1{1},-2{2},-3{3}",result,"numerical flag")
 ```
 
-using any of those flags on the command line will result in the specified number in the output.  Similar things can be done for string values, and enumerations, as long as the default value can be converted to the given type.
-
+Using any of those flags on the command line will result in the specified number in the output.  Similar things can be done for string values, and enumerations, as long as the default value can be converted to the given type.
 
 On a `C++14` compiler, you can pass a callback function directly to `.add_flag`, while in C++11 mode you'll need to use `.add_flag_function` if you want a callback function. The function will be given the number of times the flag was passed. You can throw a relevant `CLI::ParseError` to signal a failure.
 
 #### Example
 
--   `"one,-o,--one"`: Valid as long as not a flag, would create an option that can be specified positionally, or with `-o` or `--one`
--   `"this"` Can only be passed positionally
--   `"-a,-b,-c"` No limit to the number of non-positional option names
+* `"one,-o,--one"`: Valid as long as not a flag, would create an option that can be specified positionally, or with `-o` or `--one`
+* `"this"` Can only be passed positionally
+* `"-a,-b,-c"` No limit to the number of non-positional option names
 
 The add commands return a pointer to an internally stored `Option`.
 This option can be used directly to check for the count (`->count()`) after parsing to avoid a string based lookup.
-⚠️  Deprecated: The `add_*` commands have a final argument than can be set to true, which causes the default value to be captured and printed on the command line with the help flag. Since CLI11 1.8, you can simply add `->capture_default_str()`.
 
 #### Option options
 
 Before parsing, you can set the following options:
 
--   `->required()`: The program will quit if this option is not present. This is `mandatory` in Plumbum, but required options seems to be a more standard term. For compatibility, `->mandatory()` also works.
--   `->expected(N)`: Take `N` values instead of as many as possible, only for vector args. If negative, require at least `-N`; end with `--` or another recognized option or subcommand.
--   `->type_name(typename)`: Set the name of an Option's type (`type_name_fn` allows a function instead)
--   `->type_size(N)`: Set the intrinsic size of an option. The parser will require multiples of this number if negative.
--   `->needs(opt)`: This option requires another option to also be present, opt is an `Option` pointer.
--   `->excludes(opt)`: This option cannot be given with `opt` present, opt is an `Option` pointer.
--   `->envname(name)`: Gets the value from the environment if present and not passed on the command line.
--   `->group(name)`: The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `""` will not show up in the help print (hidden).
--   `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments).
--   `->ignore_underscore()`: Ignore any underscores in the options names (also works on subcommands, does not affect arguments). For example "option_one" will match with "optionone".  This does not apply to short form options since they only have one character
--   `->disable_flag_override()`: From the command line long form flag options can be assigned a value on the command line using the `=` notation `--flag=value`. If this behavior is not desired, the `disable_flag_override()` disables it and will generate an exception if it is done on the command line.  The `=` does not work with short form flag options.
--   `->allow_extra_args(true/false)`: 🚧 If set to true the option will take an unlimited number of arguments like a vector, if false it will limit the number of arguments to the size of the type used in the option.  Default value depends on the nature of the type use, containers default to true, others default to false.
--   `->delimiter(char)`: Allows specification of a custom delimiter for separating single arguments into vector arguments, for example specifying `->delimiter(',')` on an option would result in `--opt=1,2,3` producing 3 elements of a vector and the equivalent of --opt 1 2 3 assuming opt is a vector value.
--   `->description(str)`: Set/change the description.
--   `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option policy. Shortcuts available: `->take_last()`, `->take_first()`,`->take_all()`, and `->join()`. This will only affect options expecting 1 argument or bool flags (which do not inherit their default but always start with a specific policy).
--   `->check(std::string(const std::string &), validator_name="",validator_description="")`: Define a check function.  The function should return a non empty string with the error message if the check fails
--   `->check(Validator)`: Use a Validator object to do the check see [Validators](#validators) for a description of available Validators and how to create new ones.
--   `->transform(std::string(std::string &), validator_name="",validator_description=")`: Converts the input string into the output string, in-place in the parsed options.
--   `->transform(Validator)`: Uses a Validator object to do the transformation see [Validators](#validators) for a description of available Validators and how to create new ones.
--   `->each(void(const std::string &)>`: Run this function on each value received, as it is received. It should throw a `ValidationError` if an error is encountered.
--   `->configurable(false)`: Disable this option from being in a configuration file.
-    `->capture_default_str()`: Store the current value attached and display it in the help string.
--   `->default_function(std::string())`: Advanced: Change the function that `capture_default_str()` uses.
--   `->always_capture_default()`: Always run `capture_default_str()` when creating new options. Only useful on an App's `option_defaults`.
--   `default_str(string)`:  Set the default string directly.  This string will also be used as a default value if no arguments are passed and the value is requested.
--   `default_val(value)`: 🆕 Generate the default string from a value and validate that the value is also valid.  For options that assign directly to a value type the value in that type is also updated.  Value must be convertible to a string(one of known types or have a stream operator).
--   `->option_text(string)`: Sets the text between the option name and description.
-
+* `->required()`: The program will quit if this option is not present. This is `mandatory` in Plumbum, but required options seems to be a more standard term. For compatibility, `->mandatory()` also works.
+* `->expected(N)`: Take `N` values instead of as many as possible, only for vector args. If negative, require at least `-N`; end with `--` or another recognized option or subcommand.
+* `->expected(MIN,MAX)`: Set a range of expected values to accompany an option.  `expected(0,1)` is the equivalent of making a flag.
+* `->type_name(typename)`: Set the name of an Option's type (`type_name_fn` allows a function instead)
+* `->type_size(N)`: Set the intrinsic size of an option value. The parser will require multiples of this number if negative. Most of the time this is detected automatically though can be modified for specific use cases.
+* `->type_size(MIN,MAX)`: Set the intrinsic size of an option to a range.
+* `->needs(opt)`: This option requires another option to also be present, opt is an `Option` pointer. Options can be removed from the `needs` with `remove_needs(opt)`. The option can also be specified with a string containing the name of the option
+* `->excludes(opt)`: This option cannot be given with `opt` present, opt is an `Option` pointer.  Can also be given as a string containing the name of the option.  Options can be removed from the excludes list with `->remove_excludes(opt)`
+* `->envname(name)`: Gets the value from the environment if present and not passed on the command line.
+* `->group(name)`: The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `""` will not show up in the help print (hidden).
+* `->ignore_case()`: Ignore the case on the command line (also works on subcommands, does not affect arguments).
+* `->ignore_underscore()`: Ignore any underscores in the options names (also works on subcommands, does not affect arguments). For example "option_one" will match with "optionone".  This does not apply to short form options since they only have one character
+* `->disable_flag_override()`: From the command line long form flag options can be assigned a value on the command line using the `=` notation `--flag=value`. If this behavior is not desired, the `disable_flag_override()` disables it and will generate an exception if it is done on the command line.  The `=` does not work with short form flag options.
+* `->allow_extra_args(true/false)`: If set to true the option will take an unlimited number of arguments like a vector, if false it will limit the number of arguments to the size of the type used in the option.  Default value depends on the nature of the type use, containers default to true, others default to false.
+* `->delimiter(char)`: Allows specification of a custom delimiter for separating single arguments into vector arguments, for example specifying `->delimiter(',')` on an option would result in `--opt=1,2,3` producing 3 elements of a vector and the equivalent of --opt 1 2 3 assuming opt is a vector value.
+* `->description(str)`: Set/change the description.
+* `->multi_option_policy(CLI::MultiOptionPolicy::Throw)`: Set the multi-option policy. Shortcuts available: `->take_last()`, `->take_first()`,`->take_all()`, and `->join()`. This will only affect options expecting 1 argument or bool flags (which do not inherit their default but always start with a specific policy). `->join(delim)` can also be used to join with a specific delimiter. This equivalent to calling `->delimiter(delim)` and `->join()`
+* `->check(std::string(const std::string &), validator_name="",validator_description="")`: Define a check function.  The function should return a non empty string with the error message if the check fails
+* `->check(Validator)`: Use a Validator object to do the check see [Validators](#validators) for a description of available Validators and how to create new ones.
+* `->transform(std::string(std::string &), validator_name="",validator_description=")`: Converts the input string into the output string, in-place in the parsed options.
+* `->transform(Validator)`: Uses a Validator object to do the transformation see [Validators](#validators) for a description of available Validators and how to create new ones.
+* `->each(void(const std::string &)>`: Run this function on each value received, as it is received. It should throw a `ValidationError` if an error is encountered.
+* `->configurable(false)`: Disable this option from being in a configuration file.
+* `->capture_default_str()`: Store the current value attached and display it in the help string.
+* `->default_function(std::string())`: Advanced: Change the function that `capture_default_str()` uses.
+* `->always_capture_default()`: Always run `capture_default_str()` when creating new options. Only useful on an App's `option_defaults`.
+* `->default_str(string)`:  Set the default string directly (NO VALIDATION OR CALLBACKS).  This string will also be used as a default value if no arguments are passed and the value is requested.
+* `->default_val(value)`: Generate the default string from a value and validate that the value is also valid.  For options that assign directly to a value type the value in that type is also updated.  Value must be convertible to a string(one of known types or have a stream operator). The callback may be triggered if the `run_callback_for_default` is set.
+* `->run_callback_for_default()`: This will force the option callback to be executed or the variable set when the `default_val` is set.
+* `->option_text(string)`: Sets the text between the option name and description.
+* `->force_callback()`: 🆕 Causes the option callback or value set to be triggered even if the option was not present in parsing.
+* `->trigger_on_parse()`: 🆕 If set, causes the callback and all associated validation checks for the option to be executed when the option value is parsed vs. at the end of all parsing. This could cause the callback to be executed multiple times.
 
 These options return the `Option` pointer, so you can chain them together, and even skip storing the pointer entirely. The `each` function takes any function that has the signature `void(const std::string&)`; it should throw a `ValidationError` when validation fails. The help message will have the name of the parent option prepended. Since `each`, `check` and `transform` use the same underlying mechanism, you can chain as many as you want, and they will be executed in order. Operations added through `transform` are executed first in reverse order of addition, and `check` and `each` are run following the transform functions in order of addition. If you just want to see the unconverted values, use `.results()` to get the `std::vector<std::string>` of results.
 
 On the command line, options can be given as:
 
--   `-a` (flag)
--   `-abc` (flags can be combined)
--   `-f filename` (option)
--   `-ffilename` (no space required)
--   `-abcf filename` (flags and option can be combined)
--   `--long` (long flag)
--   `--long_flag=true` (long flag with equals to override default value)
--   `--file filename` (space)
--   `--file=filename` (equals)
+* `-a` (flag)
+* `-abc` (flags can be combined)
+* `-f filename` (option)
+* `-ffilename` (no space required)
+* `-abcf filename` (flags and option can be combined)
+* `--long` (long flag)
+* `--long_flag=true` (long flag with equals to override default value)
+* `--file filename` (space)
+* `--file=filename` (equals)
 
 If `allow_windows_style_options()` is specified in the application or subcommand options can also be given as:
--   `/a` (flag)
--   `/f filename` (option)
--   `/long` (long flag)
--   `/file filename` (space)
--   `/file:filename` (colon)
--   `/long_flag:false` (long flag with : to override the default value)
-=  Windows style options do not allow combining short options or values not separated from the short option like with `-` options
+
+* `/a` (flag)
+* `/f filename` (option)
+* `/long` (long flag)
+* `/file filename` (space)
+* `/file:filename` (colon)
+* `/long_flag:false` (long flag with : to override the default value)
+  * Windows style options do not allow combining short options or values not separated from the short option like with `-` options
 
 Long flag options may be given with an `=<value>` to allow specifying a false value, or some other value to the flag. See [config files](#configuration-file) for details on the values supported.  NOTE: only the `=` or `:` for windows-style options may be used for this, using a space will result in the argument being interpreted as a positional argument.  This syntax can override the default values, and can be disabled by using `disable_flag_override()`.
 
@@ -394,22 +419,22 @@ Validators are structures to check or modify inputs, they can be used to verify
 
 CLI11 has several Validators built-in that perform some common checks
 
--   `CLI::IsMember(...)`: Require an option be a member of a given set. See [Transforming Validators](#transforming-validators) for more details.
--   `CLI::Transformer(...)`: Modify the input using a map. See [Transforming Validators](#transforming-validators) for more details.
--   `CLI::CheckedTransformer(...)`: Modify the input using a map, and require that the input is either in the set or already one of the outputs of the set. See [Transforming Validators](#transforming-validators) for more details.
--   `CLI::AsNumberWithUnit(...)`: Modify the `<NUMBER> <UNIT>` pair by matching the unit and multiplying the number by the corresponding factor. It can be used as a base for transformers, that accept things like size values (`1 KB`) or durations (`0.33 ms`).
--   `CLI::AsSizeValue(...)`: Convert inputs like `100b`, `42 KB`, `101 Mb`, `11 Mib` to absolute values. `KB` can be configured to be interpreted as 10^3 or 2^10.
--   `CLI::ExistingFile`: Requires that the file exists if given.
--   `CLI::ExistingDirectory`: Requires that the directory exists.
--   `CLI::ExistingPath`: Requires that the path (file or directory) exists.
--   `CLI::NonexistentPath`: Requires that the path does not exist.
--   `CLI::Range(min,max)`: Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0.
--   `CLI::Bounded(min,max)`: Modify the input such that it is always between min and max (make sure to use floating point if needed). Min defaults to 0.  Will produce an error if conversion is not possible.
--   `CLI::PositiveNumber`: Requires the number be greater than 0
--   `CLI::NonNegativeNumber`: Requires the number be greater or equal to 0
--   `CLI::Number`: Requires the input be a number.
--   `CLI::ValidIPV4`: Requires that the option be a valid IPv4 string e.g. `'255.255.255.255'`, `'10.1.1.7'`.
--   `CLI::TypeValidator<TYPE>`:🚧 Requires that the option be convertible to the specified type e.g.  `CLI::TypeValidator<unsigned int>()` would require that the input be convertible to an `unsigned int` regardless of the end conversion.
+* `CLI::IsMember(...)`: Require an option be a member of a given set. See [Transforming Validators](#transforming-validators) for more details.
+* `CLI::Transformer(...)`: Modify the input using a map. See [Transforming Validators](#transforming-validators) for more details.
+* `CLI::CheckedTransformer(...)`: Modify the input using a map, and require that the input is either in the set or already one of the outputs of the set. See [Transforming Validators](#transforming-validators) for more details.
+* `CLI::AsNumberWithUnit(...)`: Modify the `<NUMBER> <UNIT>` pair by matching the unit and multiplying the number by the corresponding factor. It can be used as a base for transformers, that accept things like size values (`1 KB`) or durations (`0.33 ms`).
+* `CLI::AsSizeValue(...)`: Convert inputs like `100b`, `42 KB`, `101 Mb`, `11 Mib` to absolute values. `KB` can be configured to be interpreted as 10^3 or 2^10.
+* `CLI::ExistingFile`: Requires that the file exists if given.
+* `CLI::ExistingDirectory`: Requires that the directory exists.
+* `CLI::ExistingPath`: Requires that the path (file or directory) exists.
+* `CLI::NonexistentPath`: Requires that the path does not exist.
+* `CLI::Range(min,max)`: Requires that the option be between min and max (make sure to use floating point if needed). Min defaults to 0.
+* `CLI::Bounded(min,max)`: Modify the input such that it is always between min and max (make sure to use floating point if needed). Min defaults to 0.  Will produce an error if conversion is not possible.
+* `CLI::PositiveNumber`: Requires the number be greater than 0
+* `CLI::NonNegativeNumber`: Requires the number be greater or equal to 0
+* `CLI::Number`: Requires the input be a number.
+* `CLI::ValidIPV4`: Requires that the option be a valid IPv4 string e.g. `'255.255.255.255'`, `'10.1.1.7'`.
+* `CLI::TypeValidator<TYPE>`:Requires that the option be convertible to the specified type e.g.  `CLI::TypeValidator<unsigned int>()` would require that the input be convertible to an `unsigned int` regardless of the end conversion.
 
 These Validators can be used by simply passing the name into the `check` or `transform` methods on an option
 
@@ -433,36 +458,39 @@ will produce a check to ensure a value is between 0 and 10 or 20 and 30.
 will produce a check for a number less than or equal to 0.
 
 ##### Transforming Validators
+
 There are a few built in Validators that let you transform values if used with the `transform` function.  If they also do some checks then they can be used `check` but some may do nothing in that case.
--   `CLI::Bounded(min,max)` will bound values between min and max and values outside of that range are limited to min or max,  it will fail if the value cannot be converted and produce a `ValidationError`
--   The `IsMember` Validator lets you specify a set of predefined options. You can pass any container or copyable pointer (including `std::shared_ptr`) to a container to this Validator; the container just needs to be iterable and have a `::value_type`. The key type should be convertible from a string,  You can use an initializer list directly if you like. If you need to modify the set later, the pointer form lets you do that; the type message and check will correctly refer to the current version of the set.  The container passed in can be a set, vector, or a map like structure. If used in the `transform` method the output value will be the matching key as it could be modified by filters.
-After specifying a set of options, you can also specify "filter" functions of the form `T(T)`, where `T` is the type of the values. The most common choices probably will be `CLI::ignore_case` an `CLI::ignore_underscore`, and `CLI::ignore_space`.  These all work on strings but it is possible to define functions that work on other types.
-Here are some examples
-of `IsMember`:
-
--   `CLI::IsMember({"choice1", "choice2"})`: Select from exact match to choices.
--   `CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore)`: Match things like `Choice_1`, too.
--   `CLI::IsMember(std::set<int>({2,3,4}))`: Most containers and types work; you just need `std::begin`, `std::end`, and `::value_type`.
--   `CLI::IsMember(std::map<std::string, TYPE>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched value with the matched key.  The value member of the map is not used in `IsMember`, so it can be any type.
--   `auto p = std::make_shared<std::vector<std::string>>(std::initializer_list<std::string>("one", "two")); CLI::IsMember(p)`: You can modify `p` later.
--   The `Transformer` and `CheckedTransformer` Validators transform one value into another. Any container or copyable pointer (including `std::shared_ptr`) to a container that generates pairs of values can be passed to these `Validator's`; the container just needs to be iterable and have a `::value_type` that consists of pairs. The key type should be convertible from a string, and the value type should be convertible to a string  You can use an initializer list directly if you like. If you need to modify the map later, the pointer form lets you do that; the description message will correctly refer to the current version of the map.  `Transformer` does not do any checking so values not in the map are ignored.  `CheckedTransformer` takes an extra step of verifying that the value is either one of the map key values, in which case it is transformed, or one of the expected output values, and if not will generate a `ValidationError`.  A Transformer placed using `check` will not do anything.
+
+* `CLI::Bounded(min,max)` will bound values between min and max and values outside of that range are limited to min or max,  it will fail if the value cannot be converted and produce a `ValidationError`
+* The `IsMember` Validator lets you specify a set of predefined options. You can pass any container or copyable pointer (including `std::shared_ptr`) to a container to this Validator; the container just needs to be iterable and have a `::value_type`. The key type should be convertible from a string,  You can use an initializer list directly if you like. If you need to modify the set later, the pointer form lets you do that; the type message and check will correctly refer to the current version of the set.  The container passed in can be a set, vector, or a map like structure. If used in the `transform` method the output value will be the matching key as it could be modified by filters.
+
+After specifying a set of options, you can also specify "filter" functions of the form `T(T)`, where `T` is the type of the values. The most common choices probably will be `CLI::ignore_case` an `CLI::ignore_underscore`, and `CLI::ignore_space`.  These all work on strings but it is possible to define functions that work on other types. Here are some examples of `IsMember`:
+
+* `CLI::IsMember({"choice1", "choice2"})`: Select from exact match to choices.
+* `CLI::IsMember({"choice1", "choice2"}, CLI::ignore_case, CLI::ignore_underscore)`: Match things like `Choice_1`, too.
+* `CLI::IsMember(std::set<int>({2,3,4}))`: Most containers and types work; you just need `std::begin`, `std::end`, and `::value_type`.
+* `CLI::IsMember(std::map<std::string, TYPE>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched value with the matched key.  The value member of the map is not used in `IsMember`, so it can be any type.
+* `auto p = std::make_shared<std::vector<std::string>>(std::initializer_list<std::string>("one", "two")); CLI::IsMember(p)`: You can modify `p` later.
+* The `Transformer` and `CheckedTransformer` Validators transform one value into another. Any container or copyable pointer (including `std::shared_ptr`) to a container that generates pairs of values can be passed to these `Validator's`; the container just needs to be iterable and have a `::value_type` that consists of pairs. The key type should be convertible from a string, and the value type should be convertible to a string  You can use an initializer list directly if you like. If you need to modify the map later, the pointer form lets you do that; the description message will correctly refer to the current version of the map.  `Transformer` does not do any checking so values not in the map are ignored.  `CheckedTransformer` takes an extra step of verifying that the value is either one of the map key values, in which case it is transformed, or one of the expected output values, and if not will generate a `ValidationError`.  A Transformer placed using `check` will not do anything.
+
 After specifying a map of options, you can also specify "filter" just like in `CLI::IsMember`.
 Here are some examples (`Transformer` and `CheckedTransformer` are interchangeable in the examples)
 of `Transformer`:
 
--   `CLI::Transformer({{"key1", "map1"},{"key2","map2"}})`: Select from key values and produce map values.
-
--   `CLI::Transformer(std::map<std::string,int>({"two",2},{"three",3},{"four",4}}))`: most maplike containers work,  the `::value_type` needs to produce a pair of some kind.
--   `CLI::CheckedTransformer(std::map<std::string, int>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched key with the value.  `CheckedTransformer` also requires that the value either match one of the keys or match one of known outputs.
--   `auto p = std::make_shared<CLI::TransformPairs<std::string>>(std::initializer_list<std::pair<std::string,std::string>>({"key1", "map1"},{"key2","map2"})); CLI::Transformer(p)`: You can modify `p` later. `TransformPairs<T>` is an alias for `std::vector<std::pair<<std::string,T>>`
+* `CLI::Transformer({{"key1", "map1"},{"key2","map2"}})`: Select from key values and produce map values.
+* `CLI::Transformer(std::map<std::string,int>({"two",2},{"three",3},{"four",4}}))`: most maplike containers work,  the `::value_type` needs to produce a pair of some kind.
+* `CLI::CheckedTransformer(std::map<std::string, int>({{"one", 1}, {"two", 2}}))`: You can use maps; in `->transform()` these replace the matched key with the value.  `CheckedTransformer` also requires that the value either match one of the keys or match one of known outputs.
+* `auto p = std::make_shared<CLI::TransformPairs<std::string>>(std::initializer_list<std::pair<std::string,std::string>>({"key1", "map1"},{"key2","map2"})); CLI::Transformer(p)`: You can modify `p` later. `TransformPairs<T>` is an alias for `std::vector<std::pair<<std::string,T>>`
 
 NOTES:  If the container used in `IsMember`, `Transformer`, or `CheckedTransformer` has a `find` function like `std::unordered_map`  or `std::map` then that function is used to do the searching. If it does not have a `find` function a linear search is performed.  If there are filters present, the fast search is performed first, and if that fails a linear search with the filters on the key values is performed.
 
 ##### Validator operations
+
 Validators are copyable and have a few operations that can be performed on them to alter settings.  Most of the built in Validators have a default description that is displayed in the help.  This can be altered via `.description(validator_description)`.
 The name of a Validator, which is useful for later reference from the `get_validator(name)` method of an `Option` can be set via `.name(validator_name)`
 The operation function of a Validator can be set via
-`.operation(std::function<std::string(std::string &>)`.  The `.active()` function can activate or deactivate a Validator from the operation.  A validator can be set to apply only to a specific element of the output.  For example in a pair option `std::pair<int, std::string>` the first element may need to be a positive integer while the second may need to be a valid file.  The `.application_index(int)` 🆕 function can specify this.  It is zero based and negative indices apply to all values.
+`.operation(std::function<std::string(std::string &>)`.  The `.active()` function can activate or deactivate a Validator from the operation.  A validator can be set to apply only to a specific element of the output.  For example in a pair option `std::pair<int, std::string>` the first element may need to be a positive integer while the second may need to be a valid file.  The `.application_index(int)` function can specify this.  It is zero based and negative indices apply to all values.
+
 ```cpp
 opt->check(CLI::Validator(CLI::PositiveNumber).application_index(0));
 opt->check(CLI::Validator(CLI::ExistingFile).application_index(1));
@@ -507,27 +535,28 @@ opt->get_validator(name);
 
 This will retrieve a Validator with the given name or throw a `CLI::OptionNotFound` error.  If no name is given or name is empty the first unnamed Validator will be returned or the first Validator if there is only one.
 
-or 🆕
+or
 
 ```cpp
 opt->get_validator(index);
 ```
 
 Which will return a validator in the index it is applied which isn't necessarily the order in which was defined.  The pointer can be `nullptr` if an invalid index is given.
-Validators have a few functions to query the current values
--   `get_description()`: Will return a description string
--   `get_name()`: Will return the Validator name
--   `get_active()`: Will return the current active state, true if the Validator is active.
--   `get_application_index()`: 🆕 Will return the current application index.
--   `get_modifying()`: Will return true if the Validator is allowed to modify the input, this can be controlled via the `non_modifying()` method, though it is recommended to let `check` and `transform` option methods manipulate it if needed.
+Validators have a few functions to query the current values:
+
+* `get_description()`: Will return a description string
+* `get_name()`: Will return the Validator name
+* `get_active()`: Will return the current active state, true if the Validator is active.
+* `get_application_index()`: Will return the current application index.
+* `get_modifying()`: Will return true if the Validator is allowed to modify the input, this can be controlled via the `non_modifying()` method, though it is recommended to let `check` and `transform` option methods manipulate it if needed.
 
 #### Getting results
 
 In most cases, the fastest and easiest way is to return the results through a callback or variable specified in one of the `add_*` functions.  But there are situations where this is not possible or desired.  For these cases the results may be obtained through one of the following functions. Please note that these functions will do any type conversions and processing during the call so should not used in performance critical code:
 
--   `results()`: Retrieves a vector of strings with all the results in the order they were given.
--   `results(variable_to_bind_to)`: Gets the results according to the MultiOptionPolicy and converts them just like the `add_option_function` with a variable.
--   `Value=as<type>()`: Returns the result or default value directly as the specified type if possible, can be vector to return all results, and a non-vector to get the result according to the MultiOptionPolicy in place.
+* `->results()`: Retrieves a vector of strings with all the results in the order they were given.
+* `->results(variable_to_bind_to)`: Gets the results according to the MultiOptionPolicy and converts them just like the `add_option_function` with a variable.
+* `Value=opt->as<type>()`: Returns the result or default value directly as the specified type if possible, can be vector to return all results, and a non-vector to get the result according to the MultiOptionPolicy in place.
 
 ### Subcommands
 
@@ -546,72 +575,73 @@ even exit the program through the callback.
 Multiple subcommands are allowed, to allow [`Click`][click] like series of commands (order is preserved).  The same subcommand can be triggered multiple times but all positional arguments will take precedence over the second and future calls of the subcommand.  `->count()` on the subcommand will return the number of times the subcommand was called.  The subcommand callback will only be triggered once unless the `.immediate_callback()`  flag is set or the callback is specified through the `parse_complete_callback()` function. The `final_callback()` is triggered only once.  In which case the callback executes on completion of the subcommand arguments but after the arguments for that subcommand have been parsed, and can be triggered multiple times.
 
 Subcommands may also have an empty name either by calling `add_subcommand` with an empty string for the name or with no arguments.
-Nameless subcommands function a similarly to groups in the main `App`. See [Option groups](#option-groups) to see how this might work.  If an option is not defined in the main App, all nameless subcommands are checked as well.  This allows for the options to be defined in a composable group.  The `add_subcommand` function has an overload for adding a `shared_ptr<App>` so the subcommand(s) could be defined in different components and merged into a main `App`, or possibly multiple `Apps`.  Multiple nameless subcommands are allowed.  Callbacks for nameless subcommands are only triggered if any options from the subcommand were parsed.
+Nameless subcommands function a similarly to groups in the main `App`. See [Option groups](#option-groups) to see how this might work.  If an option is not defined in the main App, all nameless subcommands are checked as well.  This allows for the options to be defined in a composable group.  The `add_subcommand` function has an overload for adding a `shared_ptr<App>` so the subcommand(s) could be defined in different components and merged into a main `App`, or possibly multiple `Apps`.  Multiple nameless subcommands are allowed.  Callbacks for nameless subcommands are only triggered if any options from the subcommand were parsed. Subcommand names given through the `add_subcommand` method have the same restrictions as option names.
 
 #### Subcommand options
 
 There are several options that are supported on the main app and subcommands and option_groups. These are:
 
--   `.ignore_case()`: Ignore the case of this subcommand. Inherited by added subcommands, so is usually used on the main `App`.
--   `.ignore_underscore()`: Ignore any underscores in the subcommand name. Inherited by added subcommands, so is usually used on the main `App`.
--   `.allow_windows_style_options()`: Allow command line options to be parsed in the form of `/s /long /file:file_name.ext`  This option does not change how options are specified in the `add_option` calls or the ability to process options in the form of `-s --long --file=file_name.ext`.
--   `.fallthrough()`: Allow extra unmatched options and positionals to "fall through" and be matched on a parent option. Subcommands always are allowed to "fall through" as in they will first attempt to match on the current subcommand and if they fail will progressively check parents for matching subcommands.
--   `.configurable()`: 🆕 Allow the subcommand to be triggered from a configuration file. By default subcommand options in a configuration file do not trigger a subcommand but will just update default values.
--   `.disable()`: Specify that the subcommand is disabled, if given with a bool value it will enable or disable the subcommand or option group.
--   `.disabled_by_default()`: Specify that at the start of parsing the subcommand/option_group should be disabled. This is useful for allowing some Subcommands to trigger others.
--   `.enabled_by_default()`: Specify that at the start of each parse the subcommand/option_group should be enabled.  This is useful for allowing some Subcommands to disable others.
--   `.silent()`: 🚧 Specify that the subcommand is silent meaning that if used it won't show up in the subcommand list.  This allows the use of subcommands as modifiers
--   `.validate_positionals()`: Specify that positionals should pass validation before matching.  Validation is specified through `transform`, `check`, and `each` for an option.  If an argument fails validation it is not an error and matching proceeds to the next available positional or extra arguments.
--   `.excludes(option_or_subcommand)`: If given an option pointer or pointer to another subcommand, these subcommands cannot be given together.  In the case of options, if the option is passed the subcommand cannot be used and will generate an error.
--   `.needs(option_or_subcommand)`: 🆕 If given an option pointer or pointer to another subcommand, the subcommands will require the given option to have been given before this subcommand is validated which occurs prior to execution of any callback or after parsing is completed.
--   `.require_option()`: Require 1 or more options or option groups be used.
--   `.require_option(N)`: Require `N` options or option groups, if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more.
--   `.require_option(min, max)`: Explicitly set min and max allowed options or option groups. Setting `max` to 0 implies unlimited options.
--   `.require_subcommand()`: Require 1 or more subcommands.
--   `.require_subcommand(N)`: Require `N` subcommands if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more.
--   `.require_subcommand(min, max)`: Explicitly set min and max allowed subcommands. Setting `max` to 0 is unlimited.
--   `.add_subcommand(name="", description="")`: Add a subcommand, returns a pointer to the internally stored subcommand.
--   `.add_subcommand(shared_ptr<App>)`: Add a subcommand by shared_ptr, returns a pointer to the internally stored subcommand.
--   `.remove_subcommand(App)`: Remove a subcommand from the app or subcommand.
--   `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line.
--   `.get_subcommands(filter)`: The list of subcommands that match a particular filter function.
--   `.add_option_group(name="", description="")`: Add an [option group](#option-groups) to an App,  an option group is specialized subcommand intended for containing groups of options or other groups for controlling how options interact.
--   `.get_parent()`: Get the parent App or `nullptr` if called on master App.
--   `.get_option(name)`: Get an option pointer by option name will throw if the specified option is not available,  nameless subcommands are also searched
--   `.get_option_no_throw(name)`: Get an option pointer by option name. This function will return a `nullptr` instead of throwing if the option is not available.
--   `.get_options(filter)`: Get the list of all defined option pointers (useful for processing the app for custom output formats).
--   `.parse_order()`: Get the list of option pointers in the order they were parsed (including duplicates).
--   `.formatter(fmt)`: Set a formatter, with signature `std::string(const App*, std::string, AppFormatMode)`. See Formatting for more details.
--   `.description(str)`: Set/change the description.
--   `.get_description()`: Access the description.
--   `.alias(str)`: 🆕 set an alias for the subcommand, this allows subcommands to be called by more than one name.
--   `.parsed()`: True if this subcommand was given on the command line.
--   `.count()`: Returns the number of times the subcommand was called.
--   `.count(option_name)`: Returns the number of times a particular option was called.
--   `.count_all()`: Returns the total number of arguments a particular subcommand processed, on the master App it returns the total number of processed commands.
--   `.name(name)`: Add or change the name.
--   `.callback(void() function)`: Set the callback for an app. 🆕 either sets the pre_parse_callback or the final_callback depending on the value of `immediate_callback`. See [Subcommand callbacks](#callbacks) for some additional details.
--   `.parse_complete_callback(void() function)`: 🆕 Set the callback that runs at the completion of parsing. for subcommands this is executed at the completion of the single subcommand and can be executed multiple times. See [Subcommand callbacks](#callbacks) for some additional details.
--   `.final_callback(void() function)`: 🆕 Set the callback that runs at the end of all processing. This is the last thing that is executed before returning. See [Subcommand callbacks](#callbacks) for some additional details.
--   `.immediate_callback()`: Specifies whether the callback for a subcommand should be run as a `parse_complete_callback`(true) or `final_callback`(false). When used on the main app 🆕 it will execute the main app callback prior to the callbacks for a subcommand if they do not also have the `immediate_callback` flag set. 🆕 It is preferable to use the `parse_complete_callback` or `final_callback` directly instead of the `callback` and `immediate_callback` if one wishes to control the ordering and timing of callback.  Though `immediate_callback` can be used to swap them if that is needed.
--   `.pre_parse_callback(void(std::size_t) function)`: Set a callback that executes after the first argument of an application is processed.  See [Subcommand callbacks](#callbacks) for some additional details.
--   `.allow_extras()`: Do not throw an error if extra arguments are left over.
--   `.positionals_at_end()`: Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered.
--   `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognized item. It is ideal for allowing your app or subcommand to be a "prefix" to calling another app.
--   `.footer(message)`: Set text to appear at the bottom of the help string.
--   `.footer(std::string())`: 🆕 Set a callback to generate a string that will appear at the end of the help string.
--   `.set_help_flag(name, message)`: Set the help flag name and message, returns a pointer to the created option.
--   `.set_help_all_flag(name, message)`: Set the help all flag name and message, returns a pointer to the created option. Expands subcommands.
--   `.failure_message(func)`: Set the failure message function. Two provided: `CLI::FailureMessage::help` and `CLI::FailureMessage::simple` (the default).
--   `.group(name)`: Set a group name, defaults to `"Subcommands"`. Setting `""` will be hide the subcommand.
--   `[option_name]`: retrieve a const pointer to an option given by `option_name` for Example `app["--flag1"]` will get a pointer to the option for the "--flag1" value,  `app["--flag1"]->as<bool>()` will get the results of the command line for a flag. The operation will throw an exception if the option name is not valid.
+* `.ignore_case()`: Ignore the case of this subcommand. Inherited by added subcommands, so is usually used on the main `App`.
+* `.ignore_underscore()`: Ignore any underscores in the subcommand name. Inherited by added subcommands, so is usually used on the main `App`.
+* `.allow_windows_style_options()`: Allow command line options to be parsed in the form of `/s /long /file:file_name.ext`  This option does not change how options are specified in the `add_option` calls or the ability to process options in the form of `-s --long --file=file_name.ext`.
+* `.fallthrough()`: Allow extra unmatched options and positionals to "fall through" and be matched on a parent option. Subcommands always are allowed to "fall through" as in they will first attempt to match on the current subcommand and if they fail will progressively check parents for matching subcommands.
+* `.configurable()`: Allow the subcommand to be triggered from a configuration file. By default subcommand options in a configuration file do not trigger a subcommand but will just update default values.
+* `.disable()`: Specify that the subcommand is disabled, if given with a bool value it will enable or disable the subcommand or option group.
+* `.disabled_by_default()`: Specify that at the start of parsing the subcommand/option_group should be disabled. This is useful for allowing some Subcommands to trigger others.
+* `.enabled_by_default()`: Specify that at the start of each parse the subcommand/option_group should be enabled.  This is useful for allowing some Subcommands to disable others.
+* `.silent()`: Specify that the subcommand is silent meaning that if used it won't show up in the subcommand list.  This allows the use of subcommands as modifiers
+* `.validate_positionals()`: Specify that positionals should pass validation before matching.  Validation is specified through `transform`, `check`, and `each` for an option.  If an argument fails validation it is not an error and matching proceeds to the next available positional or extra arguments.
+* `.excludes(option_or_subcommand)`: If given an option pointer or pointer to another subcommand, these subcommands cannot be given together.  In the case of options, if the option is passed the subcommand cannot be used and will generate an error.
+* `.needs(option_or_subcommand)`: If given an option pointer or pointer to another subcommand, the subcommands will require the given option to have been given before this subcommand is validated which occurs prior to execution of any callback or after parsing is completed.
+* `.require_option()`: Require 1 or more options or option groups be used.
+* `.require_option(N)`: Require `N` options or option groups, if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more.
+* `.require_option(min, max)`: Explicitly set min and max allowed options or option groups. Setting `max` to 0 implies unlimited options.
+* `.require_subcommand()`: Require 1 or more subcommands.
+* `.require_subcommand(N)`: Require `N` subcommands if `N>0`, or up to `N` if `N<0`. `N=0` resets to the default to 0 or more.
+* `.require_subcommand(min, max)`: Explicitly set min and max allowed subcommands. Setting `max` to 0 is unlimited.
+* `.add_subcommand(name="", description="")`: Add a subcommand, returns a pointer to the internally stored subcommand.
+* `.add_subcommand(shared_ptr<App>)`: Add a subcommand by shared_ptr, returns a pointer to the internally stored subcommand.
+* `.remove_subcommand(App)`: Remove a subcommand from the app or subcommand.
+* `.got_subcommand(App_or_name)`: Check to see if a subcommand was received on the command line.
+* `.get_subcommands(filter)`: The list of subcommands that match a particular filter function.
+* `.add_option_group(name="", description="")`: Add an [option group](#option-groups) to an App,  an option group is specialized subcommand intended for containing groups of options or other groups for controlling how options interact.
+* `.get_parent()`: Get the parent App or `nullptr` if called on main App.
+* `.get_option(name)`: Get an option pointer by option name will throw if the specified option is not available,  nameless subcommands are also searched
+* `.get_option_no_throw(name)`: Get an option pointer by option name. This function will return a `nullptr` instead of throwing if the option is not available.
+* `.get_options(filter)`: Get the list of all defined option pointers (useful for processing the app for custom output formats).
+* `.parse_order()`: Get the list of option pointers in the order they were parsed (including duplicates).
+* `.formatter(fmt)`: Set a formatter, with signature `std::string(const App*, std::string, AppFormatMode)`. See Formatting for more details.
+* `.description(str)`: Set/change the description.
+* `.get_description()`: Access the description.
+* `.alias(str)`: set an alias for the subcommand, this allows subcommands to be called by more than one name.
+* `.parsed()`: True if this subcommand was given on the command line.
+* `.count()`: Returns the number of times the subcommand was called.
+* `.count(option_name)`: Returns the number of times a particular option was called.
+* `.count_all()`: Returns the total number of arguments a particular subcommand processed, on the main App it returns the total number of processed commands.
+* `.name(name)`: Add or change the name.
+* `.callback(void() function)`: Set the callback for an app. Either sets the `pre_parse_callback` or the `final_callback` depending on the value of `immediate_callback`. See [Subcommand callbacks](#callbacks) for some additional details.
+* `.parse_complete_callback(void() function)`: Set the callback that runs at the completion of parsing. For subcommands this is executed at the completion of the single subcommand and can be executed multiple times. See [Subcommand callbacks](#callbacks) for some additional details.
+* `.final_callback(void() function)`: Set the callback that runs at the end of all processing. This is the last thing that is executed before returning. See [Subcommand callbacks](#callbacks) for some additional details.
+* `.immediate_callback()`: Specifies whether the callback for a subcommand should be run as a `parse_complete_callback`(true) or `final_callback`(false). When used on the main app it will execute the main app callback prior to the callbacks for a subcommand if they do not also have the `immediate_callback` flag set. It is preferable to use the `parse_complete_callback` or `final_callback` directly instead of the `callback` and `immediate_callback` if one wishes to control the ordering and timing of callback.  Though `immediate_callback` can be used to swap them if that is needed.
+* `.pre_parse_callback(void(std::size_t) function)`: Set a callback that executes after the first argument of an application is processed.  See [Subcommand callbacks](#callbacks) for some additional details.
+* `.allow_extras()`: Do not throw an error if extra arguments are left over.
+* `.positionals_at_end()`: Specify that positional arguments occur as the last arguments and throw an error if an unexpected positional is encountered.
+* `.prefix_command()`: Like `allow_extras`, but stop immediately on the first unrecognized item. It is ideal for allowing your app or subcommand to be a "prefix" to calling another app.
+* `.footer(message)`: Set text to appear at the bottom of the help string.
+* `.footer(std::string())`: Set a callback to generate a string that will appear at the end of the help string.
+* `.set_help_flag(name, message)`: Set the help flag name and message, returns a pointer to the created option.
+* `.set_version_flag(name, versionString or callback, help_message)`: Set the version flag name and version string or callback and optional help message, returns a pointer to the created option.
+* `.set_help_all_flag(name, message)`: Set the help all flag name and message, returns a pointer to the created option. Expands subcommands.
+* `.failure_message(func)`: Set the failure message function. Two provided: `CLI::FailureMessage::help` and `CLI::FailureMessage::simple` (the default).
+* `.group(name)`: Set a group name, defaults to `"Subcommands"`. Setting `""` will be hide the subcommand.
+* `[option_name]`: retrieve a const pointer to an option given by `option_name` for Example `app["--flag1"]` will get a pointer to the option for the "--flag1" value,  `app["--flag1"]->as<bool>()` will get the results of the command line for a flag. The operation will throw an exception if the option name is not valid.
 
 > Note: if you have a fixed number of required positional options, that will match before subcommand names. `{}` is an empty filter function, and any positional argument will match before repeated subcommand names.
 
-
 #### Callbacks
+
 A subcommand has three optional callbacks that are executed at different stages of processing.  The `preparse_callback` is executed once after the first argument of a subcommand or application is processed and gives an argument for the number of remaining arguments to process.  For the main app the first argument is considered the program name,  for subcommands the first argument is the subcommand name.  For Option groups and nameless subcommands the first argument is after the first argument or subcommand is processed from that group.
-The second callback is executed after parsing.  This is known as the `parse_complete_callback`. For subcommands this is executed immediately after parsing and can be executed multiple times if a subcommand is called multiple times.    On the main app this callback is executed after all the `parse_complete_callback`s for the subcommands are executed but prior to any `final_callback` calls in the subcommand or option groups. If the main app or subcommand has a config file, no data from the config file will be reflected in `parse_complete_callback` on named subcommands 🆕.  For `option_group`s the `parse_complete_callback` is executed prior to the `parse_complete_callback` on the main app but after the `config_file` is loaded (if specified).  The 🆕 `final_callback` is executed after all processing is complete.  After the `parse_complete_callback` is executed on the main app, the used subcommand `final_callback` are executed followed by the "final callback" for option groups.  The last thing to execute is the `final_callback` for the `main_app`.
+The second callback is executed after parsing.  This is known as the `parse_complete_callback`. For subcommands this is executed immediately after parsing and can be executed multiple times if a subcommand is called multiple times.    On the main app this callback is executed after all the `parse_complete_callback`s for the subcommands are executed but prior to any `final_callback` calls in the subcommand or option groups. If the main app or subcommand has a config file, no data from the config file will be reflected in `parse_complete_callback` on named subcommands.  For `option_group`s the `parse_complete_callback` is executed prior to the `parse_complete_callback` on the main app but after the `config_file` is loaded (if specified).  The `final_callback` is executed after all processing is complete.  After the `parse_complete_callback` is executed on the main app, the used subcommand `final_callback` are executed followed by the "final callback" for option groups.  The last thing to execute is the `final_callback` for the `main_app`.
 For example say an application was set up like
 
 ```cpp
@@ -622,25 +652,25 @@ auto sub2=app.add_subcommand("sub2")->final_callback(c2)->preparse_callback(pc2)
 app.preparse_callback( pa);
 
 ... A bunch of other options
-
 ```
 
 Then the command line is given as
 
-```
+```bash
 program --opt1 opt1_val  sub1 --sub1opt --sub1optb val sub2 --sub2opt sub1 --sub1opt2 sub2 --sub2opt2 val
 ```
 
--   pa will be called prior to parsing any values with an argument of 13.
--   pc1 will be called immediately after processing the sub1 command with a value of 10.
--   c1 will be called when the `sub2` command is encountered.
--   pc2 will be called with value of 6 after the sub2 command is encountered.
--   c1 will be called again after the second sub2 command is encountered.
--   ac1 will be called after processing of all arguments
--   c2 will be called once after processing all arguments.
--   ac2 will be called last after completing  all lower level callbacks have been executed.
+* `pa` will be called prior to parsing any values with an argument of 13.
+* `pc1` will be called immediately after processing the `sub1` command with a value of 10.
+* `c1` will be called when the `sub2` command is encountered.
+* `pc2` will be called with value of 6 after the `sub2` command is encountered.
+* `c1` will be called again after the second `sub2` command is encountered.
+* `ac1` will be called after processing of all arguments
+* `c2` will be called once after processing all arguments.
+* `ac2` will be called last after completing  all lower level callbacks have been executed.
 
 A subcommand is considered terminated when one of the following conditions are met.
+
 1. There are no more arguments to process
 2. Another subcommand is encountered that would not fit in an optional slot of the subcommand
 3. The `positional_mark` (`--`) is encountered and there are no available positional slots in the subcommand.
@@ -648,8 +678,6 @@ A subcommand is considered terminated when one of the following conditions are m
 
 Prior to executed a `parse_complete_callback` all contained options are processed before the callback is triggered.  If a subcommand with a `parse_complete_callback` is called again, then the contained options are reset, and can be triggered again.
 
-
-
 #### Option groups
 
 The subcommand method
@@ -658,7 +686,7 @@ The subcommand method
 .add_option_group(name,description)
 ```
 
-Will create an option group, and return a pointer to it. The argument for `description` is optional and can be omitted.  An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements.  They allow specific sets of options to be composed and controlled as a collective.  For an example see [range example](https://github.com/CLIUtils/CLI11/blob/master/examples/ranges.cpp).  Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App or subcommand also work on option groups.  Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through
+Will create an option group, and return a pointer to it. The argument for `description` is optional and can be omitted.  An option group allows creation of a collection of options, similar to the groups function on options, but with additional controls and requirements.  They allow specific sets of options to be composed and controlled as a collective.  For an example see [range example](https://github.com/CLIUtils/CLI11/blob/main/examples/ranges.cpp).  Option groups are a specialization of an App so all [functions](#subcommand-options) that work with an App or subcommand also work on option groups.  Options can be created as part of an option group using the add functions just like a subcommand, or previously created options can be added through.  The name given in an option group must not contain newlines or null characters.🆕
 
 ```cpp
 ogroup->add_option(option_pointer);
@@ -686,17 +714,20 @@ CLI::TriggerOff(group2_pointer, disabled_group);
 
 These functions make use of `preparse_callback`, `enabled_by_default()` and `disabled_by_default`.  The triggered group may be a vector of group pointers.  These methods should only be used once per group and will override any previous use of the underlying functions.  More complex arrangements can be accomplished using similar methodology with a custom `preparse_callback` function that does more.
 
-Additional helper functions `deprecate_option` 🆕 and `retire_option` 🆕 are available to deprecate or retire options
+Additional helper functions `deprecate_option` and `retire_option` are available to deprecate or retire options
+
 ```cpp
 CLI::deprecate_option(option *, replacement_name="");
 CLI::deprecate_option(App,option_name,replacement_name="");
 ```
+
 will specify that the option is deprecated which will display a message in the help and a warning on first usage.  Deprecated options function normally but will add a message in the help and display a warning on first use.
 
 ```cpp
 CLI::retire_option(App,option *);
 CLI::retire_option(App,option_name);
 ```
+
 will create an option that does nothing by default and will display a warning on first usage that the option is retired and has no effect.  If the option exists it is replaces with a dummy option that takes the same arguments.
 
 If an empty string is passed the option group name the entire group will be hidden in the help results.  For example.
@@ -704,6 +735,7 @@ If an empty string is passed the option group name the entire group will be hidd
 ```cpp
 auto hidden_group=app.add_option_group("");
 ```
+
 will create a group such that no options in that group are displayed in the help string.
 
 ### Configuration file
@@ -715,7 +747,7 @@ app.set_config(option_name="",
                required=false)
 ```
 
-If this is called with no arguments, it will remove the configuration file option (like `set_help_flag`). Setting a configuration option is special. If it is present, it will be read along with the normal command line arguments. The file will be read if it exists, and does not throw an error unless `required` is `true`. Configuration files are in [TOML] format by default 🚧, though the default reader can also accept files in INI format as well 🆕. It should be noted that CLI11 does not contain a full TOML parser but can read strings from most TOML file and run them through the CLI11 parser. Other formats can be added by an adept user, some variations are available through customization points in the default formatter. An example of a TOML file 🆕:
+If this is called with no arguments, it will remove the configuration file option (like `set_help_flag`). Setting a configuration option is special. If it is present, it will be read along with the normal command line arguments. The file will be read if it exists, and does not throw an error unless `required` is `true`. Configuration files are in [TOML][] format by default, though the default reader can also accept files in INI format as well. It should be noted that CLI11 does not contain a full TOML parser but can read strings from most TOML file and run them through the CLI11 parser. Other formats can be added by an adept user, some variations are available through customization points in the default formatter. An example of a TOML file:
 
 ```toml
 # Comments are supported, using a #
@@ -731,7 +763,9 @@ str_vector = ["one","two","and three"]
 in_subcommand = Wow
 sub.subcommand = true
 ```
+
 or equivalently in INI format
+
 ```ini
 ; Comments are supported, using a ;
 ; The default section is [default], case insensitive
@@ -747,10 +781,10 @@ in_subcommand = Wow
 sub.subcommand = true
 ```
 
-Spaces before and after the name and argument are ignored. Multiple arguments are separated by spaces. One set of quotes will be removed, preserving spaces (the same way the command line works). Boolean options can be `true`, `on`, `1`, `yes`, `enable`; or `false`, `off`, `0`, `no`, `disable` (case insensitive). Sections (and `.` separated names) are treated as subcommands (note: this does not necessarily mean that subcommand was passed, it just sets the "defaults"). You cannot set positional-only arguments.  🆕 Subcommands can be triggered from configuration files if the `configurable` flag was set on the subcommand.  Then the use of `[subcommand]` notation will trigger a subcommand and cause it to act as if it were on the command line.
+Spaces before and after the name and argument are ignored. Multiple arguments are separated by spaces. One set of quotes will be removed, preserving spaces (the same way the command line works). Boolean options can be `true`, `on`, `1`, `yes`, `enable`; or `false`, `off`, `0`, `no`, `disable` (case insensitive). Sections (and `.` separated names) are treated as subcommands (note: this does not necessarily mean that subcommand was passed, it just sets the "defaults"). You cannot set positional-only arguments.  Subcommands can be triggered from configuration files if the `configurable` flag was set on the subcommand.  Then the use of `[subcommand]` notation will trigger a subcommand and cause it to act as if it were on the command line.
 
 To print a configuration file from the passed
-arguments, use `.config_to_str(default_also=false, write_description=false)`, where `default_also` will also show any defaulted arguments, and `write_description` will include the app and option descriptions.  See [Config files](https://cliutils.github.io/CLI11/book/chapters/config.html) for some additional details.
+arguments, use `.config_to_str(default_also=false, write_description=false)`, where `default_also` will also show any defaulted arguments, and `write_description` will include the app and option descriptions.  See [Config files](https://cliutils.github.io/CLI11/book/chapters/config.html) for some additional details and customization points.
 
 If it is desired that multiple configuration be allowed.  Use
 
@@ -758,7 +792,7 @@ If it is desired that multiple configuration be allowed.  Use
 app.set_config("--config")->expected(1, X);
 ```
 
-Where X is some positive number and will allow up to `X` configuration files to be specified by separate `--config` arguments.
+Where X is some positive number and will allow up to `X` configuration files to be specified by separate `--config` arguments.  Value strings with quote characters in it will be printed with a single quote. All other arguments will use double quote.  Empty strings will use a double quoted argument. Numerical or boolean values are not quoted.
 
 ### Inheriting defaults
 
@@ -788,8 +822,8 @@ The App class was designed allow toolkits to subclass it, to provide preset defa
 but before run behavior, while
 still giving the user freedom to `callback` on the main app.
 
-The most important parse function is `parse(std::vector<std::string>)`, which takes a reversed list of arguments (so that `pop_back` processes the args in the correct order). `get_help_ptr` and `get_config_ptr` give you access to the help/config option pointers. The standard `parse` manually sets the name from the first argument, so it should not be in this vector. You can also use `parse(string, bool)` to split up and parse a string; the optional boolean should be set to true if you are
-including the program name in the string, and false otherwise.
+The most important parse function is `parse(std::vector<std::string>)`, which takes a reversed list of arguments (so that `pop_back` processes the args in the correct order). `get_help_ptr` and `get_config_ptr` give you access to the help/config option pointers. The standard `parse` manually sets the name from the first argument, so it should not be in this vector. You can also use `parse(string, bool)` to split up and parse a single string; the optional boolean should be set to true if you are
+including the program name in the string, and false otherwise.  The program name can contain spaces if it is an existing file,  otherwise can be enclosed in quotes(single quote, double quote or backtick).  Embedded quote characters can be escaped with `\`.
 
 Also, in a related note, the `App` you get a pointer to is stored in the parent `App` in a `shared_ptr`s (similar to `Option`s) and are deleted when the main `App` goes out of scope unless the object has another owner.
 
@@ -800,7 +834,7 @@ Every `add_` option you have seen so far depends on one method that takes a lamb
 
 Other values can be added as long as they support `operator>>` (and defaults can be printed if they support `operator<<`). To add a new type, for example, provide a custom `operator>>` with an `istream` (inside the CLI namespace is fine if you don't want to interfere with an existing `operator>>`).
 
-If you wanted to extend this to support a completely new type, use a lambda or add a specialization of the `lexical_cast` function template in the namespace `CLI::detail` with the type you need to convert to. Some examples of some new parsers for `complex<double>` that support all of the features of a standard `add_options` call are in [one of the tests](./tests/NewParseTest.cpp). A simpler example is shown below:
+If you wanted to extend this to support a completely new type, use a lambda or add a specialization of the `lexical_cast` function template in the namespace of the type you need to convert to. Some examples of some new parsers for `complex<double>` that support all of the features of a standard `add_options` call are in [one of the tests](./tests/NewParseTest.cpp). A simpler example is shown below:
 
 #### Example
 
@@ -871,28 +905,28 @@ The API is [documented here][api-docs]. Also see the [CLI11 tutorial GitBook][gi
 
 Several short examples of different features are included in the repository. A brief description of each is included here
 
- - [callback_passthrough](https://github.com/CLIUtils/CLI11/blob/master/examples/callback_passthrough.cpp): Example of directly passing remaining arguments through to a callback function which generates a CLI11 application based on existing arguments.
- - [digit_args](https://github.com/CLIUtils/CLI11/blob/master/examples/digit_args.cpp):  Based on [Issue #123](https://github.com/CLIUtils/CLI11/issues/123), uses digit flags to pass a value
- - [enum](https://github.com/CLIUtils/CLI11/blob/master/examples/enum.cpp):  Using enumerations in an option, and the use of [CheckedTransformer](#transforming-validators)
- - [enum_ostream](https://github.com/CLIUtils/CLI11/blob/master/examples/enum_ostream.cpp):  In addition to the contents of example enum.cpp, this example shows how a custom ostream operator overrides CLI11's enum streaming.
- - [formatter](https://github.com/CLIUtils/CLI11/blob/master/examples/formatter.cpp): Illustrating usage of a custom formatter
- - [groups](https://github.com/CLIUtils/CLI11/blob/master/examples/groups.cpp):  Example using groups of options for help grouping and a the timer helper class
- - [inter_argument_order](https://github.com/CLIUtils/CLI11/blob/master/examples/inter_argument_order.cpp): An app to practice mixing unlimited arguments, but still recover the original order.
- - [json](https://github.com/CLIUtils/CLI11/blob/master/examples/json.cpp):  Using JSON as a config file parser
- - [modhelp](https://github.com/CLIUtils/CLI11/blob/master/examples/modhelp.cpp):  How to modify the help flag to do something other than default
- - [nested](https://github.com/CLIUtils/CLI11/blob/master/examples/nested.cpp):  Nested subcommands
- - [option_groups](https://github.com/CLIUtils/CLI11/blob/master/examples/option_groups.cpp):  illustrating the use of option groups and a required number of options.
- based on [Issue #88](https://github.com/CLIUtils/CLI11/issues/88) to set interacting groups of options
- - [positional_arity](https://github.com/CLIUtils/CLI11/blob/master/examples/positional_arity.cpp): Illustrating use of `preparse_callback` to handle situations where the number of arguments can determine which should get parsed,  Based on [Issue #166](https://github.com/CLIUtils/CLI11/issues/166)
- - [positional_validation](https://github.com/CLIUtils/CLI11/blob/master/examples/positional_validation.cpp): Example of how positional arguments are validated using the `validate_positional` flag, also based on [Issue #166](https://github.com/CLIUtils/CLI11/issues/166)
- - [prefix_command](https://github.com/CLIUtils/CLI11/blob/master/examples/prefix_command.cpp): illustrating use of the `prefix_command` flag.
- - [ranges](https://github.com/CLIUtils/CLI11/blob/master/examples/ranges.cpp):  App to demonstrate exclusionary option groups based on [Issue #88](https://github.com/CLIUtils/CLI11/issues/88)
- - [shapes](https://github.com/CLIUtils/CLI11/blob/master/examples/shapes.cpp): illustrating how to set up repeated subcommands Based on [gitter discussion](https://gitter.im/CLI11gitter/Lobby?at=5c7af6b965ffa019ea788cd5)
- - [simple](https://github.com/CLIUtils/CLI11/blob/master/examples/simple.cpp): a simple example of how to set up a CLI11 Application with different flags and options
- - [subcom_help](https://github.com/CLIUtils/CLI11/blob/master/examples/subcom_help.cpp): configuring help for subcommands
- - [subcom_partitioned](https://github.com/CLIUtils/CLI11/blob/master/examples/subcom_partitioned.cpp): Example with a timer and subcommands generated separately and added to the main app later.
- - [subcommands](https://github.com/CLIUtils/CLI11/blob/master/examples/subcommands.cpp): Short example of subcommands
- - [validators](https://github.com/CLIUtils/CLI11/blob/master/examples/validators.cpp): Example illustrating use of validators
+* [callback_passthrough](https://github.com/CLIUtils/CLI11/blob/main/examples/callback_passthrough.cpp): Example of directly passing remaining arguments through to a callback function which generates a CLI11 application based on existing arguments.
+* [custom_parse](https://github.com/CLIUtils/CLI11/blob/main/examples/custom_parse.cpp): Based on [Issue #566](https://github.com/CLIUtils/CLI11/issues/566), example of custom parser
+* [digit_args](https://github.com/CLIUtils/CLI11/blob/main/examples/digit_args.cpp): Based on [Issue #123](https://github.com/CLIUtils/CLI11/issues/123), uses digit flags to pass a value
+* [enum](https://github.com/CLIUtils/CLI11/blob/main/examples/enum.cpp): Using enumerations in an option, and the use of [CheckedTransformer](#transforming-validators)
+* [enum_ostream](https://github.com/CLIUtils/CLI11/blob/main/examples/enum_ostream.cpp): In addition to the contents of example enum.cpp, this example shows how a custom ostream operator overrides CLI11's enum streaming.
+* [formatter](https://github.com/CLIUtils/CLI11/blob/main/examples/formatter.cpp): Illustrating usage of a custom formatter
+* [groups](https://github.com/CLIUtils/CLI11/blob/main/examples/groups.cpp): Example using groups of options for help grouping and a the timer helper class
+* [inter_argument_order](https://github.com/CLIUtils/CLI11/blob/main/examples/inter_argument_order.cpp): An app to practice mixing unlimited arguments, but still recover the original order.
+* [json](https://github.com/CLIUtils/CLI11/blob/main/examples/json.cpp): Using JSON as a config file parser
+* [modhelp](https://github.com/CLIUtils/CLI11/blob/main/examples/modhelp.cpp): How to modify the help flag to do something other than default
+* [nested](https://github.com/CLIUtils/CLI11/blob/main/examples/nested.cpp): Nested subcommands
+* [option_groups](https://github.com/CLIUtils/CLI11/blob/main/examples/option_groups.cpp): Illustrating the use of option groups and a required number of options. Based on [Issue #88](https://github.com/CLIUtils/CLI11/issues/88) to set interacting groups of options
+* [positional_arity](https://github.com/CLIUtils/CLI11/blob/main/examples/positional_arity.cpp): Illustrating use of `preparse_callback` to handle situations where the number of arguments can determine which should get parsed,  Based on [Issue #166](https://github.com/CLIUtils/CLI11/issues/166)
+* [positional_validation](https://github.com/CLIUtils/CLI11/blob/main/examples/positional_validation.cpp): Example of how positional arguments are validated using the `validate_positional` flag, also based on [Issue #166](https://github.com/CLIUtils/CLI11/issues/166)
+* [prefix_command](https://github.com/CLIUtils/CLI11/blob/main/examples/prefix_command.cpp): Illustrating use of the `prefix_command` flag.
+* [ranges](https://github.com/CLIUtils/CLI11/blob/main/examples/ranges.cpp): App to demonstrate exclusionary option groups based on [Issue #88](https://github.com/CLIUtils/CLI11/issues/88)
+* [shapes](https://github.com/CLIUtils/CLI11/blob/main/examples/shapes.cpp): Illustrating how to set up repeated subcommands Based on [gitter discussion](https://gitter.im/CLI11gitter/Lobby?at=5c7af6b965ffa019ea788cd5)
+* [simple](https://github.com/CLIUtils/CLI11/blob/main/examples/simple.cpp): A simple example of how to set up a CLI11 Application with different flags and options
+* [subcom_help](https://github.com/CLIUtils/CLI11/blob/main/examples/subcom_help.cpp): Configuring help for subcommands
+* [subcom_partitioned](https://github.com/CLIUtils/CLI11/blob/main/examples/subcom_partitioned.cpp): Example with a timer and subcommands generated separately and added to the main app later.
+* [subcommands](https://github.com/CLIUtils/CLI11/blob/main/examples/subcommands.cpp): Short example of subcommands
+* [validators](https://github.com/CLIUtils/CLI11/blob/main/examples/validators.cpp): Example illustrating use of validators
 
 ## Contribute
 
@@ -901,7 +935,6 @@ This readme roughly follows the [Standard Readme Style][] and includes a mention
 
 This project was created by [Henry Schreiner](https://github.com/henryiii) and major features were added by  [Philip Top](https://github.com/phlptp). Special thanks to all the contributors ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
 
-
 <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
 <!-- prettier-ignore-start -->
 <!-- markdownlint-disable -->
@@ -965,6 +998,12 @@ This project was created by [Henry Schreiner](https://github.com/henryiii) and m
     <td align="center"><a href="https://github.com/ZeeD26"><img src="https://avatars2.githubusercontent.com/u/2487468?v=4" width="100px;" alt=""/><br /><sub><b>Dominik Steinberger</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=ZeeD26" title="Code">💻</a></td>
     <td align="center"><a href="https://github.com/dfleury2"><img src="https://avatars1.githubusercontent.com/u/4805384?v=4" width="100px;" alt=""/><br /><sub><b>D. Fleury</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=dfleury2" title="Code">💻</a></td>
     <td align="center"><a href="https://github.com/dbarowy"><img src="https://avatars3.githubusercontent.com/u/573142?v=4" width="100px;" alt=""/><br /><sub><b>Dan Barowy</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=dbarowy" title="Documentation">📖</a></td>
+    <td align="center"><a href="https://github.com/paddy-hack"><img src="https://avatars.githubusercontent.com/u/6804372?v=4" width="100px;" alt=""/><br /><sub><b>Olaf Meeuwissen</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=paddy-hack" title="Code">💻</a></td>
+    <td align="center"><a href="https://github.com/dryleev"><img src="https://avatars.githubusercontent.com/u/83670813?v=4" width="100px;" alt=""/><br /><sub><b>dryleev</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=dryleev" title="Code">💻</a></td>
+    <td align="center"><a href="https://github.com/AnticliMaxtic"><img src="https://avatars.githubusercontent.com/u/43995389?v=4" width="100px;" alt=""/><br /><sub><b>Max</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=AnticliMaxtic" title="Code">💻</a></td>
+  </tr>
+  <tr>
+    <td align="center"><a href="https://profiles.sussex.ac.uk/p281168-alex-dewar/publications"><img src="https://avatars.githubusercontent.com/u/23149834?v=4" width="100px;" alt=""/><br /><sub><b>Alex Dewar</b></sub></a><br /><a href="https://github.com/CLIUtils/CLI11/commits?author=alexdewar" title="Code">💻</a></td>
   </tr>
 </table>
 
@@ -972,7 +1011,6 @@ This project was created by [Henry Schreiner](https://github.com/henryiii) and m
 <!-- prettier-ignore-end -->
 <!-- ALL-CONTRIBUTORS-LIST:END -->
 
-
 This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
 
 ## License
@@ -983,21 +1021,23 @@ CLI11 was developed at the [University of Cincinnati][] to support of the [GooFi
 
 [doi-badge]: https://zenodo.org/badge/80064252.svg
 [doi-link]: https://zenodo.org/badge/latestdoi/80064252
-[azure-badge]: https://dev.azure.com/CLIUtils/CLI11/_apis/build/status/CLIUtils.CLI11?branchName=master
+[azure-badge]: https://dev.azure.com/CLIUtils/CLI11/_apis/build/status/CLIUtils.CLI11?branchName=main
 [azure]: https://dev.azure.com/CLIUtils/CLI11
-[travis-badge]: https://img.shields.io/travis/CLIUtils/CLI11/master.svg?label=Linux/macOS
-[travis]: https://travis-ci.org/CLIUtils/CLI11
-[appveyor-badge]: https://img.shields.io/appveyor/ci/HenrySchreiner/cli11/master.svg?label=AppVeyor
+[actions-link]: https://github.com/CLIUtils/CLI11/actions
+[actions-badge]: https://github.com/CLIUtils/CLI11/actions/workflows/tests.yml/badge.svg
+[appveyor-badge]: https://ci.appveyor.com/api/projects/status/82niaxpaa28dwbms/branch/main?svg=true
 [appveyor]: https://ci.appveyor.com/project/HenrySchreiner/cli11
-[actions-badge]: https://github.com/CLIUtils/CLI11/workflows/Tests/badge.svg
-[actions-link]:  https://github.com/CLIUtils/CLI11/actions
-[codecov-badge]: https://codecov.io/gh/CLIUtils/CLI11/branch/master/graph/badge.svg
+[repology-badge]: https://repology.org/badge/latest-versions/cli11.svg
+[repology]: https://repology.org/project/cli11/versions
+[codecov-badge]: https://codecov.io/gh/CLIUtils/CLI11/branch/main/graph/badge.svg?token=2O4wfs8NJO
 [codecov]: https://codecov.io/gh/CLIUtils/CLI11
 [gitter-badge]: https://badges.gitter.im/CLI11gitter/Lobby.svg
 [gitter]: https://gitter.im/CLI11gitter/Lobby
 [license-badge]: https://img.shields.io/badge/License-BSD-blue.svg
-[conan-badge]: https://api.bintray.com/packages/cliutils/CLI11/CLI11%3Acliutils/images/download.svg
-[conan-link]: https://bintray.com/cliutils/CLI11/CLI11%3Acliutils/_latestVersion
+[conan-badge]: https://img.shields.io/badge/conan-io-blue
+[conan-link]: https://conan.io/center/cli11
+[conda-badge]: https://img.shields.io/conda/vn/conda-forge/cli11.svg
+[conda-link]: https://github.com/conda-forge/cli11-feedstock
 [github releases]: https://github.com/CLIUtils/CLI11/releases
 [github issues]: https://github.com/CLIUtils/CLI11/issues
 [github pull requests]: https://github.com/CLIUtils/CLI11/pulls
@@ -1028,16 +1068,18 @@ CLI11 was developed at the [University of Cincinnati][] to support of the [GooFi
 [version 1.0 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-10/
 [version 1.3 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-13/
 [version 1.6 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-16/
-[wandbox-badge]: https://img.shields.io/badge/try_1.9-online-blue.svg
-[wandbox-link]: https://wandbox.org/permlink/8SirASwhbFQZyDTW
+[version 2.0 post]: https://iscinumpy.gitlab.io/post/announcing-cli11-20/
+[wandbox-badge]: https://img.shields.io/badge/try_2.1-online-blue.svg
+[wandbox-link]: https://wandbox.org/permlink/CA5bymNHh0AczdeN
 [releases-badge]: https://img.shields.io/github/release/CLIUtils/CLI11.svg
 [cli11-po-compare]: https://iscinumpy.gitlab.io/post/comparing-cli11-and-boostpo/
 [diana slides]: https://indico.cern.ch/event/619465/contributions/2507949/attachments/1448567/2232649/20170424-diana-2.pdf
 [awesome c++]: https://github.com/fffaraz/awesome-cpp/blob/master/README.md#cli
 [cli]: https://codesynthesis.com/projects/cli/
 [single file libs]: https://github.com/nothings/single_file_libs/blob/master/README.md
-[codacy-badge]: https://api.codacy.com/project/badge/Grade/ac0df3aead2a4421b02070c3f324a0b9
-[codacy-link]: https://www.codacy.com/app/henryiii/CLI11?utm_source=github.com&utm_medium=referral&utm_content=CLIUtils/CLI11&utm_campaign=Badge_Grade
+[codacy-badge]: https://app.codacy.com/project/badge/Grade/2796b969c1b54321a02ad08affec0800
+[codacy-link]: https://www.codacy.com/gh/CLIUtils/CLI11/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=CLIUtils/CLI11&amp;utm_campaign=Badge_Grade
 [hunter]: https://docs.hunter.sh/en/latest/packages/pkg/CLI11.html
 [standard readme style]: https://github.com/RichardLitt/standard-readme
 [argparse]: https://github.com/p-ranav/argparse
+[toml]: https://toml.io
diff --git a/packages/CLI11/azure-pipelines.yml b/packages/CLI11/azure-pipelines.yml
index c72c748e6f59d6af651e28231b6cbb0d53f8a32d..750ac31d9aa57f93fc36992c675ff4e0d9d99b3f 100644
--- a/packages/CLI11/azure-pipelines.yml
+++ b/packages/CLI11/azure-pipelines.yml
@@ -4,11 +4,11 @@
 # https://docs.microsoft.com/azure/devops/pipelines/apps/c-cpp/gcc
 
 trigger:
-- master
+- main
 - 'v*'
 
 pr:
-- master
+- main
 - 'v*'
 
 variables:
diff --git a/packages/CLI11/book/CMakeLists.txt b/packages/CLI11/book/CMakeLists.txt
index e99bde60600fa985350a6c76f2e63b10b64626a9..8934873a2e256f0372e871e54e0e80219bf3f7e1 100644
--- a/packages/CLI11/book/CMakeLists.txt
+++ b/packages/CLI11/book/CMakeLists.txt
@@ -1,10 +1,7 @@
+set(book_sources README.md SUMMARY.md)
 
-set(
-    book_sources
-    README.md
-    SUMMARY.md
-)
-
-file(GLOB book_chapters RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} chapters/*.md)
+file(
+  GLOB book_chapters
+  RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+  chapters/*.md)
 add_custom_target(cli_book SOURCES ${book_sources} ${book_chapters})
-
diff --git a/packages/CLI11/book/README.md b/packages/CLI11/book/README.md
index 79a061fb1c6514e8f46f2829bcb49185f7d3706e..917e753fe0876806d6d13ad6636e3ce18faaf885 100644
--- a/packages/CLI11/book/README.md
+++ b/packages/CLI11/book/README.md
@@ -1,8 +1,8 @@
 # CLI11: An introduction
 
-This gitbook is designed to provide an introduction to using the CLI11 library to write your own command line programs. The library is designed to be clean, intuitive, but powerful. There are no requirements beyond C++11 support (and even `<regex>` support not required). It works on Mac, Linux, and Windows, and has 100% test coverage on all three systems. You can simply drop in a single header file (`CLI11.hpp` available in [releases]) to use CLI11 in your own application. Other ways to integrate it into a build system are listed in the [README].
+This gitbook is designed to provide an introduction to using the CLI11 library to write your own command line programs. The library is designed to be clean, intuitive, but powerful. There are no requirements beyond C++11 support (and even `<regex>` support not required). It works on Mac, Linux, and Windows, and has 100% test coverage on all three systems. You can simply drop in a single header file (`CLI11.hpp` available in [releases][]) to use CLI11 in your own application. Other ways to integrate it into a build system are listed in the [README][].
 
-The library was inspired the Python libraries [Plumbum] and [Click], and incorporates many of their user friendly features. The library is extensively documented, with a [friendly introduction][README], this tutorial book, and more technical [API docs].
+The library was inspired the Python libraries [Plumbum][] and [Click][], and incorporates many of their user friendly features. The library is extensively documented, with a [friendly introduction][README], this tutorial book, and more technical [API docs][].
 
 > Feel free to contribute to [this documentation here][CLI11Tutorial] if something can be improved!
 
@@ -28,7 +28,7 @@ Like any good command line application, help is provided. This program can be im
 
 [include](code/intro.cpp)
 
-[Source code](https://github.com/CLIUtils/CLI11/blob/master/book/code/intro.cpp)
+[Source code](https://github.com/CLIUtils/CLI11/blob/main/book/code/intro.cpp)
 
 Unlike some other libraries, this is enough to exit correctly and cleanly if help is requested or if incorrect arguments are passed. You can try this example out for yourself. To compile with GCC:
 
@@ -49,21 +49,17 @@ You can use any valid type; the above example could have used a `boost::file_sys
 
 You can use subcommands, as well. Subcommands support callback lambda functions when parsed, or they can be checked later. You can infinitely nest subcommands, and each is a full `App` instance, supporting everything listed above.
 
-Reading/producing `.ini` files for configuration is also supported, as is using environment variables as input. The base `App` can be subclassed and customized for use in a toolkit (like [GooFit]). All the standard shell idioms, like `--`, work as well.
+Reading/producing `.ini` files for configuration is also supported, as is using environment variables as input. The base `App` can be subclassed and customized for use in a toolkit (like [GooFit][]). All the standard shell idioms, like `--`, work as well.
 
-CLI11 was developed at the [University of Cincinnati] in support of the [GooFit] library under [NSF Award 1414736][NSF 1414736]. It was featured in a [DIANA/HEP] meeting at CERN. Please give it a try! Feedback is always welcome.
+CLI11 was developed at the [University of Cincinnati][] in support of the [GooFit][] library under [NSF Award 1414736][NSF 1414736]. It was featured in a [DIANA/HEP][] meeting at CERN. Please give it a try! Feedback is always welcome.
 
-[GooFit]: https://github.com/GooFit/GooFit
-[DIANA/HEP]: http://diana-hep.org
-[CLI11]: https://github.com/CLIUtils/CLI11
-[CLI11Tutorial]: https://cliutils.github.io/CLI11/book
+[goofit]: https://github.com/GooFit/GooFit
+[diana/hep]: https://diana-hep.org
+[cli11tutorial]: https://cliutils.github.io/CLI11/book
 [releases]: https://github.com/CLIUtils/CLI11/releases
-[API docs]: https://cliutils.github.io/CLI11
-[README]: https://github.com/CLIUtils/CLI11/blob/master/README.md
-[NSF 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736
-[University of Cincinnati]: http://www.uc.edu
-[Plumbum]: http://plumbum.readthedocs.io/en/latest/
-[Click]: https://click.palletsprojects.com/
-
-
-
+[api docs]: https://cliutils.github.io/CLI11
+[readme]: https://github.com/CLIUtils/CLI11/blob/main/README.md
+[nsf 1414736]: https://nsf.gov/awardsearch/showAward?AWD_ID=1414736
+[university of cincinnati]: https://www.uc.edu
+[plumbum]: https://plumbum.readthedocs.io/en/latest/
+[click]: https://click.palletsprojects.com
diff --git a/packages/CLI11/book/SUMMARY.md b/packages/CLI11/book/SUMMARY.md
index cdbc049f444f91b420eaa5cb163b6e3be1e19ad5..38261d52e2c23e55c595824029042fb392271a47 100644
--- a/packages/CLI11/book/SUMMARY.md
+++ b/packages/CLI11/book/SUMMARY.md
@@ -13,4 +13,3 @@
 * [Toolkits](/chapters/toolkits.md)
 * [Advanced topics](/chapters/advanced-topics.md)
 * [Internals](/chapters/internals.md)
-
diff --git a/packages/CLI11/book/chapters/advanced-topics.md b/packages/CLI11/book/chapters/advanced-topics.md
index 1a7ef53790226412e0766a737d099c79a81a50ab..fee282165ada96485d6b594f51f5494597c0dc1e 100644
--- a/packages/CLI11/book/chapters/advanced-topics.md
+++ b/packages/CLI11/book/chapters/advanced-topics.md
@@ -1,6 +1,5 @@
 # Advanced topics
 
-
 ## Environment variables
 
 Environment variables can be used to fill in the value of an option:
@@ -9,6 +8,7 @@ Environment variables can be used to fill in the value of an option:
 std::string opt;
 app.add_option("--my_option", opt)->envname("MY_OPTION");
 ```
+
 If not given on the command line, the environment variable will be checked and read from if it exists. All the standard tools, like default and required, work as expected.
 If passed on the command line, this will ignore the environment variable.
 
@@ -55,7 +55,7 @@ add_option(CLI::App &app, std::string name, cx &variable, std::string descriptio
 
 Then you could use it like this:
 
-```
+```cpp
 std::complex<double> comp{0, 0};
 add_option(app, "-c,--complex", comp);
 ```
@@ -89,7 +89,6 @@ template <typename T> std::istringstream &operator>>(std::istringstream &in, boo
 
 This is an example of how to use the system only; if you are just looking for a way to activate `boost::optional` support on older compilers, you should define `CLI11_BOOST_OPTIONAL` before including a CLI11 file, you'll get the `boost::optional` support.
 
-
 ## Custom converters and type names: std::chrono example
 
 An example of adding a custom converter and typename for `std::chrono` follows:
@@ -97,40 +96,40 @@ An example of adding a custom converter and typename for `std::chrono` follows:
 ```cpp
 namespace CLI
 {
-	template <typename T, typename R>
-	std::istringstream &operator>>(std::istringstream &in, std::chrono::duration<T,R> &val)
-	{
-		T v;
-		in >> v;
-		val = std::chrono::duration<T,R>(v);
-		return in;
-	}
-
-	template <typename T, typename R>
-	std::stringstream &operator<<(std::stringstream &in, std::chrono::duration<T,R> &val)
-	{
-		in << val.count();
-		return in;
-	}
+    template <typename T, typename R>
+    std::istringstream &operator>>(std::istringstream &in, std::chrono::duration<T,R> &val)
+    {
+        T v;
+        in >> v;
+        val = std::chrono::duration<T,R>(v);
+        return in;
+    }
+
+    template <typename T, typename R>
+    std::stringstream &operator<<(std::stringstream &in, std::chrono::duration<T,R> &val)
+    {
+        in << val.count();
+        return in;
+    }
  }
 
 #include <CLI/CLI.hpp>
 
 namespace CLI
 {
-	namespace detail
-	{
-		template <>
-		constexpr const char *type_name<std::chrono::hours>()
-		{
-			return "TIME [H]";
-		}
-
-		template <>
-		constexpr const char *type_name<std::chrono::minutes>()
-		{
-			return "TIME [MIN]";
-		}
+    namespace detail
+    {
+        template <>
+        constexpr const char *type_name<std::chrono::hours>()
+        {
+            return "TIME [H]";
+        }
+
+        template <>
+        constexpr const char *type_name<std::chrono::minutes>()
+        {
+            return "TIME [MIN]";
+        }
         }
 }
 ```
diff --git a/packages/CLI11/book/chapters/an-advanced-example.md b/packages/CLI11/book/chapters/an-advanced-example.md
index 2b20dde15d37fed0cb05094e7160a8887ef96050..84e838ebf32aa0b909dee1fe75e2316b60d305ad 100644
--- a/packages/CLI11/book/chapters/an-advanced-example.md
+++ b/packages/CLI11/book/chapters/an-advanced-example.md
@@ -1,7 +1,5 @@
 # Making a git clone
 
-
-
 Let's try our hand at a little `git` clone, called `geet`. It will just print it's intent, rather than running actual code, since it's just a demonstration. Let's start by adding an app and requiring 1 subcommand to run:
 
 [include:"Intro"](../code/geet.cpp)
@@ -18,7 +16,7 @@ All that's need now is the parse call. We'll print a little message after the co
 
 [include:"Parse"](../code/geet.cpp)
 
-[Source code](https://github.com/CLIUtils/CLI11/tree/master/book/code/geet.cpp)
+[Source code](https://github.com/CLIUtils/CLI11/tree/main/book/code/geet.cpp)
 
 If you compile and run:
 
diff --git a/packages/CLI11/book/chapters/basics.md b/packages/CLI11/book/chapters/basics.md
index aa52cffb7be15e0a97b8fef50e081939899578c1..0c55ec4f404a88e65d0f467e20f0f13ead31f025 100644
--- a/packages/CLI11/book/chapters/basics.md
+++ b/packages/CLI11/book/chapters/basics.md
@@ -24,4 +24,3 @@ Usage: ./a.out [OPTIONS]
 Options:
   -h,--help                   Print this help message and exit
 ```
-
diff --git a/packages/CLI11/book/chapters/config.md b/packages/CLI11/book/chapters/config.md
index 219c3d9dcde4cff4fbbcfe41a838446ddd0a7112..79295bdbfc283e8d7eaa3ff42bdd332dec5d9b57 100644
--- a/packages/CLI11/book/chapters/config.md
+++ b/packages/CLI11/book/chapters/config.md
@@ -5,36 +5,48 @@
 You can tell your app to allow configure files with `set_config("--config")`. There are arguments: the first is the option name. If empty, it will clear the config flag. The second item is the default file name. If that is specified, the config will try to read that file. The third item is the help string, with a reasonable default, and the final argument is a boolean (default: false) that indicates that the configuration file is required and an error will be thrown if the file is not found and this is set to true.
 
 ### Extra fields
+
 Sometimes configuration files are used for multiple purposes so CLI11 allows options on how to deal with extra fields
 
 ```cpp
 app.allow_config_extras(true);
 ```
+
 will allow capture the extras in the extras field of the app. (NOTE:  This also sets the `allow_extras` in the app to true)
 
 ```cpp
 app.allow_config_extras(false);
 ```
+
 will generate an error if there are any extra fields
+for slightly finer control there is a scoped enumeration of the modes or
 
-for slightly finer control there is a scoped enumeration of the modes
-or
 ```cpp
 app.allow_config_extras(CLI::config_extras_mode::ignore);
 ```
+
 will completely ignore extra parameters in the config file.   This mode is the default.
 
 ```cpp
 app.allow_config_extras(CLI::config_extras_mode::capture);
 ```
+
 will store the unrecognized options in the app extras fields. This option is the closest equivalent to `app.allow_config_extras(true);` with the exception that it does not also set the `allow_extras` flag so using this option without also setting `allow_extras(true)` will generate an error which may or may not be the desired behavior.
 
 ```cpp
 app.allow_config_extras(CLI::config_extras_mode::error);
 ```
+
 is equivalent to `app.allow_config_extras(false);`
 
+```cpp
+app.allow_config_extras(CLI::config_extras_mode::ignore_all);
+```
+
+will completely ignore any mismatches, extras, or other issues with the config file
+
 ### Getting the used configuration file name
+
 If it is needed to get the configuration file name used this can be obtained via
 `app.get_config_ptr()->as<std::string>()`  or
 `app["--config"]->as<std::string>()` assuming `--config` was the configuration option name.
@@ -93,7 +105,6 @@ Where X is some positive integer and will allow up to `X` configuration files to
 To print a configuration file from the passed arguments, use `.config_to_str(default_also=false, write_description=false)`, where `default_also` will also show any defaulted arguments, and `write_description` will include option descriptions and the App description
 
 ```cpp
-
  CLI::App app;
  app.add_option(...);
     // several other options
@@ -105,7 +116,6 @@ To print a configuration file from the passed arguments, use `.config_to_str(def
 if a prefix is needed to print before the options, for example to print a config for just a subcommand, the config formatter can be obtained directly.
 
 ```cpp
-
   auto fmtr=app.get_config_formatter();
   //std::string to_config(const App *app, bool default_also, bool write_description, std::string prefix)
   fmtr->to_config(&app,true,true,"sub.");
@@ -113,7 +123,9 @@ if a prefix is needed to print before the options, for example to print a config
 ```
 
 ### Customization of configure file output
-The default config parser/generator has some customization points that allow variations on the TOML format.  The default formatter has a base configuration that matches the TOML format.  It defines 5 characters that define how different aspects of the configuration are handled
+
+The default config parser/generator has some customization points that allow variations on the TOML format.  The default formatter has a base configuration that matches the TOML format.  It defines 5 characters that define how different aspects of the configuration are handled.  You must use `get_config_formatter_base()` to have access to these fields
+
 ```cpp
 /// the character used for comments
 char commentChar = '#';
@@ -125,16 +137,33 @@ char arrayEnd = ']';
 char arraySeparator = ',';
 /// the character used separate the name from the value
 char valueDelimiter = '=';
+/// the character to use around strings
+char stringQuote = '"';
+/// the character to use around single characters
+char characterQuote = '\'';
+/// the maximum number of layers to allow
+uint8_t maximumLayers{255};
+/// the separator used to separator parent layers
+char parentSeparatorChar{'.'};
+/// Specify the configuration index to use for arrayed sections
+uint16_t configIndex{0};
+/// Specify the configuration section that should be used
+std::string configSection;
 ```
 
 These can be modified via setter functions
 
-- ` ConfigBase *comment(char cchar)` Specify the character to start a comment block
--  `ConfigBase *arrayBounds(char aStart, char aEnd)`  Specify the start and end characters for an array
--  `ConfigBase *arrayDelimiter(char aSep)` Specify the delimiter character for an array
--  `ConfigBase *valueSeparator(char vSep)` Specify the delimiter between a name and value
+* `ConfigBase *comment(char cchar)`: Specify the character to start a comment block
+* `ConfigBase *arrayBounds(char aStart, char aEnd)`: Specify the start and end characters for an array
+* `ConfigBase *arrayDelimiter(char aSep)`: Specify the delimiter character for an array
+* `ConfigBase *valueSeparator(char vSep)`: Specify the delimiter between a name and value
+* `ConfigBase *quoteCharacter(char qString, char qChar)` :specify the characters to use around strings and single characters
+* `ConfigBase *maxLayers(uint8_t layers)` : specify the maximum number of parent layers to process. This is useful to limit processing for larger config files
+* `ConfigBase *parentSeparator(char sep)` : specify the character to separate parent layers from options
+* `ConfigBase *section(const std::string &sectionName)` : specify the section name to use to get the option values, only this section will be processed
+* `ConfigBase *index(uint16_t sectionIndex)` : specify an index section to use for processing if multiple TOML sections of the same name are present `[[section]]`
 
-For example to specify reading a configure file that used `:` to separate name and values
+For example, to specify reading a configure file that used `:` to separate name and values:
 
 ```cpp
 auto config_base=app.get_config_formatter_base();
@@ -142,9 +171,11 @@ config_base->valueSeparator(':');
 ```
 
 The default configuration file will read INI files, but will write out files in the TOML format.  To specify outputting INI formatted files use
+
 ```cpp
 app.config_formatter(std::make_shared<CLI::ConfigINI>());
 ```
+
 which makes use of a predefined modification of the ConfigBase class which TOML also uses. If a custom formatter is used that is not inheriting from the from ConfigBase class `get_config_formatter_base() will return a nullptr if RTTI is on (usually the default), or garbage if RTTI is off, so some care must be exercised in its use with custom configurations.
 
 ## Custom formats
@@ -164,18 +195,41 @@ Finally, set your new class as new config formatter:
 app.config_formatter(std::make_shared<NewConfig>());
 ```
 
-See [`examples/json.cpp`](https://github.com/CLIUtils/CLI11/blob/master/examples/json.cpp) for a complete JSON config example.
+See [`examples/json.cpp`](https://github.com/CLIUtils/CLI11/blob/main/examples/json.cpp) for a complete JSON config example.
+
+### Trivial JSON configuration example
+
+```JSON
+{
+   "test": 56,
+   "testb": "test",
+   "flag": true
+}
+```
+
+The parser can handle these structures with only a minor tweak
 
+```cpp
+app.get_config_formatter_base()->valueSeparator(':');
+```
+
+The open and close brackets must be on a separate line and the comma gets interpreted as an array separator but since no values are after the comma they get ignored as well.  This will not support multiple layers or sections or any other moderately complex JSON, but can work if the input file is simple.
 
 ## Triggering Subcommands
-Configuration files can be used to trigger subcommands if a subcommand is set to configure.  By default configuration file just set the default values of a subcommand.  But if the `configure()` option is set on a subcommand then the if the subcommand is utilized via a `[subname]` block in the configuration file it will act as if it were called from the command line.  Subsubcommands can be triggered via [subname.subsubname].  Using the `[[subname]]` will be as if the subcommand were triggered multiple times from the command line.  This functionality can allow the configuration file to act as a scripting file.
+
+Configuration files can be used to trigger subcommands if a subcommand is set to configure.  By default configuration file just set the default values of a subcommand.  But if the `configure()` option is set on a subcommand then the if the subcommand is utilized via a `[subname]` block in the configuration file it will act as if it were called from the command line.  Subsubcommands can be triggered via `[subname.subsubname]`.  Using the `[[subname]]` will be as if the subcommand were triggered multiple times from the command line.  This functionality can allow the configuration file to act as a scripting file.
 
 For custom configuration files this behavior can be triggered by specifying the parent subcommands in the structure and `++` as the name to open a new subcommand scope and `--` to close it.  These names trigger the different callbacks of configurable subcommands.
 
+## Stream parsing
+
+In addition to the regular parse functions a `parse_from_stream(std::istream &input)` is available to directly parse a stream operator.  For example to process some arguments in an already open file stream.  The stream is fed directly in the config parser so bypasses the normal command line parsing.
+
 ## Implementation Notes
-The config file input works with any form of the option given:  Long, short, positional, or the environment variable name.  When generating a config file it will create a name in following priority.
 
-1.   First long name
-1.   Positional name
-1.   First short name
-1.   Environment name
+The config file input works with any form of the option given:  Long, short, positional, or the environment variable name.  When generating a config file it will create an option name in following priority.
+
+1. First long name
+2. Positional name
+3. First short name
+4. Environment name
diff --git a/packages/CLI11/book/chapters/flags.md b/packages/CLI11/book/chapters/flags.md
index d4f88fb6895a9d5402e14e6c65e2975311f94e53..4e269acdf73dd1a87cce0f6e1a7c4bd8347aeefd 100644
--- a/packages/CLI11/book/chapters/flags.md
+++ b/packages/CLI11/book/chapters/flags.md
@@ -11,8 +11,7 @@ bool my_flag{false};
 app.add_flag("-f", my_flag, "Optional description");
 ```
 
-This will bind the flag `-f` to the boolean `my_flag`. After the parsing step, `my_flag` will be `false` if the flag was not found on the command line, or `true` if it was. By default, it will be allowed any number of times, but if you explicitly[^1] request `->take_last(false)`, it will only be allowed once; passing something like `./my_app -f -f` or `./my_app -ff` will throw a `ParseError` with a nice help description.
-
+This will bind the flag `-f` to the boolean `my_flag`. After the parsing step, `my_flag` will be `false` if the flag was not found on the command line, or `true` if it was. By default, it will be allowed any number of times, but if you explicitly\[^1\] request `->take_last(false)`, it will only be allowed once; passing something like `./my_app -f -f` or `./my_app -ff` will throw a `ParseError` with a nice help description. A flag name may start with any character except ('-', ' ', '\n', and '!'). For long flags, after the first character all characters are allowed except ('=',':','{',' ', '\n').  Names are given as a comma separated string, with the dash or dashes. An flag can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed.
 
 ## Integer flags
 
@@ -65,12 +64,11 @@ After parsing, you can use `my_flag->count()` to count the number of times this
 
 If you want to define a callback that runs when you make a flag, you can use `add_flag_function` (C++11 or newer) or `add_flag` (C++14 or newer only) to add a callback function. The function should have the signature `void(std::size_t)`. This could be useful for a version printout, etc.
 
-```
+```cpp
 auto callback = [](int count){std::cout << "This was called " << count << " times";};
 app.add_flag_function("-c", callback, "Optional description");
 ```
 
-
 ## Aliases
 
 The name string, the first item of every `add_` method, can contain as many short and long names as you want, separated by commas. For example, `"-a,--alpha,-b,--beta"` would allow any of those to be recognized on the command line. If you use the same name twice, or if you use the same name in multiple flags, CLI11 will immediately throw a `CLI::ConstructionError` describing your problem (it will not wait until the parsing step).
@@ -99,7 +97,7 @@ The values would be used like this:
 
 [include:"usage"](../code/flags.cpp)
 
-[Source code](https://github.com/CLIUtils/CLI11/tree/master/book/code/flags.cpp)
+[Source code](https://github.com/CLIUtils/CLI11/tree/main/book/code/flags.cpp)
 
 If you compile and run:
 
@@ -122,5 +120,4 @@ Flag int: 3
 Flag plain: 1
 ```
 
-
-[^1] It will not inherit this from the parent defaults, since this is often useful even if you don't want all options to allow multiple passed options.
+\[^1\]: It will not inherit this from the parent defaults, since this is often useful even if you don't want all options to allow multiple passed options.
diff --git a/packages/CLI11/book/chapters/formatting.md b/packages/CLI11/book/chapters/formatting.md
index 66dd228da893faa11fca64c3524b7e137bb3022a..f9cd36fa49d875ec3c0741e03e2b6014f5723667 100644
--- a/packages/CLI11/book/chapters/formatting.md
+++ b/packages/CLI11/book/chapters/formatting.md
@@ -4,13 +4,12 @@
 New in CLI11 1.6
 {% endhint %}
 
-##  Customizing an existing formatter
+## Customizing an existing formatter
 
 In CLI11, you can control the output of the help printout in full or in part. The default formatter was written in such a way as to be customizable. You can use `app.get_formatter()` to get the current formatter. The formatter you set will be inherited by subcommands that are created after you set the formatter.
 
 There are several configuration options that you can set:
 
-
 | Set method | Description | Availability |
 |------------|-------------|--------------|
 | `column_width(width)` | The width of the columns | Both |
@@ -41,7 +40,7 @@ Look at the class definitions in `FormatterFwd.hpp` or the method definitions in
 
 This is a normal printout, with `<>` indicating the methods used to produce each line.
 
-```
+```text
 <make_description(app)>
 <make_usage(app)>
 <make_positionals(app)>
@@ -60,7 +59,7 @@ This is a normal printout, with `<>` indicating the methods used to produce each
 
 The `make_groups` print the group name then call `make_option(o)` on the options listed in that group. The normal printout for an option looks like this:
 
-```
+```text
         make_option_opts(o)
             ┌───┴────┐
  -n,--name  (REQUIRED)      This is a description
@@ -72,7 +71,3 @@ Notes:
 
 * `*1`: This signature depends on whether the call is from a positional or optional.
 * `o` is opt pointer, `p` is true if positional.
-
-
-
-
diff --git a/packages/CLI11/book/chapters/internals.md b/packages/CLI11/book/chapters/internals.md
index 17bc54aa34bebd52a14f09fca5afa306ec20554e..1551c79c659bb91610448b762c13d42ea1f08831 100644
--- a/packages/CLI11/book/chapters/internals.md
+++ b/packages/CLI11/book/chapters/internals.md
@@ -41,5 +41,3 @@ The `_process` procedure runs the following steps; each step is recursive and co
 ## Exceptions
 
 The library immediately returns a C++ exception when it detects a problem, such as an incorrect construction or a malformed command line.
-
-
diff --git a/packages/CLI11/book/chapters/options.md b/packages/CLI11/book/chapters/options.md
index 04bfd0bbd2f0a04e683d5f31a9df9f1bf2e79e00..8b1daa0fffe546677b3b8f40b280e7961c95dc69 100644
--- a/packages/CLI11/book/chapters/options.md
+++ b/packages/CLI11/book/chapters/options.md
@@ -1,37 +1,37 @@
 # Options
 
 ## Simple options
-The most versatile addition to a command line program is a option. This is like a flag, but it takes an argument. CLI11 handles all the details for many types of options for you, based on their type. To add an option:
 
+The most versatile addition to a command line program is a option. This is like a flag, but it takes an argument. CLI11 handles all the details for many types of options for you, based on their type. To add an option:
 
 ```cpp
 int int_option{0};
 app.add_option("-i", int_option, "Optional description");
 ```
 
-This will bind the option `-i` to the integer `int_option`. On the command line, a single value that can be converted to an integer will be expected. Non-integer results will fail. If that option is not given, CLI11 will not touch the initial value. This allows you to set up defaults by simply setting your value beforehand. If you want CLI11 to display your default value, you can add the optional final argument `true` when you add the option.
+This will bind the option `-i` to the integer `int_option`. On the command line, a single value that can be converted to an integer will be expected. Non-integer results will fail. If that option is not given, CLI11 will not touch the initial value. This allows you to set up defaults by simply setting your value beforehand. If you want CLI11 to display your default value, you can add `->capture_default_str()` after the option.
 
 ```cpp
 int int_option{0};
-app.add_option("-i", int_option, "Optional description", true);
+app.add_option("-i", int_option, "Optional description")->capture_default_str();
 ```
 
 You can use any C++ int-like type, not just `int`. CLI11 understands the following categories of types:
 
 | Type        | CLI11 |
 |-------------|-------|
-| number like    | Integers, floats, bools, or any type that can be constructed from an integer or floating point number |
+| number like    | Integers, floats, bools, or any type that can be constructed from an integer or floating point number.  Accepts common numerical strings like `0xFF` as well as octal, and decimal |
 | string-like | std\::string, or anything that can be constructed from or assigned a std\::string |
 | char | For a single char, single string values are accepted, otherwise longer strings are treated as integral values and a conversion is attempted |
 | complex-number | std::complex or any type which has a real(), and imag() operations available, will allow 1 or 2 string definitions like "1+2j" or two arguments "1","2" |
 | enumeration | any enum or enum class type is supported through conversion from the underlying type(typically int, though it can be specified otherwise) |
 | container-like | a container(like vector) of any available types including other containers |
-| wrapper | any other object with a `value_type` static definition where the type specified by `value_type` is one of type in this list |
+| wrapper | any other object with a `value_type` static definition where the type specified by `value_type` is one of the type in this list, including `std::atomic<>` |
 | tuple | a tuple, pair, or array, or other type with a tuple size and tuple_type operations defined and the members being a type contained in this list |
 | function | A function that takes an array of strings and returns a string that describes the conversion failure or empty for success. May be the empty function. (`{}`) |
 | streamable | any other type with a `<<` operator will also work |
 
-By default, CLI11 will assume that an option is optional, and one value is expected if you do not use a vector. You can change this on a specific option using option modifiers.
+By default, CLI11 will assume that an option is optional, and one value is expected if you do not use a vector. You can change this on a specific option using option modifiers.  An option name may start with any character except ('-', ' ', '\n', and '!'). For long options, after the first character all characters are allowed except ('=',':','{',' ', '\n'). Names are given as a comma separated string, with the dash or dashes. An option can have as many names as you want, and afterward, using `count`, you can use any of the names, with dashes as needed, to count the options. One of the names is allowed to be given without proceeding dash(es); if present the option is a positional option, and that name will be used on the help line for its positional form.
 
 ## Positional options and aliases
 
@@ -72,29 +72,38 @@ Vectors will be replaced by the parsed content if the option is given on the com
 
 A definition of a container for purposes of CLI11 is a type with a `end()`, `insert(...)`, `clear()` and `value_type` definitions.  This includes `vector`, `set`, `deque`, `list`, `forward_iist`, `map`, `unordered_map` and a few others from the standard library, and many other containers from the boost library.
 
-### containers of containers
+### Containers of containers
+
 Containers of containers are also supported.
+
 ```cpp
 std::vector<std::vector<int>> int_vec;
 app.add_option("--vec", int_vec, "My vector of vectors option");
 ```
+
 CLI11 inserts a separator sequence at the start of each argument call to separate the vectors.  So unless the separators are injected as part of the command line each call of the option on the command line will result in a separate element of the outer vector.  This can be manually controlled via `inject_separator(true|false)` but in nearly all cases this should be left to the defaults.  To insert of a separator from the command line add a `%%` where the separation should occur.
-```
+
+```bash
 cmd --vec_of_vec 1 2 3 4 %% 1 2
 ```
+
 would then result in a container of size 2 with the first element containing 4 values and the second 2.
 
 This separator is also the only way to get values into something like
+
 ```cpp
 std::pair<std::vector<int>,std::vector<int>> two_vecs;
 app.add_option("--vec", two_vecs, "pair of vectors");
 ```
+
 without calling the argument twice.
 
 Further levels of nesting containers should compile but intermediate layers will only have a single element in the container, so is probably not that useful.
 
 ### Nested types
-Types can be nested For example
+
+Types can be nested. For example:
+
 ```cpp
 std::map<int, std::pair<int,std::string>> map;
 app.add_option("--dict", map, "map of pairs");
@@ -106,6 +115,7 @@ will require 3 arguments for each invocation, and multiple sets of 3 arguments c
 std::map<int, std::pair<int,std::vector<std::string>>> map;
 app.add_option("--dict", map, "map of pairs");
 ```
+
 will result in a requirement for 2 integers on each invocation and absorb an unlimited number of strings including 0.
 
 ## Option modifiers
@@ -119,8 +129,8 @@ When you call `add_option`, you get a pointer to the added option. You can use t
 | `->expected(Nmin,Nmax)` | Take between `Nmin` and `Nmax` values. |
 | `->type_size(N)` | specify that each block of values would consist of N elements |
 | `->type_size(Nmin,Nmax)` | specify that each block of values would consist of between Nmin and Nmax elements |
-| `->needs(opt)` | This option requires another option to also be present, opt is an `Option` pointer. |
-| `->excludes(opt)` | This option cannot be given with `opt` present, opt is an `Option` pointer. |
+| `->needs(opt)` | This option requires another option to also be present, opt is an `Option` pointer or a string with the name of the option.  Can be removed with `->remove_needs(opt)` |
+| `->excludes(opt)` | This option cannot be given with `opt` present, opt is an `Option` pointer or a string with the name of the option.  Can be removed with `->remove_excludes(opt)` |
 | `->envname(name)` | Gets the value from the environment if present and not passed on the command line. |
 | `->group(name)` | The help group to put the option in. No effect for positional options. Defaults to `"Options"`. `"Hidden"` will not show up in the help print. |
 | `->description(string)` | Set/change the description |
@@ -139,8 +149,14 @@ When you call `add_option`, you get a pointer to the added option. You can use t
 | `->transform(Validator)` | Run a transforming validator on each value passed. See [Validators](./validators.md) for more info |
 | `->each(void(std::string))` | Run a function on each parsed value, *in order*. |
 | `->default_str(string)` | set a default string for use in the help and as a default value if no arguments are passed and a value is requested |
-| `->default_function(string())` | Advanced: Change the function that `capture_default_str()` uses. |
+| `->default_function(std::string())` | Advanced: Change the function that `capture_default_str()` uses. |
 | `->default_val(value)` | Generate the default string from a value and validate that the value is also valid.  For options that assign directly to a value type the value in that type is also updated.  Value must be convertible to a string(one of known types or have a stream operator). |
+| `->capture_default_str()` | Store the current value attached and display it in the help string. |
+| `->always_capture_default()` | Always run `capture_default_str()` when creating new options. Only useful on an App's `option_defaults`. |
+| `->run_callback_for_default()` | Force the option callback to be executed or the variable set when the `default_val` is used.  |
+| `->force_callback()` | Force the option callback to be executed regardless of whether the option was used or not.  Will use the default_str if available, if no default is given the callback will be executed with an empty string as an argument, which will translate to a default initialized value, which can be compiler dependent |
+|`->trigger_on_parse()` | Have the option callback be triggered when the value is parsed vs. at the end of all parsing, the option callback can potentially be executed multiple times.  Generally only useful if you have a user defined callback or validation check. Or potentially if a vector input is given multiple times as it will clear the results when a repeat option is given via command line.  It will trigger the callbacks once per option call on the command line|
+| `->option_text(string)` | Sets the text between the option name and description. |
 
 The `->check(...)` and `->transform(...)` modifiers can also take a callback function of the form `bool function(std::string)` that runs on every value that the option receives, and returns a value that tells CLI11 whether the check passed or failed.
 
@@ -161,19 +177,19 @@ if(* opt)
 
 One of CLI11's systems to allow customizability without high levels of verbosity is the inheritance system. You can set default values on the parent `App`, and all options and subcommands created from it remember the default values at the point of creation. The default value for Options, specifically, are accessible through the `option_defaults()` method. There are a number of settings that can be set and inherited:
 
-*   `group`: The group name starts as "Options"
-*   `required`: If the option must be given. Defaults to `false`. Is ignored for flags.
-*   `multi_option_policy`: What to do if several copies of an option are passed and one value is expected. Defaults to `CLI::MultiOptionPolicy::Throw`. This is also used for bool flags, but they always are created with the value `CLI::MultiOptionPolicy::TakeLast` regardless of the default, so that multiple bool flags does not cause an error. But you can override that flag by flag.
-*   `ignore_case`: Allow any mixture of cases for the option or flag name
-*   `ignore_underscore`: Allow any number of underscores in the option or flag name
-*   `configurable`:  Specify whether an option can be configured through a config file
-*   `disable_flag_override`:  do not allow flag values to be overridden on the command line
-*   `always_capture_default`:  specify that the default values should be automatically captured.
-*   `delimiter`:  A delimiter to use for capturing multiple values in a single command line string (e.g. --flag="flag,-flag2,flag3")
+* `group`: The group name starts as "Options"
+* `required`: If the option must be given. Defaults to `false`. Is ignored for flags.
+* `multi_option_policy`: What to do if several copies of an option are passed and one value is expected. Defaults to `CLI::MultiOptionPolicy::Throw`. This is also used for bool flags, but they always are created with the value `CLI::MultiOptionPolicy::TakeLast` regardless of the default, so that multiple bool flags does not cause an error. But you can override that flag by flag.
+* `ignore_case`: Allow any mixture of cases for the option or flag name
+* `ignore_underscore`: Allow any number of underscores in the option or flag name
+* `configurable`:  Specify whether an option can be configured through a config file
+* `disable_flag_override`:  do not allow flag values to be overridden on the command line
+* `always_capture_default`:  specify that the default values should be automatically captured.
+* `delimiter`:  A delimiter to use for capturing multiple values in a single command line string (e.g. --flag="flag,-flag2,flag3")
 
 An example of usage:
 
-```
+```cpp
 app.option_defaults()->ignore_case()->group("Required");
 
 app.add_flag("--CaSeLeSs");
@@ -182,17 +198,16 @@ app.get_group() // is "Required"
 
 Groups are mostly for visual organization, but an empty string for a group name will hide the option.
 
-
 ### Windows style options
 
 You can also set the app setting `app->allow_windows_style_options()` to allow windows style options to also be recognized on the command line:
 
-*   `/a` (flag)
-*   `/f filename` (option)
-*   `/long` (long flag)
-*   `/file filename` (space)
-*   `/file:filename` (colon)
-*   `/long_flag:false` (long flag with : to override the default value)
+* `/a` (flag)
+* `/f filename` (option)
+* `/long` (long flag)
+* `/file filename` (space)
+* `/file:filename` (colon)
+* `/long_flag:false` (long flag with : to override the default value)
 
 Windows style options do not allow combining short options or values not separated from the short option like with `-` options. You still specify option names in the same manner as on Linux with single and double dashes when you use the `add_*` functions, and the Linux style on the command line will still work. If a long and a short option share the same name, the option will match on the first one defined.
 
@@ -200,15 +215,19 @@ Windows style options do not allow combining short options or values not separat
 
 How an option and its arguments are parsed depends on a set of controls that are part of the option structure.  In most circumstances these controls are set automatically based on the type or function used to create the option and the type the arguments are parsed into.  The variables define the size of the underlying type (essentially how many strings make up the type), the expected size (how many groups are expected) and a flag indicating if multiple groups are allowed with a single option.  And these interact with the `multi_option_policy` when it comes time to parse.
 
-### examples
-How options manage this is best illustrated through some examples
+### Examples
+
+How options manage this is best illustrated through some examples.
+
 ```cpp
 std::string val;
 app.add_option("--opt",val,"description");
 ```
+
 creates an option that assigns a value to a `std::string`  When this option is constructed it sets a type_size min and max of 1.  Meaning that the assignment uses a single string.  The Expected size is also set to 1 by default, and `allow_extra_args` is set to false. meaning that each time this option is called 1 argument is expected.  This would also be the case if val were a `double`, `int` or any other single argument types.
 
 now for example
+
 ```cpp
 std::pair<int, std::string> val;
 app.add_option("--opt",val,"description");
@@ -227,6 +246,7 @@ detects a type size of 1, since the underlying element type is a single string,
 std::vector<std::tuple<int, double, std::string>> val;
 app.add_option("--opt",val,"description");
 ```
+
 gets into the complicated cases where the type size is now 3.  and the expected max is set to a large number and `allow_extra_args` is set to true.  In this case at least 3 arguments are required to follow the option,  and subsequent groups must come in groups of three, otherwise an error will result.
 
 ```cpp
@@ -243,11 +263,11 @@ app.add_option("--opt",val,"description");
 
 triggers the complex number type which has a min of 1 and max of 2,  so 1 or 2 strings can be passed.  Complex number conversion supports arguments of the form "1+2j" or "1","2", or "1" "2i".  The imaginary number symbols `i` and `j` are interchangeable in this context.
 
-
 ```cpp
 std::vector<std::vector<int>> val;
 app.add_option("--opt",val,"description");
 ```
+
 has a type size of 1 to (1<<30).
 
 ### Customization
@@ -259,11 +279,13 @@ std::string val;
 auto opt=app.add_flag("--opt{vvv}",val,"description");
 opt->expected(0,1);
 ```
+
 will create a hybrid option, that can exist on its own in which case the value "vvv" is used or if a value is given that value will be used.
 
-There are some additional options that can be specified to modify an option for specific cases
--   `->run_callback_for_default()` will specify that the callback should be executed when a default_val is set. This is set automatically when appropriate though it can be turned on or off and any user specified callback for an option will be executed when the default value for an option is set.
+There are some additional options that can be specified to modify an option for specific cases:
+
+* `->run_callback_for_default()` will specify that the callback should be executed when a default_val is set. This is set automatically when appropriate though it can be turned on or off and any user specified callback for an option will be executed when the default value for an option is set.
 
 ## Unusual circumstances
-There are a few cases where some things break down in the type system managing options and definitions.  Using the `add_option` method defines a lambda function to extract a default value if required.  In most cases this either straightforward or a failure is detected automatically and handled.  But in a few cases a streaming template is available that several layers down may not actually be defined.  The conditions in CLI11 cannot detect this circumstance automatically and will result in compile error.  One specific known case is `boost::optional` if the boost optional_io header is included.  This header defines a template for all boost optional values even if they do no actually have a streaming operator.  For example `boost::optional<std::vector>` does not have a streaming operator but one is detected since it is part of a template.  For these cases a secondary method `app->add_option_no_stream(...)` is provided that bypasses this operation completely and should compile in these cases.
 
+There are a few cases where some things break down in the type system managing options and definitions.  Using the `add_option` method defines a lambda function to extract a default value if required.  In most cases this is either straightforward or a failure is detected automatically and handled.  But in a few cases a streaming template is available that several layers down may not actually be defined.  This results in CLI11 not being able to detect this circumstance automatically and will result in compile error.  One specific known case is `boost::optional` if the boost optional_io header is included.  This header defines a template for all boost optional values even if they do not actually have a streaming operator.  For example `boost::optional<std::vector>` does not have a streaming operator but one is detected since it is part of a template.  For these cases a secondary method `app->add_option_no_stream(...)` is provided that bypasses this operation completely and should compile in these cases.
diff --git a/packages/CLI11/book/chapters/subcommands.md b/packages/CLI11/book/chapters/subcommands.md
index 230cca880040931ee41a590832f58908cd6b5697..585f91c72cf26cbe0d5b101f562b507d6029f8c4 100644
--- a/packages/CLI11/book/chapters/subcommands.md
+++ b/packages/CLI11/book/chapters/subcommands.md
@@ -7,7 +7,6 @@ C++ application, though the system git uses to extend the main command by callin
 in separate executables is supported too; that's called "Prefix commands" and is included at the
 end of this chapter.
 
-
 ## The parent App
 
 We'll start by discussing the parent `App`. You've already used it quite a bit, to create
@@ -18,7 +17,6 @@ You can replace the default help print when a `ParseError` is thrown with `app.s
 The default is `CLI:::FailureMessage::simple`, and you can easily define a new one. Just make a (lambda) function that takes an App pointer
 and a reference to an error code (even if you don't use them), and returns a string.
 
-
 ## Adding a subcommand
 
 Subcommands can be added just like an option:
@@ -55,6 +53,7 @@ Each App has controls to set the number of subcommands you expect. This is contr
 ```cpp
 app.require_subcommand(/* min */ 0, /* max */ 1);
 ```
+
 If you set the max to 0, CLI11 will allow an unlimited number of subcommands. After the (non-unlimited) maximum
 is reached, CLI11 will stop trying to match subcommands. So the if you pass "`one two`" to a command, and both `one`
 and `two` are subcommands, it will depend on the maximum number as to whether the "`two`" is a subcommand or an argument to the
diff --git a/packages/CLI11/book/chapters/toolkits.md b/packages/CLI11/book/chapters/toolkits.md
index 3b8e93a1ff78f6421f287bb4ce350e36dd2ca68e..4c276387a5469fab86e957720fc7294cc2db4e4c 100644
--- a/packages/CLI11/book/chapters/toolkits.md
+++ b/packages/CLI11/book/chapters/toolkits.md
@@ -2,7 +2,7 @@
 
 CLI11 was designed to be integrate into a toolkit, providing a native experience for users. This was used in GooFit to provide `GooFit::Application`, an class designed to make ROOT users feel at home.
 
-# Custom namespace
+## Custom namespace
 
 If you want to provide CLI11 in a custom namespace, you'll want to at least put `using CLI::App` in your namespace. You can also include Option, some errors, and validators. You can also put `using namespace CLI` inside your namespace to import everything.
 
@@ -17,14 +17,12 @@ You may also want to make your own copy of the `CLI11_PARSE` macro. Something li
      }
 ```
 
-
-# Subclassing App
+## Subclassing App
 
 If you subclass `App`, you'll just need to do a few things. You'll need a constructor; calling the base `App` constructor is a good idea, but not necessary (it just sets a description and adds a help flag.
 
 You can call anything you would like to configure in the constructor, like `option_defaults()->take_last()` or `fallthrough()`, and it will be set on all user instances. You can add flags and options, as well.
 
-
-# Virtual functions provided
+## Virtual functions provided
 
 You are given a few virtual functions that you can change (only on the main App). `pre_callback` runs right before the callbacks run, letting you print out custom messages at the top of your app.
diff --git a/packages/CLI11/book/chapters/validators.md b/packages/CLI11/book/chapters/validators.md
index 6266a9cae4e8e7394370b8877fde2679a4f05548..06e42c80a0f82a25f94a9329be65550fca92233d 100644
--- a/packages/CLI11/book/chapters/validators.md
+++ b/packages/CLI11/book/chapters/validators.md
@@ -49,7 +49,6 @@ The built-in validators for CLI11 are:
 | `NonexistentPath`   | Check for an non-existing path |
 | `Range(min=0, max)` |  Produce a range (factory). Min and max are inclusive. |
 
-
 And, the protected members that you can set when you make your own are:
 
 | Type | Member | Description |
@@ -60,5 +59,3 @@ And, the protected members that you can set when you make your own are:
 | `int` (`-1`) | `application_index_` | The element this validator applies to (-1 for all) |
 | `bool` (`true`) | `active_` | This can be disabled |
 | `bool` (`false`) | `non_modifying_` | Specify that this is a Validator instead of a Transformer |
-
-
diff --git a/packages/CLI11/book/code/CMakeLists.txt b/packages/CLI11/book/code/CMakeLists.txt
index b91a9c7bbfc95a8515868b75bc9fe22e72027efe..987d53c4988a151977ed3f409215c2a2b706af2c 100644
--- a/packages/CLI11/book/code/CMakeLists.txt
+++ b/packages/CLI11/book/code/CMakeLists.txt
@@ -12,27 +12,21 @@ enable_testing()
 
 # Quick function to add the base executable
 function(add_cli_exe NAME)
-    add_executable(${NAME} ${NAME}.cpp)
-    target_link_libraries(${NAME} CLI11::CLI11)
+  add_executable(${NAME} ${NAME}.cpp)
+  target_link_libraries(${NAME} CLI11::CLI11)
 endfunction()
 
-
 add_cli_exe(simplest)
 add_test(NAME simplest COMMAND simplest)
 
-
 add_cli_exe(intro)
 add_test(NAME intro COMMAND intro)
 add_test(NAME intro_p COMMAND intro -p 5)
 
-
 add_cli_exe(flags)
 add_test(NAME flags COMMAND flags)
 add_test(NAME flags_bip COMMAND flags -b -i -p)
 
-
 add_cli_exe(geet)
-add_test(NAME geet_add    COMMAND geet add)
+add_test(NAME geet_add COMMAND geet add)
 add_test(NAME geet_commit COMMAND geet commit -m "Test")
-
-
diff --git a/packages/CLI11/cmake/CLI11GeneratePkgConfig.cmake b/packages/CLI11/cmake/CLI11GeneratePkgConfig.cmake
index 667705d2a7a53d6b78ae6d17413cc77a90b8a3cd..5abb03d165f908d73ba0c85b97dd156483100778 100644
--- a/packages/CLI11/cmake/CLI11GeneratePkgConfig.cmake
+++ b/packages/CLI11/cmake/CLI11GeneratePkgConfig.cmake
@@ -1,6 +1,3 @@
 configure_file("cmake/CLI11.pc.in" "CLI11.pc" @ONLY)
 
-install(FILES "${PROJECT_BINARY_DIR}/CLI11.pc"
-  DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
-
-
+install(FILES "${PROJECT_BINARY_DIR}/CLI11.pc" DESTINATION "${CMAKE_INSTALL_DATADIR}/pkgconfig")
diff --git a/packages/CLI11/cmake/CodeCoverage.cmake b/packages/CLI11/cmake/CodeCoverage.cmake
index 907cf72d01094f84568dd0e214ab8942e980c24a..e011ef1342773b14b1e8dedebb3e3e83e4161f3c 100644
--- a/packages/CLI11/cmake/CodeCoverage.cmake
+++ b/packages/CLI11/cmake/CodeCoverage.cmake
@@ -69,57 +69,51 @@
 include(CMakeParseArguments)
 
 # Check prereqs
-find_program( GCOV_PATH gcov )
-find_program( LCOV_PATH  NAMES lcov lcov.bat lcov.exe lcov.perl)
-find_program( GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat )
-find_program( GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
-find_program( SIMPLE_PYTHON_EXECUTABLE python )
+find_program(GCOV_PATH gcov)
+find_program(LCOV_PATH NAMES lcov lcov.bat lcov.exe lcov.perl)
+find_program(GENHTML_PATH NAMES genhtml genhtml.perl genhtml.bat)
+find_program(GCOVR_PATH gcovr PATHS ${CMAKE_SOURCE_DIR}/scripts/test)
+find_program(SIMPLE_PYTHON_EXECUTABLE python)
 
 if(NOT GCOV_PATH)
-    message(FATAL_ERROR "gcov not found! Aborting...")
+  message(FATAL_ERROR "gcov not found! Aborting...")
 endif() # NOT GCOV_PATH
 
 if("${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
-    if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
-        message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
-    endif()
+  if("${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS 3)
+    message(FATAL_ERROR "Clang version must be 3.0.0 or greater! Aborting...")
+  endif()
 elseif(NOT CMAKE_COMPILER_IS_GNUCXX)
-    message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
+  message(FATAL_ERROR "Compiler is not GNU gcc! Aborting...")
 endif()
 
-set(COVERAGE_COMPILER_FLAGS "-g -O0 --coverage -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
+set(COVERAGE_COMPILER_FLAGS
+    "-g -O0 --coverage -fprofile-arcs -ftest-coverage -fno-inline -fno-inline-small-functions -fno-default-inline"
     CACHE INTERNAL "")
 
 set(CMAKE_CXX_FLAGS_COVERAGE
     ${COVERAGE_COMPILER_FLAGS}
-    CACHE STRING "Flags used by the C++ compiler during coverage builds."
-    FORCE )
+    CACHE STRING "Flags used by the C++ compiler during coverage builds." FORCE)
 set(CMAKE_C_FLAGS_COVERAGE
     ${COVERAGE_COMPILER_FLAGS}
-    CACHE STRING "Flags used by the C compiler during coverage builds."
-    FORCE )
+    CACHE STRING "Flags used by the C compiler during coverage builds." FORCE)
 set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
     ""
-    CACHE STRING "Flags used for linking binaries during coverage builds."
-    FORCE )
+    CACHE STRING "Flags used for linking binaries during coverage builds." FORCE)
 set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE
     ""
-    CACHE STRING "Flags used by the shared libraries linker during coverage builds."
-    FORCE )
-mark_as_advanced(
-    CMAKE_CXX_FLAGS_COVERAGE
-    CMAKE_C_FLAGS_COVERAGE
-    CMAKE_EXE_LINKER_FLAGS_COVERAGE
-    CMAKE_SHARED_LINKER_FLAGS_COVERAGE )
+    CACHE STRING "Flags used by the shared libraries linker during coverage builds." FORCE)
+mark_as_advanced(CMAKE_CXX_FLAGS_COVERAGE CMAKE_C_FLAGS_COVERAGE CMAKE_EXE_LINKER_FLAGS_COVERAGE
+                 CMAKE_SHARED_LINKER_FLAGS_COVERAGE)
 
 if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
-    message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
+  message(WARNING "Code coverage results with an optimised (non-Debug) build may be misleading")
 endif() # NOT CMAKE_BUILD_TYPE STREQUAL "Debug"
 
 if(CMAKE_C_COMPILER_ID STREQUAL "GNU")
-    link_libraries(gcov)
+  link_libraries(gcov)
 else()
-    set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
+  set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} --coverage")
 endif()
 
 # Defines a target for running and collection code coverage information
@@ -134,54 +128,57 @@ endif()
 # )
 function(SETUP_TARGET_FOR_COVERAGE)
 
-    set(options NONE)
-    set(oneValueArgs NAME)
-    set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
-    cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-
-    if(NOT LCOV_PATH)
-        message(FATAL_ERROR "lcov not found! Aborting...")
-    endif() # NOT LCOV_PATH
-
-    if(NOT GENHTML_PATH)
-        message(FATAL_ERROR "genhtml not found! Aborting...")
-    endif() # NOT GENHTML_PATH
-
-    # Setup target
-    add_custom_target(${Coverage_NAME}
-
-        # Cleanup lcov
-        COMMAND ${LCOV_PATH} --directory . --zerocounters
-        # Create baseline to make sure untouched files show up in the report
-        COMMAND ${LCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base
-
-        # Run tests
-        COMMAND ${Coverage_EXECUTABLE}
-
-        # Capturing lcov counters and generating report
-        COMMAND ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info
-        # add baseline counters
-        COMMAND ${LCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file ${Coverage_NAME}.total
-        COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_EXCLUDES} --output-file ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
-        COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
-        COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.total ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
-
-        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
-        DEPENDS ${Coverage_DEPENDENCIES}
-        COMMENT "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
-    )
-
-    # Show where to find the lcov info report
-    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
-        COMMAND ;
-        COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info."
-    )
-
-    # Show info where to find the report
-    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
-        COMMAND ;
-        COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report."
-    )
+  set(options NONE)
+  set(oneValueArgs NAME)
+  set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+  cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+  if(NOT LCOV_PATH)
+    message(FATAL_ERROR "lcov not found! Aborting...")
+  endif() # NOT LCOV_PATH
+
+  if(NOT GENHTML_PATH)
+    message(FATAL_ERROR "genhtml not found! Aborting...")
+  endif() # NOT GENHTML_PATH
+
+  # Setup target
+  add_custom_target(
+    ${Coverage_NAME}
+    # Cleanup lcov
+    COMMAND ${LCOV_PATH} --directory . --zerocounters
+    # Create baseline to make sure untouched files show up in the report
+    COMMAND ${LCOV_PATH} -c -i -d . -o ${Coverage_NAME}.base
+    # Run tests
+    COMMAND ${Coverage_EXECUTABLE}
+    # Capturing lcov counters and generating report
+    COMMAND ${LCOV_PATH} --directory . --capture --output-file ${Coverage_NAME}.info
+    # add baseline counters
+    COMMAND ${LCOV_PATH} -a ${Coverage_NAME}.base -a ${Coverage_NAME}.info --output-file
+            ${Coverage_NAME}.total
+    COMMAND ${LCOV_PATH} --remove ${Coverage_NAME}.total ${COVERAGE_EXCLUDES} --output-file
+            ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
+    COMMAND ${GENHTML_PATH} -o ${Coverage_NAME} ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
+    COMMAND ${CMAKE_COMMAND} -E remove ${Coverage_NAME}.base ${Coverage_NAME}.total
+            ${PROJECT_BINARY_DIR}/${Coverage_NAME}.info.cleaned
+    WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+    DEPENDS ${Coverage_DEPENDENCIES}
+    COMMENT
+      "Resetting code coverage counters to zero.\nProcessing code coverage counters and generating report."
+  )
+
+  # Show where to find the lcov info report
+  add_custom_command(
+    TARGET ${Coverage_NAME}
+    POST_BUILD
+    COMMAND ;
+    COMMENT "Lcov code coverage info report saved in ${Coverage_NAME}.info.")
+
+  # Show info where to find the report
+  add_custom_command(
+    TARGET ${Coverage_NAME}
+    POST_BUILD
+    COMMAND ;
+    COMMENT "Open ./${Coverage_NAME}/index.html in your browser to view the coverage report.")
 
 endfunction() # SETUP_TARGET_FOR_COVERAGE
 
@@ -197,48 +194,50 @@ endfunction() # SETUP_TARGET_FOR_COVERAGE
 # )
 function(SETUP_TARGET_FOR_COVERAGE_COBERTURA)
 
-    set(options NONE)
-    set(oneValueArgs NAME)
-    set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
-    cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-
-    if(NOT SIMPLE_PYTHON_EXECUTABLE)
-        message(FATAL_ERROR "python not found! Aborting...")
-    endif() # NOT SIMPLE_PYTHON_EXECUTABLE
-
-    if(NOT GCOVR_PATH)
-        message(FATAL_ERROR "gcovr not found! Aborting...")
-    endif() # NOT GCOVR_PATH
-
-    # Combine excludes to several -e arguments
-    set(COBERTURA_EXCLUDES "")
-    foreach(EXCLUDE ${COVERAGE_EXCLUDES})
-        set(COBERTURA_EXCLUDES "-e ${EXCLUDE} ${COBERTURA_EXCLUDES}")
-    endforeach()
-
-    add_custom_target(${Coverage_NAME}
-
-        # Run tests
-        ${Coverage_EXECUTABLE}
-
-        # Running gcovr
-        COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} ${COBERTURA_EXCLUDES}
-            -o ${Coverage_NAME}.xml
-        WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
-        DEPENDS ${Coverage_DEPENDENCIES}
-        COMMENT "Running gcovr to produce Cobertura code coverage report."
-    )
-
-    # Show info where to find the report
-    add_custom_command(TARGET ${Coverage_NAME} POST_BUILD
-        COMMAND ;
-        COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml."
-    )
+  set(options NONE)
+  set(oneValueArgs NAME)
+  set(multiValueArgs EXECUTABLE EXECUTABLE_ARGS DEPENDENCIES)
+  cmake_parse_arguments(Coverage "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+  if(NOT SIMPLE_PYTHON_EXECUTABLE)
+    message(FATAL_ERROR "python not found! Aborting...")
+  endif() # NOT SIMPLE_PYTHON_EXECUTABLE
+
+  if(NOT GCOVR_PATH)
+    message(FATAL_ERROR "gcovr not found! Aborting...")
+  endif() # NOT GCOVR_PATH
+
+  # Combine excludes to several -e arguments
+  set(COBERTURA_EXCLUDES "")
+  foreach(EXCLUDE ${COVERAGE_EXCLUDES})
+    set(COBERTURA_EXCLUDES "-e ${EXCLUDE} ${COBERTURA_EXCLUDES}")
+  endforeach()
+
+  add_custom_target(
+    ${Coverage_NAME}
+    # Run tests
+    ${Coverage_EXECUTABLE}
+    # Running gcovr
+    COMMAND ${GCOVR_PATH} -x -r ${CMAKE_SOURCE_DIR} ${COBERTURA_EXCLUDES} -o ${Coverage_NAME}.xml
+    WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
+    DEPENDS ${Coverage_DEPENDENCIES}
+    COMMENT "Running gcovr to produce Cobertura code coverage report.")
+
+  # Show info where to find the report
+  add_custom_command(
+    TARGET ${Coverage_NAME}
+    POST_BUILD
+    COMMAND ;
+    COMMENT "Cobertura code coverage report saved in ${Coverage_NAME}.xml.")
 
 endfunction() # SETUP_TARGET_FOR_COVERAGE_COBERTURA
 
 function(APPEND_COVERAGE_COMPILER_FLAGS)
-    set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
-    set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}" PARENT_SCOPE)
-    message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
+  set(CMAKE_C_FLAGS
+      "${CMAKE_C_FLAGS} ${COVERAGE_COMPILER_FLAGS}"
+      PARENT_SCOPE)
+  set(CMAKE_CXX_FLAGS
+      "${CMAKE_CXX_FLAGS} ${COVERAGE_COMPILER_FLAGS}"
+      PARENT_SCOPE)
+  message(STATUS "Appending code coverage compiler flags: ${COVERAGE_COMPILER_FLAGS}")
 endfunction() # APPEND_COVERAGE_COMPILER_FLAGS
diff --git a/packages/CLI11/docs/CMakeLists.txt b/packages/CLI11/docs/CMakeLists.txt
index f98ff35b684697c065c18bd0b56ed64fbab462ec..e7ceb1d399c8c5ceb0943040078f07f96ac95a9c 100644
--- a/packages/CLI11/docs/CMakeLists.txt
+++ b/packages/CLI11/docs/CMakeLists.txt
@@ -2,17 +2,10 @@ set(DOXYGEN_EXTRACT_ALL YES)
 set(DOXYGEN_BUILTIN_STL_SUPPORT YES)
 set(PROJECT_BRIEF "C++11 Command Line Interface Parser")
 
-file(GLOB DOC_LIST
-    RELATIVE "${PROJECT_SOURCE_DIR}/include"
-    "${PROJECT_SOURCE_DIR}/include/CLI/*.hpp"
-    )
-
-doxygen_add_docs(docs
-    ${DOC_LIST}
-    "${CMAKE_CURRENT_SOURCE_DIR}/mainpage.md"
-    WORKING_DIRECTORY
-    "${PROJECT_SOURCE_DIR}/include"
-)
-
-
+file(
+  GLOB DOC_LIST
+  RELATIVE "${PROJECT_SOURCE_DIR}/include"
+  "${PROJECT_SOURCE_DIR}/include/CLI/*.hpp")
 
+doxygen_add_docs(docs ${DOC_LIST} "${CMAKE_CURRENT_SOURCE_DIR}/mainpage.md"
+                 WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}/include")
diff --git a/packages/CLI11/docs/Doxyfile b/packages/CLI11/docs/Doxyfile
index d08a2902e11ab318fce026f068dfb412780554f0..c86b0ba7283129aaa4916f896a34f44d07e0967c 100644
--- a/packages/CLI11/docs/Doxyfile
+++ b/packages/CLI11/docs/Doxyfile
@@ -1325,7 +1325,7 @@ CHM_FILE               =
 HHC_LOCATION           =
 
 # The GENERATE_CHI flag controls if a separate .chi index file is generated
-# (YES) or that it should be included in the master .chm file (NO).
+# (YES) or that it should be included in the main .chm file (NO).
 # The default value is: NO.
 # This tag requires that the tag GENERATE_HTMLHELP is set to YES.
 
diff --git a/packages/CLI11/docs/mainpage.md b/packages/CLI11/docs/mainpage.md
index b9e2d33f831c9d3754d5a0d6d5ba079a3d0fbdcb..b1f2b396709b9cddda94231d2e0cbc3e59c3f8be 100644
--- a/packages/CLI11/docs/mainpage.md
+++ b/packages/CLI11/docs/mainpage.md
@@ -1,6 +1,6 @@
 # Introduction {#mainpage}
 
-This is the Doxygen API documentation for CLI11 parser. There is a friendly introduction to CLI11 on the [Github page](https://github.com/CLIUtils/CLI11), and [a tutorial series](https://cliutils.github.io/CLI11/book/).
+This is the Doxygen API documentation for CLI11 parser. There is a friendly introduction to CLI11 on the [GitHub page](https://github.com/CLIUtils/CLI11), and [a tutorial series](https://cliutils.github.io/CLI11/book/).
 
 The main classes are:
 
@@ -14,13 +14,9 @@ The main classes are:
 |CLI::Timer     | A timer class, only in CLI/Timer.hpp (not in `CLI11.hpp`) |
 |CLI::AutoTimer | A timer that prints on deletion     |
 
-
 Groups of related topics:
 
 | Name                 | Description                                    |
 |----------------------|------------------------------------------------|
 | @ref error_group     | Errors that can be thrown                      |
 | @ref validator_group | Common validators used in CLI::Option::check() |
-
-
-
diff --git a/packages/CLI11/examples/CMakeLists.txt b/packages/CLI11/examples/CMakeLists.txt
index 8cea1581d17b3dd919f1066e0c9a5f07a7eff756..d0d45c6282ed21995bbcfa0a70bef4d190c1ef53 100644
--- a/packages/CLI11/examples/CMakeLists.txt
+++ b/packages/CLI11/examples/CMakeLists.txt
@@ -1,186 +1,183 @@
-
 function(add_cli_exe T)
-    add_executable(${T} ${ARGN} ${CLI11_headers})
-    target_link_libraries(${T} PUBLIC CLI11)
-    set_property(TARGET ${T} PROPERTY FOLDER "Examples")
-    if(CLI11_FORCE_LIBCXX)
-        set_property(TARGET ${T} APPEND_STRING PROPERTY LINK_FLAGS -stdlib=libc++)
-    endif()
-    if(CLI11_CLANG_TIDY)
-        set_property(TARGET ${T} PROPERTY CXX_CLANG_TIDY "${DO_CLANG_TIDY}")
-    endif()
+  add_executable(${T} ${ARGN} ${CLI11_headers})
+  target_link_libraries(${T} PUBLIC CLI11)
+  set_property(TARGET ${T} PROPERTY FOLDER "Examples")
+  if(CLI11_FORCE_LIBCXX)
+    set_property(
+      TARGET ${T}
+      APPEND_STRING
+      PROPERTY LINK_FLAGS -stdlib=libc++)
+  endif()
+  if(CLI11_CLANG_TIDY)
+    set_property(TARGET ${T} PROPERTY CXX_CLANG_TIDY "${DO_CLANG_TIDY}")
+  endif()
 endfunction()
 
 if(CLI11_BUILD_EXAMPLES_JSON)
-    message(STATUS "Using nlohmann/json")
-    FetchContent_Declare(json
-        URL https://github.com/nlohmann/json/releases/download/v3.7.3/include.zip
-        URL_HASH "SHA256=87b5884741427220d3a33df1363ae0e8b898099fbc59f1c451113f6732891014"
-    )
-
-    FetchContent_GetProperties(json)
-    if (NOT json_POPULATED)
-        FetchContent_Populate(json)
-    endif()
-
-    add_cli_exe(json json.cpp)
-    target_include_directories(json PUBLIC SYSTEM "${json_SOURCE_DIR}/single_include")
-
-    add_test(NAME json_config_out COMMAND json --item 2)
-    set_property(TEST json_config_out PROPERTY PASS_REGULAR_EXPRESSION
-        [[{]]
-        [["item": "2"]]
-        [["simple": false]]
-        [[}]]
-        )
-
-    file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/input.json" [=[{"item":3,"simple":false}]=])
-    add_test(NAME json_config_in COMMAND json --config "${CMAKE_CURRENT_BINARY_DIR}/input.json")
-    set_property(TEST json_config_in PROPERTY PASS_REGULAR_EXPRESSION
-        [[{]]
-        [["item": "3"]]
-        [["simple": false]]
-        [[}]]
-)
+  message(STATUS "Using nlohmann/json")
+  FetchContent_Declare(
+    json
+    URL https://github.com/nlohmann/json/releases/download/v3.7.3/include.zip
+    URL_HASH "SHA256=87b5884741427220d3a33df1363ae0e8b898099fbc59f1c451113f6732891014")
+
+  FetchContent_GetProperties(json)
+  if(NOT json_POPULATED)
+    FetchContent_Populate(json)
+  endif()
+
+  add_cli_exe(json json.cpp)
+  target_include_directories(json PUBLIC SYSTEM "${json_SOURCE_DIR}/single_include")
+
+  add_test(NAME json_config_out COMMAND json --item 2)
+  set_property(TEST json_config_out PROPERTY PASS_REGULAR_EXPRESSION [[{]] [["item": "2"]]
+                                             [["simple": false]] [[}]])
+
+  file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/input.json" [=[{"item":3,"simple":false}]=])
+  add_test(NAME json_config_in COMMAND json --config "${CMAKE_CURRENT_BINARY_DIR}/input.json")
+  set_property(TEST json_config_in PROPERTY PASS_REGULAR_EXPRESSION [[{]] [["item": "3"]]
+                                            [["simple": false]] [[}]])
 endif()
 
-
 add_cli_exe(simple simple.cpp)
 add_test(NAME simple_basic COMMAND simple)
 add_test(NAME simple_all COMMAND simple -f filename.txt -c 12 --flag --flag -d 1.2)
-set_property(TEST simple_all PROPERTY PASS_REGULAR_EXPRESSION
-    "Working on file: filename.txt, direct count: 1, opt count: 1"
-    "Working on count: 12, direct count: 1, opt count: 1"
-    "Received flag: 2 (2) times"
-    "Some value: 1.2")
+set_property(
+  TEST simple_all
+  PROPERTY PASS_REGULAR_EXPRESSION "Working on file: filename.txt, direct count: 1, opt count: 1"
+           "Working on count: 12, direct count: 1, opt count: 1" "Received flag: 2 (2) times"
+           "Some value: 1.2")
 
 add_test(NAME simple_version COMMAND simple --version)
-set_property(TEST simple_version PROPERTY PASS_REGULAR_EXPRESSION
-    "${CLI11_VERSION}")
+set_property(TEST simple_version PROPERTY PASS_REGULAR_EXPRESSION "${CLI11_VERSION}")
 
 add_cli_exe(subcommands subcommands.cpp)
 add_test(NAME subcommands_none COMMAND subcommands)
-set_property(TEST subcommands_none PROPERTY
-    PASS_REGULAR_EXPRESSION "A subcommand is required")
-add_test(NAME subcommands_all COMMAND subcommands --random start --file name stop --count)
-set_property(TEST subcommands_all PROPERTY PASS_REGULAR_EXPRESSION
-    "Working on --file from start: name"
-    "Working on --count from stop: 1, direct count: 1"
-    "Count of --random flag: 1"
-    "Subcommand: start"
-    "Subcommand: stop")
+set_property(TEST subcommands_none PROPERTY PASS_REGULAR_EXPRESSION "A subcommand is required")
+add_test(
+  NAME subcommands_all
+  COMMAND subcommands --random start --file
+  name stop --count)
+set_property(
+  TEST subcommands_all
+  PROPERTY PASS_REGULAR_EXPRESSION "Working on --file from start: name"
+           "Working on --count from stop: 1, direct count: 1" "Count of --random flag: 1"
+           "Subcommand: start" "Subcommand: stop")
 
 add_cli_exe(subcom_partitioned subcom_partitioned.cpp)
 add_test(NAME subcom_partitioned_none COMMAND subcom_partitioned)
-set_property(TEST subcom_partitioned_none PROPERTY PASS_REGULAR_EXPRESSION
-    "This is a timer:"
-    "--file is required"
-    "Run with --help for more information.")
+set_property(
+  TEST subcom_partitioned_none
+  PROPERTY PASS_REGULAR_EXPRESSION "This is a timer:" "--file is required"
+           "Run with --help for more information.")
 
 add_test(NAME subcom_partitioned_all COMMAND subcom_partitioned --file this --count --count -d 1.2)
-set_property(TEST subcom_partitioned_all PROPERTY PASS_REGULAR_EXPRESSION
-    "This is a timer:"
-    "Working on file: this, direct count: 1, opt count: 1"
-    "Working on count: 2, direct count: 2, opt count: 2"
-    "Some value: 1.2")
-    # test shows that the help prints out for unnamed subcommands
+set_property(
+  TEST subcom_partitioned_all
+  PROPERTY PASS_REGULAR_EXPRESSION "This is a timer:"
+           "Working on file: this, direct count: 1, opt count: 1"
+           "Working on count: 2, direct count: 2, opt count: 2" "Some value: 1.2")
+# test shows that the help prints out for unnamed subcommands
 add_test(NAME subcom_partitioned_help COMMAND subcom_partitioned --help)
 set_property(TEST subcom_partitioned_help PROPERTY PASS_REGULAR_EXPRESSION
-    "-f,--file TEXT REQUIRED"
-    "-d,--double FLOAT")
+                                                   "-f,--file TEXT REQUIRED" "-d,--double FLOAT")
+
+####################################################
+add_cli_exe(config_app config_app.cpp)
+add_test(NAME config_app1 COMMAND config_app -p)
+set_property(TEST config_app1 PROPERTY PASS_REGULAR_EXPRESSION "file=")
+
+add_test(NAME config_app2 COMMAND config_app -p -f /)
+set_property(TEST config_app2 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"")
+
+add_test(NAME config_app3 COMMAND config_app -f "" -p)
+set_property(TEST config_app3 PROPERTY PASS_REGULAR_EXPRESSION "file=\"\"")
+
+add_test(NAME config_app4 COMMAND config_app -f "/" -p)
+set_property(TEST config_app4 PROPERTY PASS_REGULAR_EXPRESSION "file=\"/\"")
+
+####################################################
 
 add_cli_exe(option_groups option_groups.cpp)
-add_test(NAME option_groups_missing COMMAND option_groups )
-set_property(TEST option_groups_missing PROPERTY PASS_REGULAR_EXPRESSION
-    "Exactly 1 option from"
-    "is required")
+add_test(NAME option_groups_missing COMMAND option_groups)
+set_property(TEST option_groups_missing PROPERTY PASS_REGULAR_EXPRESSION "Exactly 1 option from"
+                                                 "is required")
 add_test(NAME option_groups_extra COMMAND option_groups --csv --binary)
-set_property(TEST option_groups_extra PROPERTY PASS_REGULAR_EXPRESSION
-    "and 2 were given")
-add_test(NAME option_groups_extra2 COMMAND option_groups --csv --address "192.168.1.1" -o "test.out")
-set_property(TEST option_groups_extra2 PROPERTY PASS_REGULAR_EXPRESSION
-    "at most 1")
+set_property(TEST option_groups_extra PROPERTY PASS_REGULAR_EXPRESSION "and 2 were given")
+add_test(NAME option_groups_extra2 COMMAND option_groups --csv --address "192.168.1.1" -o
+                                           "test.out")
+set_property(TEST option_groups_extra2 PROPERTY PASS_REGULAR_EXPRESSION "at most 1")
 
 add_cli_exe(positional_arity positional_arity.cpp)
-add_test(NAME positional_arity1 COMMAND positional_arity one )
-set_property(TEST positional_arity1 PROPERTY PASS_REGULAR_EXPRESSION
-    "File 1 = one")
-add_test(NAME positional_arity2 COMMAND positional_arity one two )
-set_property(TEST positional_arity2 PROPERTY PASS_REGULAR_EXPRESSION
-    "File 1 = one"
-    "File 2 = two")
+add_test(NAME positional_arity1 COMMAND positional_arity one)
+set_property(TEST positional_arity1 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
+add_test(NAME positional_arity2 COMMAND positional_arity one two)
+set_property(TEST positional_arity2 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one" "File 2 = two")
 add_test(NAME positional_arity3 COMMAND positional_arity 1 2 one)
-set_property(TEST positional_arity3 PROPERTY PASS_REGULAR_EXPRESSION
-    "File 1 = one")
+set_property(TEST positional_arity3 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
 add_test(NAME positional_arity_fail COMMAND positional_arity 1 one two)
-set_property(TEST positional_arity_fail PROPERTY PASS_REGULAR_EXPRESSION
-    "Could not convert")
-
-    add_cli_exe(positional_validation positional_validation.cpp)
-add_test(NAME positional_validation1 COMMAND positional_validation one )
-set_property(TEST positional_validation1 PROPERTY PASS_REGULAR_EXPRESSION
-    "File 1 = one")
-add_test(NAME positional_validation2 COMMAND positional_validation one 1 2 two )
-set_property(TEST positional_validation2 PROPERTY PASS_REGULAR_EXPRESSION
-    "File 1 = one"
-    "File 2 = two")
+set_property(TEST positional_arity_fail PROPERTY PASS_REGULAR_EXPRESSION "Could not convert")
+
+add_cli_exe(positional_validation positional_validation.cpp)
+add_test(NAME positional_validation1 COMMAND positional_validation one)
+set_property(TEST positional_validation1 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
+add_test(NAME positional_validation2 COMMAND positional_validation one 1 2 two)
+set_property(TEST positional_validation2 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one"
+                                                  "File 2 = two")
 add_test(NAME positional_validation3 COMMAND positional_validation 1 2 one)
-set_property(TEST positional_validation3 PROPERTY PASS_REGULAR_EXPRESSION
-    "File 1 = one")
+set_property(TEST positional_validation3 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one")
 add_test(NAME positional_validation4 COMMAND positional_validation 1 one two 2)
-set_property(TEST positional_validation4 PROPERTY PASS_REGULAR_EXPRESSION
-     "File 1 = one"
-    "File 2 = two")
+set_property(TEST positional_validation4 PROPERTY PASS_REGULAR_EXPRESSION "File 1 = one"
+                                                  "File 2 = two")
 
 add_cli_exe(shapes shapes.cpp)
-add_test(NAME shapes_all COMMAND shapes circle 4.4 circle 10.7 rectangle 4 4 circle 2.3 triangle 4.5 ++ rectangle 2.1 ++ circle 234.675)
-set_property(TEST shapes_all PROPERTY PASS_REGULAR_EXPRESSION
-    "circle2"
-    "circle4"
-    "rectangle2 with edges [2.1,2.1]"
-    "triangel1 with sides [4.5]")
+add_test(NAME shapes_all COMMAND shapes circle 4.4 circle 10.7 rectangle 4 4 circle 2.3 triangle
+                                 4.5 ++ rectangle 2.1 ++ circle 234.675)
+set_property(
+  TEST shapes_all PROPERTY PASS_REGULAR_EXPRESSION "circle2" "circle4"
+                           "rectangle2 with edges [2.1,2.1]" "triangel1 with sides [4.5]")
 
 add_cli_exe(ranges ranges.cpp)
 add_test(NAME ranges_range COMMAND ranges --range 1 2 3)
-set_property(TEST ranges_range PROPERTY PASS_REGULAR_EXPRESSION
-    "[2:1:3]")
+set_property(TEST ranges_range PROPERTY PASS_REGULAR_EXPRESSION "[2:1:3]")
 add_test(NAME ranges_minmax COMMAND ranges --min 2 --max 3)
-set_property(TEST ranges_minmax PROPERTY PASS_REGULAR_EXPRESSION
-    "[2:1:3]")
+set_property(TEST ranges_minmax PROPERTY PASS_REGULAR_EXPRESSION "[2:1:3]")
 add_test(NAME ranges_error COMMAND ranges --min 2 --max 3 --step 1 --range 1 2 3)
-set_property(TEST ranges_error PROPERTY PASS_REGULAR_EXPRESSION
-    "Exactly 1 option from")
+set_property(TEST ranges_error PROPERTY PASS_REGULAR_EXPRESSION "Exactly 1 option from")
 
 add_cli_exe(validators validators.cpp)
 add_test(NAME validators_help COMMAND validators --help)
-set_property(TEST validators_help PROPERTY PASS_REGULAR_EXPRESSION
-    "  -f,--file TEXT:FILE[\\r\\n\\t ]+File name"
-    "  -v,--value INT:INT in [3 - 6][\\r\\n\\t ]+Value in range")
+set_property(
+  TEST validators_help
+  PROPERTY PASS_REGULAR_EXPRESSION "  -f,--file TEXT:FILE[\\r\\n\\t ]+File name"
+           "  -v,--value INT:INT in [3 - 6][\\r\\n\\t ]+Value in range")
 add_test(NAME validators_file COMMAND validators --file nonex.xxx)
-set_property(TEST validators_file PROPERTY PASS_REGULAR_EXPRESSION
-    "--file: File does not exist: nonex.xxx"
-    "Run with --help for more information.")
+set_property(
+  TEST validators_file PROPERTY PASS_REGULAR_EXPRESSION "--file: File does not exist: nonex.xxx"
+                                "Run with --help for more information.")
 add_test(NAME validators_plain COMMAND validators --value 9)
-set_property(TEST validators_plain PROPERTY PASS_REGULAR_EXPRESSION
-    "--value: Value 9 not in range 3 to 6"
-    "Run with --help for more information.")
+set_property(
+  TEST validators_plain PROPERTY PASS_REGULAR_EXPRESSION "--value: Value 9 not in range 3 to 6"
+                                 "Run with --help for more information.")
 
 add_cli_exe(groups groups.cpp)
 add_test(NAME groups_none COMMAND groups)
-set_property(TEST groups_none PROPERTY PASS_REGULAR_EXPRESSION
-    "This is a timer:"
-    "--file is required"
-    "Run with --help for more information.")
+set_property(
+  TEST groups_none PROPERTY PASS_REGULAR_EXPRESSION "This is a timer:" "--file is required"
+                            "Run with --help for more information.")
 add_test(NAME groups_all COMMAND groups --file this --count --count -d 1.2)
-set_property(TEST groups_all PROPERTY PASS_REGULAR_EXPRESSION
-    "This is a timer:"
-    "Working on file: this, direct count: 1, opt count: 1"
-    "Working on count: 2, direct count: 2, opt count: 2"
-    "Some value: 1.2")
+set_property(
+  TEST groups_all
+  PROPERTY PASS_REGULAR_EXPRESSION "This is a timer:"
+           "Working on file: this, direct count: 1, opt count: 1"
+           "Working on count: 2, direct count: 2, opt count: 2" "Some value: 1.2")
 
 add_cli_exe(inter_argument_order inter_argument_order.cpp)
-add_test(NAME inter_argument_order COMMAND inter_argument_order --foo 1 2 3 --x --bar 4 5 6 --z --foo 7 8)
-set_property(TEST inter_argument_order PROPERTY PASS_REGULAR_EXPRESSION
+add_test(NAME inter_argument_order COMMAND inter_argument_order --foo 1 2 3 --x --bar 4 5 6 --z
+                                           --foo 7 8)
+set_property(
+  TEST inter_argument_order
+  PROPERTY
+    PASS_REGULAR_EXPRESSION
     [=[foo : 1
 foo : 2
 foo : 3
@@ -192,44 +189,37 @@ foo : 8]=])
 
 add_cli_exe(prefix_command prefix_command.cpp)
 add_test(NAME prefix_command COMMAND prefix_command -v 3 2 1 -- other one two 3)
-set_property(TEST prefix_command PROPERTY PASS_REGULAR_EXPRESSION
-    "Prefix: 3 : 2 : 1"
-    "Remaining commands: other one two 3")
+set_property(TEST prefix_command PROPERTY PASS_REGULAR_EXPRESSION "Prefix: 3 : 2 : 1"
+                                          "Remaining commands: other one two 3")
 
 add_cli_exe(callback_passthrough callback_passthrough.cpp)
 add_test(NAME callback_passthrough1 COMMAND callback_passthrough --argname t2 --t2 test)
-set_property(TEST callback_passthrough1 PROPERTY PASS_REGULAR_EXPRESSION
-    "the value is now test")
+set_property(TEST callback_passthrough1 PROPERTY PASS_REGULAR_EXPRESSION "the value is now test")
 add_test(NAME callback_passthrough2 COMMAND callback_passthrough --arg EEEK --argname arg)
-set_property(TEST callback_passthrough2 PROPERTY PASS_REGULAR_EXPRESSION
-    "the value is now EEEK")
+set_property(TEST callback_passthrough2 PROPERTY PASS_REGULAR_EXPRESSION "the value is now EEEK")
 
 add_cli_exe(enum enum.cpp)
 add_test(NAME enum_pass COMMAND enum -l 1)
 add_test(NAME enum_fail COMMAND enum -l 4)
-set_property(TEST enum_fail PROPERTY PASS_REGULAR_EXPRESSION
-    "--level: Check 4 value in {" "FAILED")
+set_property(TEST enum_fail PROPERTY PASS_REGULAR_EXPRESSION "--level: Check 4 value in {"
+                                     "FAILED")
 
 add_cli_exe(enum_ostream enum_ostream.cpp)
 add_test(NAME enum_ostream_pass COMMAND enum_ostream --level medium)
-set_property(TEST enum_ostream_pass PROPERTY PASS_REGULAR_EXPRESSION
-    "Enum received: Medium")
+set_property(TEST enum_ostream_pass PROPERTY PASS_REGULAR_EXPRESSION "Enum received: Medium")
 
 add_cli_exe(digit_args digit_args.cpp)
 add_test(NAME digit_args COMMAND digit_args -h)
-set_property(TEST digit_args PROPERTY PASS_REGULAR_EXPRESSION
-    "-3{3}")
+set_property(TEST digit_args PROPERTY PASS_REGULAR_EXPRESSION "-3{3}")
 
 add_cli_exe(modhelp modhelp.cpp)
 add_test(NAME modhelp COMMAND modhelp -a test -h)
-set_property(TEST modhelp PROPERTY PASS_REGULAR_EXPRESSION
-    "Option -a string in help: test")
+set_property(TEST modhelp PROPERTY PASS_REGULAR_EXPRESSION "Option -a string in help: test")
 
 add_subdirectory(subcom_in_files)
 add_test(NAME subcom_in_files COMMAND subcommand_main subcommand_a -f this.txt --with-foo)
-set_property(TEST subcom_in_files PROPERTY PASS_REGULAR_EXPRESSION
-    "Working on file: this\.txt"
-    "Using foo!")
+set_property(TEST subcom_in_files PROPERTY PASS_REGULAR_EXPRESSION "Working on file: this\.txt"
+                                           "Using foo!")
 
 add_cli_exe(formatter formatter.cpp)
 
@@ -245,14 +235,15 @@ add_test(NAME retired_retired_test2 COMMAND retired --retired_option 567)
 add_test(NAME retired_retired_test3 COMMAND retired --retired_option2 567 689 789)
 add_test(NAME retired_deprecated COMMAND retired --deprecate 19 20)
 
-set_property(TEST retired_retired_test PROPERTY PASS_REGULAR_EXPRESSION
-    "WARNING.*retired")
+set_property(TEST retired_retired_test PROPERTY PASS_REGULAR_EXPRESSION "WARNING.*retired")
+
+set_property(TEST retired_retired_test2 PROPERTY PASS_REGULAR_EXPRESSION "WARNING.*retired")
 
-set_property(TEST retired_retired_test2 PROPERTY PASS_REGULAR_EXPRESSION
-    "WARNING.*retired")
+set_property(TEST retired_retired_test3 PROPERTY PASS_REGULAR_EXPRESSION "WARNING.*retired")
 
-set_property(TEST retired_retired_test3 PROPERTY PASS_REGULAR_EXPRESSION
-    "WARNING.*retired")
+set_property(TEST retired_deprecated PROPERTY PASS_REGULAR_EXPRESSION "deprecated.*not_deprecated")
 
-set_property(TEST retired_deprecated PROPERTY PASS_REGULAR_EXPRESSION
-    "deprecated.*not_deprecated")
+#--------------------------------------------
+add_cli_exe(custom_parse custom_parse.cpp)
+add_test(NAME cp_test COMMAND custom_parse --dv 1.7)
+set_property(TEST cp_test PROPERTY PASS_REGULAR_EXPRESSION "called correct")
diff --git a/packages/CLI11/examples/callback_passthrough.cpp b/packages/CLI11/examples/callback_passthrough.cpp
index 16698aafb9a3048e2b124d8726a0604a225a44f6..48a248756f49558e726d656a0451f1005a985253 100644
--- a/packages/CLI11/examples/callback_passthrough.cpp
+++ b/packages/CLI11/examples/callback_passthrough.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/config_app.cpp b/packages/CLI11/examples/config_app.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aec9fefdfaf80875dc71fc62a2701ca62ff384b4
--- /dev/null
+++ b/packages/CLI11/examples/config_app.cpp
@@ -0,0 +1,50 @@
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <string>
+
+int main(int argc, char **argv) {
+
+    CLI::App app("configuration print example");
+
+    app.add_flag("-p,--print", "Print configuration and exit")->configurable(false);  // NEW: print flag
+
+    std::string file;
+    CLI::Option *opt = app.add_option("-f,--file,file", file, "File name")
+                           ->capture_default_str()
+                           ->run_callback_for_default();  // NEW: capture_default_str()
+
+    int count{0};
+    CLI::Option *copt =
+        app.add_option("-c,--count", count, "Counter")->capture_default_str();  // NEW: capture_default_str()
+
+    int v{0};
+    CLI::Option *flag = app.add_flag("--flag", v, "Some flag that can be passed multiple times")
+                            ->capture_default_str();  // NEW: capture_default_str()
+
+    double value{0.0};                                                          // = 3.14;
+    app.add_option("-d,--double", value, "Some Value")->capture_default_str();  // NEW: capture_default_str()
+
+    app.get_config_formatter_base()->quoteCharacter('"', '"');
+
+    CLI11_PARSE(app, argc, argv);
+
+    if(app.get_option("--print")->as<bool>()) {  // NEW: print configuration and exit
+        std::cout << app.config_to_str(true, false);
+        return 0;
+    }
+
+    std::cout << "Working on file: " << file << ", direct count: " << app.count("--file")
+              << ", opt count: " << opt->count() << std::endl;
+    std::cout << "Working on count: " << count << ", direct count: " << app.count("--count")
+              << ", opt count: " << copt->count() << std::endl;
+    std::cout << "Received flag: " << v << " (" << flag->count() << ") times\n";
+    std::cout << "Some value: " << value << std::endl;
+
+    return 0;
+}
diff --git a/packages/CLI11/examples/custom_parse.cpp b/packages/CLI11/examples/custom_parse.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..44fc77afd1cf91379a1f73f0868a9f23eaa17260
--- /dev/null
+++ b/packages/CLI11/examples/custom_parse.cpp
@@ -0,0 +1,39 @@
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+// from Issue #566 on github https://github.com/CLIUtils/CLI11/issues/566
+
+#include <CLI/CLI.hpp>
+#include <iostream>
+#include <sstream>
+
+// example file to demonstrate a custom lexical cast function
+
+template <class T = int> struct Values {
+    T a;
+    T b;
+    T c;
+};
+
+// in C++20 this is constructible from a double due to the new aggregate initialization in C++20.
+using DoubleValues = Values<double>;
+
+// the lexical cast operator should be in the same namespace as the type for ADL to work properly
+bool lexical_cast(const std::string &input, Values<double> &v) {
+    std::cout << "called correct lexical_cast function ! val: " << input << std::endl;
+    return true;
+}
+
+DoubleValues doubles;
+void argparse(CLI::Option_group *group) { group->add_option("--dv", doubles)->default_str("0"); }
+
+int main(int argc, char **argv) {
+    CLI::App app;
+
+    argparse(app.add_option_group("param"));
+    CLI11_PARSE(app, argc, argv);
+    return 0;
+}
diff --git a/packages/CLI11/examples/digit_args.cpp b/packages/CLI11/examples/digit_args.cpp
index cf8f7d3317da7864481b031e4ff7fff37744e07f..af0891a4f81ff41f0c73db3159edda088f41106d 100644
--- a/packages/CLI11/examples/digit_args.cpp
+++ b/packages/CLI11/examples/digit_args.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/enum.cpp b/packages/CLI11/examples/enum.cpp
index a73e56b3f847b719f8f4ddfb979c849e29b82468..09b7f043b6f53edf65fb2e590b7bf79b4bb7b2c8 100644
--- a/packages/CLI11/examples/enum.cpp
+++ b/packages/CLI11/examples/enum.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/enum_ostream.cpp b/packages/CLI11/examples/enum_ostream.cpp
index 13d33502aea4a994fc2d08b2ab79f0192d296ad0..4e3e6b297f5a4b624af1d6ccfb5d2bbac22fd530 100644
--- a/packages/CLI11/examples/enum_ostream.cpp
+++ b/packages/CLI11/examples/enum_ostream.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/formatter.cpp b/packages/CLI11/examples/formatter.cpp
index 81d6209f4866def06a5e300fae3dd1051791bd46..3b2e3dce5447694d285d2d99441c363d03912e0b 100644
--- a/packages/CLI11/examples/formatter.cpp
+++ b/packages/CLI11/examples/formatter.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/groups.cpp b/packages/CLI11/examples/groups.cpp
index 22d628948ecd5a6fb01871cb9217b30d8a136c99..c766d6b23bef6806eadc94e4623aa2202c3e0090 100644
--- a/packages/CLI11/examples/groups.cpp
+++ b/packages/CLI11/examples/groups.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/inter_argument_order.cpp b/packages/CLI11/examples/inter_argument_order.cpp
index deecd057e699529ab0e7c7c35134778aa6b1bf52..ebee3977ac1d26a896805ef24620dcf458e82bba 100644
--- a/packages/CLI11/examples/inter_argument_order.cpp
+++ b/packages/CLI11/examples/inter_argument_order.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/json.cpp b/packages/CLI11/examples/json.cpp
index d5fe35a700da611a12e1f8bccd77d0c7ded229f5..ae2595c004736353724f9f4310a39e1c26496d38 100644
--- a/packages/CLI11/examples/json.cpp
+++ b/packages/CLI11/examples/json.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/modhelp.cpp b/packages/CLI11/examples/modhelp.cpp
index 706abcae0c3f319c8218a98f6592ba0f90b7f85c..9c674300f8b7e55f050f85fb74bd0d4fbb7c009a 100644
--- a/packages/CLI11/examples/modhelp.cpp
+++ b/packages/CLI11/examples/modhelp.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/nested.cpp b/packages/CLI11/examples/nested.cpp
index 23f428514568e02b9ee24fe0b1a316aabf48677a..dc8323fe00b7e9c30ea777349abb93e969c19696 100644
--- a/packages/CLI11/examples/nested.cpp
+++ b/packages/CLI11/examples/nested.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -18,7 +18,9 @@ int main(int argc, char **argv) {
 
     std::string mvcamera_config_file = "mvcamera_config.json";
     CLI::App *mvcameraApp = cameraApp->add_subcommand("mvcamera", "MatrixVision Camera Configuration");
-    mvcameraApp->add_option("-c,--config", mvcamera_config_file, "Config filename", true)->check(CLI::ExistingFile);
+    mvcameraApp->add_option("-c,--config", mvcamera_config_file, "Config filename")
+        ->capture_default_str()
+        ->check(CLI::ExistingFile);
 
     std::string mock_camera_path;
     CLI::App *mockcameraApp = cameraApp->add_subcommand("mock", "Mock Camera Configuration");
diff --git a/packages/CLI11/examples/option_groups.cpp b/packages/CLI11/examples/option_groups.cpp
index 679cb754b83404c02c325a8073b3b7411f2ef210..acc65740d62cb66f879c9859ee936fb89b602c14 100644
--- a/packages/CLI11/examples/option_groups.cpp
+++ b/packages/CLI11/examples/option_groups.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/positional_arity.cpp b/packages/CLI11/examples/positional_arity.cpp
index eb6639b04856fe90e342254dccc2cb0fd0194076..e9238fc74011a999cfbc072585ac2c0d24fe3150 100644
--- a/packages/CLI11/examples/positional_arity.cpp
+++ b/packages/CLI11/examples/positional_arity.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/positional_validation.cpp b/packages/CLI11/examples/positional_validation.cpp
index 421de25096cc0273cf928a9a15295b9ecd0db33d..afc03fa8042fedf49983f49b952c9490ad3941ae 100644
--- a/packages/CLI11/examples/positional_validation.cpp
+++ b/packages/CLI11/examples/positional_validation.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/prefix_command.cpp b/packages/CLI11/examples/prefix_command.cpp
index 8977adcbdf95459b89be82fbdef0b7b38628164c..2af1a87b3a1457b6a93b8494415662eadd79732b 100644
--- a/packages/CLI11/examples/prefix_command.cpp
+++ b/packages/CLI11/examples/prefix_command.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/ranges.cpp b/packages/CLI11/examples/ranges.cpp
index 085a7ad1bec99d898106efef4595f75d3b2a85ef..6b4c1e3bc35e9471379ce8d6a4a6f71c2e563669 100644
--- a/packages/CLI11/examples/ranges.cpp
+++ b/packages/CLI11/examples/ranges.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -19,7 +19,7 @@ int main(int argc, char **argv) {
     int min{0}, max{0}, step{1};
     ogroup->add_option("--min,-m", min, "The minimum")->required();
     ogroup->add_option("--max,-M", max, "The maximum")->required();
-    ogroup->add_option("--step,-s", step, "The step", true);
+    ogroup->add_option("--step,-s", step, "The step")->capture_default_str();
 
     app.require_option(1);
 
diff --git a/packages/CLI11/examples/retired.cpp b/packages/CLI11/examples/retired.cpp
index d658e754f5474ef08f23153bb759c602003832d4..3a18db4aed5aca16c32d6d85f2254700710ea161 100644
--- a/packages/CLI11/examples/retired.cpp
+++ b/packages/CLI11/examples/retired.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/shapes.cpp b/packages/CLI11/examples/shapes.cpp
index 880441b85dc729657392a2e6efe485218cd9c9b9..b318df0e152b23815027c4e904e92b8b6468e9b5 100644
--- a/packages/CLI11/examples/shapes.cpp
+++ b/packages/CLI11/examples/shapes.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/simple.cpp b/packages/CLI11/examples/simple.cpp
index c1354eaf38189f5f87c53ad4565d86d048082fef..0f904420f267b37dbbee4fbee517c94eedce3d59 100644
--- a/packages/CLI11/examples/simple.cpp
+++ b/packages/CLI11/examples/simple.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/subcom_help.cpp b/packages/CLI11/examples/subcom_help.cpp
index d41d4a59b93a798f995f9b39a21788861a57981c..89af131dabc4776fdafa302c266fb703f74a028d 100644
--- a/packages/CLI11/examples/subcom_help.cpp
+++ b/packages/CLI11/examples/subcom_help.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/subcom_in_files/subcommand_a.cpp b/packages/CLI11/examples/subcom_in_files/subcommand_a.cpp
index 63474fdc3f87bf74e482f80000befe02a87f2af7..6b229839bcf38e0fea489e07aa21f52234459240 100644
--- a/packages/CLI11/examples/subcom_in_files/subcommand_a.cpp
+++ b/packages/CLI11/examples/subcom_in_files/subcommand_a.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/subcom_in_files/subcommand_a.hpp b/packages/CLI11/examples/subcom_in_files/subcommand_a.hpp
index 2993739ea8764dbaf2bb16650fd847e2f719609b..116160c53059d53d393a4b0ef696521614224732 100644
--- a/packages/CLI11/examples/subcom_in_files/subcommand_a.hpp
+++ b/packages/CLI11/examples/subcom_in_files/subcommand_a.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/subcom_in_files/subcommand_main.cpp b/packages/CLI11/examples/subcom_in_files/subcommand_main.cpp
index 76ebe7c01e2ca0410d7bf178de8c467106b01b8f..62b63806edb990c58d408ed718d2df550b005729 100644
--- a/packages/CLI11/examples/subcom_in_files/subcommand_main.cpp
+++ b/packages/CLI11/examples/subcom_in_files/subcommand_main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/subcom_partitioned.cpp b/packages/CLI11/examples/subcom_partitioned.cpp
index 20dd362326901686e0263244ee7e887ce0225384..df48e2d7918266100733563d0c6be28d3bd40710 100644
--- a/packages/CLI11/examples/subcom_partitioned.cpp
+++ b/packages/CLI11/examples/subcom_partitioned.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/subcommands.cpp b/packages/CLI11/examples/subcommands.cpp
index 937eaeaf41ad0fd3026aa938d5dea617451dff20..68f163a75b3dd0e8ba0aeea8a09b9e4432952985 100644
--- a/packages/CLI11/examples/subcommands.cpp
+++ b/packages/CLI11/examples/subcommands.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/examples/validators.cpp b/packages/CLI11/examples/validators.cpp
index 05ea045c0a54e11e47c4b15e9ac94337bb1e479a..6ca8d3896a9dd3f8b5e511aa2be3078c893d1d30 100644
--- a/packages/CLI11/examples/validators.cpp
+++ b/packages/CLI11/examples/validators.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/include/CLI/App.hpp b/packages/CLI11/include/CLI/App.hpp
index edbe2b7386ba7be718762c034fa2c3d3bf87ce9b..803f0f7f62e384c63745d5904fb1501565e979b8 100644
--- a/packages/CLI11/include/CLI/App.hpp
+++ b/packages/CLI11/include/CLI/App.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -55,7 +55,7 @@ std::string help(const App *app, const Error &e);
 
 /// enumeration of modes of how to deal with extras in config files
 
-enum class config_extras_mode : char { error = 0, ignore, capture };
+enum class config_extras_mode : char { error = 0, ignore, ignore_all, capture };
 
 class App;
 
@@ -113,6 +113,7 @@ class App {
 
     /// This is a function that runs when parsing has finished.
     std::function<void()> parse_complete_callback_{};
+
     /// This is a function that runs when all processing has completed
     std::function<void()> final_callback_{};
 
@@ -367,10 +368,9 @@ class App {
 
     /// Set an alias for the app
     App *alias(std::string app_name) {
-        if(!detail::valid_name_string(app_name)) {
-            throw(IncorrectConstruction("alias is not a valid name string"));
+        if(app_name.empty() || !detail::valid_alias_name_string(app_name)) {
+            throw IncorrectConstruction("Aliases may not be empty or contain newlines or null characters");
         }
-
         if(parent_ != nullptr) {
             aliases_.push_back(app_name);
             auto &res = _compare_subcommand_names(*this, *_get_fallthrough_parent());
@@ -611,14 +611,13 @@ class App {
               enable_if_t<!std::is_const<ConvertTo>::value, detail::enabler> = detail::dummy>
     Option *add_option(std::string option_name,
                        AssignTo &variable,  ///< The variable to set
-                       std::string option_description = "",
-                       bool defaulted = false) {
+                       std::string option_description = "") {
 
         auto fun = [&variable](const CLI::results_t &res) {  // comment for spacing
             return detail::lexical_conversion<AssignTo, ConvertTo>(res, variable);
         };
 
-        Option *opt = add_option(option_name, fun, option_description, defaulted, [&variable]() {
+        Option *opt = add_option(option_name, fun, option_description, false, [&variable]() {
             return CLI::detail::checked_to_string<AssignTo, ConvertTo>(variable);
         });
         opt->type_name(detail::type_name<ConvertTo>());
@@ -720,7 +719,9 @@ class App {
     }
 
     /// Set a version flag and version display string, replace the existing one if present
-    Option *set_version_flag(std::string flag_name = "", const std::string &versionString = "") {
+    Option *set_version_flag(std::string flag_name = "",
+                             const std::string &versionString = "",
+                             const std::string &version_help = "Display program version information and exit") {
         // take flag_description by const reference otherwise add_flag tries to assign to version_description
         if(version_ptr_ != nullptr) {
             remove_option(version_ptr_);
@@ -730,17 +731,16 @@ class App {
         // Empty name will simply remove the version flag
         if(!flag_name.empty()) {
             version_ptr_ = add_flag_callback(
-                flag_name,
-                [versionString]() { throw(CLI::CallForVersion(versionString, 0)); },
-                "Display program version information and exit");
+                flag_name, [versionString]() { throw(CLI::CallForVersion(versionString, 0)); }, version_help);
             version_ptr_->configurable(false);
         }
 
         return version_ptr_;
     }
     /// Generate the version string through a callback function
-    Option *set_version_flag(std::string flag_name, std::function<std::string()> vfunc) {
-        // take flag_description by const reference otherwise add_flag tries to assign to version_description
+    Option *set_version_flag(std::string flag_name,
+                             std::function<std::string()> vfunc,
+                             const std::string &version_help = "Display program version information and exit") {
         if(version_ptr_ != nullptr) {
             remove_option(version_ptr_);
             version_ptr_ = nullptr;
@@ -749,9 +749,7 @@ class App {
         // Empty name will simply remove the version flag
         if(!flag_name.empty()) {
             version_ptr_ = add_flag_callback(
-                flag_name,
-                [vfunc]() { throw(CLI::CallForVersion(vfunc(), 0)); },
-                "Display program version information and exit");
+                flag_name, [vfunc]() { throw(CLI::CallForVersion(vfunc(), 0)); }, version_help);
             version_ptr_->configurable(false);
         }
 
@@ -897,56 +895,6 @@ class App {
     }
 #endif
 
-    /// Add a complex number DEPRECATED --use add_option instead
-    template <typename T, typename XC = double>
-    Option *add_complex(std::string option_name,
-                        T &variable,
-                        std::string option_description = "",
-                        bool defaulted = false,
-                        std::string label = "COMPLEX") {
-
-        CLI::callback_t fun = [&variable](const results_t &res) {
-            XC x, y;
-            bool worked;
-            if(res.size() >= 2 && !res[1].empty()) {
-                auto str1 = res[1];
-                if(str1.back() == 'i' || str1.back() == 'j')
-                    str1.pop_back();
-                worked = detail::lexical_cast(res[0], x) && detail::lexical_cast(str1, y);
-            } else {
-                auto str1 = res.front();
-                auto nloc = str1.find_last_of('-');
-                if(nloc != std::string::npos && nloc > 0) {
-                    worked = detail::lexical_cast(str1.substr(0, nloc), x);
-                    str1 = str1.substr(nloc);
-                    if(str1.back() == 'i' || str1.back() == 'j')
-                        str1.pop_back();
-                    worked = worked && detail::lexical_cast(str1, y);
-                } else {
-                    if(str1.back() == 'i' || str1.back() == 'j') {
-                        str1.pop_back();
-                        worked = detail::lexical_cast(str1, y);
-                        x = XC{0};
-                    } else {
-                        worked = detail::lexical_cast(str1, x);
-                        y = XC{0};
-                    }
-                }
-            }
-            if(worked)
-                variable = T{x, y};
-            return worked;
-        };
-
-        auto default_function = [&variable]() { return CLI::detail::checked_to_string<T, T>(variable); };
-
-        CLI::Option *opt =
-            add_option(option_name, std::move(fun), std::move(option_description), defaulted, default_function);
-
-        opt->type_name(label)->type_size(1, 2)->delimiter('+')->run_callback_for_default();
-        return opt;
-    }
-
     /// Set a configuration ini file option, or clear it if no name passed
     Option *set_config(std::string option_name = "",
                        std::string default_filename = "",
@@ -999,6 +947,9 @@ class App {
     /// creates an option group as part of the given app
     template <typename T = Option_group>
     T *add_option_group(std::string group_name, std::string group_description = "") {
+        if(!detail::valid_alias_name_string(group_name)) {
+            throw IncorrectConstruction("option group names may not contain newlines or null characters");
+        }
         auto option_group = std::make_shared<T>(std::move(group_description), group_name, this);
         auto ptr = option_group.get();
         // move to App_p for overload resolution on older gcc versions
@@ -1014,7 +965,17 @@ class App {
     /// Add a subcommand. Inherits INHERITABLE and OptionDefaults, and help flag
     App *add_subcommand(std::string subcommand_name = "", std::string subcommand_description = "") {
         if(!subcommand_name.empty() && !detail::valid_name_string(subcommand_name)) {
-            throw IncorrectConstruction("subcommand name is not valid");
+            if(!detail::valid_first_char(subcommand_name[0])) {
+                throw IncorrectConstruction(
+                    "Subcommand name starts with invalid character, '!' and '-' are not allowed");
+            }
+            for(auto c : subcommand_name) {
+                if(!detail::valid_later_char(c)) {
+                    throw IncorrectConstruction(std::string("Subcommand name contains invalid character ('") + c +
+                                                "'), all characters are allowed except"
+                                                "'=',':','{','}', and ' '");
+                }
+            }
         }
         CLI::App_p subcom = std::shared_ptr<App>(new App(std::move(subcommand_description), subcommand_name, this));
         return add_subcommand(std::move(subcom));
@@ -1329,6 +1290,16 @@ class App {
         run_callback();
     }
 
+    void parse_from_stream(std::istream &input) {
+        if(parsed_ == 0) {
+            _validate();
+            _configure();
+            // set the parent as nullptr as this object should be the top now
+        }
+
+        _parse_stream(input);
+        run_callback();
+    }
     /// Provide a function to print a help message. The function gets access to the App pointer and error.
     void failure_message(std::function<std::string(const App *, const Error &e)> function) {
         failure_message_ = function;
@@ -1768,10 +1739,10 @@ class App {
     /// Get a pointer to the version option. (const)
     const Option *get_version_ptr() const { return version_ptr_; }
 
-    /// Get the parent of this subcommand (or nullptr if master app)
+    /// Get the parent of this subcommand (or nullptr if main app)
     App *get_parent() { return parent_; }
 
-    /// Get the parent of this subcommand (or nullptr if master app) (const version)
+    /// Get the parent of this subcommand (or nullptr if main app) (const version)
     const App *get_parent() const { return parent_; }
 
     /// Get the name of the current app
@@ -1791,7 +1762,7 @@ class App {
         if(name_.empty()) {
             return std::string("[Option Group: ") + get_group() + "]";
         }
-        if(aliases_.empty() || !with_aliases || aliases_.empty()) {
+        if(aliases_.empty() || !with_aliases) {
             return name_;
         }
         std::string dispname = name_;
@@ -1963,8 +1934,9 @@ class App {
             app->_configure();
         }
     }
+
     /// Internal function to run (App) callback, bottom up
-    void run_callback(bool final_mode = false) {
+    void run_callback(bool final_mode = false, bool suppress_final_callback = false) {
         pre_callback();
         // in the main app if immediate_callback_ is set it runs the main callback before the used subcommands
         if(!final_mode && parse_complete_callback_) {
@@ -1972,18 +1944,18 @@ class App {
         }
         // run the callbacks for the received subcommands
         for(App *subc : get_subcommands()) {
-            subc->run_callback(true);
+            subc->run_callback(true, suppress_final_callback);
         }
         // now run callbacks for option_groups
         for(auto &subc : subcommands_) {
             if(subc->name_.empty() && subc->count_all() > 0) {
-                subc->run_callback(true);
+                subc->run_callback(true, suppress_final_callback);
             }
         }
 
         // finally run the main callback
-        if(final_callback_ && (parsed_ > 0)) {
-            if(!name_.empty() || count_all() > 0) {
+        if(final_callback_ && (parsed_ > 0) && (!suppress_final_callback)) {
+            if(!name_.empty() || count_all() > 0 || parent_ == nullptr) {
                 final_callback_();
             }
         }
@@ -2110,7 +2082,7 @@ class App {
         }
 
         for(const Option_p &opt : options_) {
-            if(opt->count() > 0 && !opt->get_callback_run()) {
+            if((*opt) && !opt->get_callback_run()) {
                 opt->run_callback();
             }
         }
@@ -2277,10 +2249,27 @@ class App {
 
     /// Process callbacks and such.
     void _process() {
-        _process_config_file();
-        _process_env();
+        CLI::FileError fe("ne");
+        bool caught_error{false};
+        try {
+            // the config file might generate a FileError but that should not be processed until later in the process
+            // to allow for help, version and other errors to generate first.
+            _process_config_file();
+            // process env shouldn't throw but no reason to process it if config generated an error
+            _process_env();
+        } catch(const CLI::FileError &fe2) {
+            fe = fe2;
+            caught_error = true;
+        }
+        // callbacks and help_flags can generate exceptions which should take priority over the config file error if one
+        // exists
         _process_callbacks();
         _process_help_flags();
+
+        if(caught_error) {
+            throw CLI::FileError(std::move(fe));
+        }
+
         _process_requirements();
     }
 
@@ -2349,7 +2338,7 @@ class App {
             _process_callbacks();
             _process_help_flags();
             _process_requirements();
-            run_callback();
+            run_callback(false, true);
         }
     }
 
@@ -2370,6 +2359,18 @@ class App {
         _process_extras();
     }
 
+    /// Internal function to parse a stream
+    void _parse_stream(std::istream &input) {
+        auto values = config_formatter_->from_config(input);
+        _parse_config(values);
+        increment_parsed();
+        _trigger_pre_parse(values.size());
+        _process();
+
+        // Throw error if any items are left over (depending on settings)
+        _process_extras();
+    }
+
     /// Parse one config param, return false if not found in any subcommand, remove if it is
     ///
     /// If this has more than one dot.separated.name, go into the subcommand matching it
@@ -2430,8 +2431,12 @@ class App {
             return false;
         }
 
-        if(!op->get_configurable())
+        if(!op->get_configurable()) {
+            if(get_allow_config_extras() == config_extras_mode::ignore_all) {
+                return false;
+            }
             throw ConfigError::NotConfigurable(item.fullname());
+        }
 
         if(op->empty()) {
             // Flag parsing
@@ -2451,7 +2456,7 @@ class App {
     }
 
     /// Parse "one" argument (some may eat more than one), delegate to parent if fails, add to missing if missing
-    /// from master return false if the parse has failed and needs to return to parent
+    /// from main return false if the parse has failed and needs to return to parent
     bool _parse_single(std::vector<std::string> &args, bool &positional_only) {
         bool retval = true;
         detail::Classifier classifier = positional_only ? detail::Classifier::NONE : _recognize(args.back());
@@ -2726,7 +2731,7 @@ class App {
                     }
                 }
             }
-            // If a subcommand, try the master command
+            // If a subcommand, try the main command
             if(parent_ != nullptr && fallthrough_)
                 return _get_fallthrough_parent()->_parse_arg(args, current_type);
             // don't capture missing if this is a nameless subcommand
@@ -2749,6 +2754,9 @@ class App {
                 op->add_result(std::string{});
             }
         }
+        if(op->get_trigger_on_parse() && op->current_option_state_ == Option::option_state::callback_run) {
+            op->clear();
+        }
         int min_num = (std::min)(op->get_type_size_min(), op->get_items_expected_min());
         int max_num = op->get_items_expected_max();
         // check container like options to limit the argument size to a single type if the allow_extra_flags argument is
@@ -2821,7 +2829,9 @@ class App {
         if(min_num > 0 && op->get_type_size_max() != min_num && (collected % op->get_type_size_max()) != 0) {
             op->add_result(std::string{});
         }
-
+        if(op->get_trigger_on_parse()) {
+            op->run_callback();
+        }
         if(!rest.empty()) {
             rest = "-" + rest;
             args.push_back(rest);
@@ -3159,25 +3169,25 @@ struct AppFriend {
 #ifdef CLI11_CPP14
 
     /// Wrap _parse_short, perfectly forward arguments and return
-    template <typename... Args> static decltype(auto) parse_arg(App *app, Args &&... args) {
+    template <typename... Args> static decltype(auto) parse_arg(App *app, Args &&...args) {
         return app->_parse_arg(std::forward<Args>(args)...);
     }
 
     /// Wrap _parse_subcommand, perfectly forward arguments and return
-    template <typename... Args> static decltype(auto) parse_subcommand(App *app, Args &&... args) {
+    template <typename... Args> static decltype(auto) parse_subcommand(App *app, Args &&...args) {
         return app->_parse_subcommand(std::forward<Args>(args)...);
     }
 #else
     /// Wrap _parse_short, perfectly forward arguments and return
     template <typename... Args>
-    static auto parse_arg(App *app, Args &&... args) ->
+    static auto parse_arg(App *app, Args &&...args) ->
         typename std::result_of<decltype (&App::_parse_arg)(App, Args...)>::type {
         return app->_parse_arg(std::forward<Args>(args)...);
     }
 
     /// Wrap _parse_subcommand, perfectly forward arguments and return
     template <typename... Args>
-    static auto parse_subcommand(App *app, Args &&... args) ->
+    static auto parse_subcommand(App *app, Args &&...args) ->
         typename std::result_of<decltype (&App::_parse_subcommand)(App, Args...)>::type {
         return app->_parse_subcommand(std::forward<Args>(args)...);
     }
diff --git a/packages/CLI11/include/CLI/CLI.hpp b/packages/CLI11/include/CLI/CLI.hpp
index 98fca54884b17de40198ea6719b90549eb3c9d61..990ba4078138d69c39170892616d191a77e1d333 100644
--- a/packages/CLI11/include/CLI/CLI.hpp
+++ b/packages/CLI11/include/CLI/CLI.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/include/CLI/Config.hpp b/packages/CLI11/include/CLI/Config.hpp
index 725872dcaf786b5787e8826f78112292f3e1ad5f..57944ecae95d3a54b07b372fbd8179abf416dd01 100644
--- a/packages/CLI11/include/CLI/Config.hpp
+++ b/packages/CLI11/include/CLI/Config.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -23,9 +23,9 @@ namespace CLI {
 // [CLI11:config_hpp:verbatim]
 namespace detail {
 
-inline std::string convert_arg_for_ini(const std::string &arg) {
+inline std::string convert_arg_for_ini(const std::string &arg, char stringQuote = '"', char characterQuote = '\'') {
     if(arg.empty()) {
-        return std::string(2, '"');
+        return std::string(2, stringQuote);
     }
     // some specifically supported strings
     if(arg == "true" || arg == "false" || arg == "nan" || arg == "inf") {
@@ -40,7 +40,7 @@ inline std::string convert_arg_for_ini(const std::string &arg) {
     }
     // just quote a single non numeric character
     if(arg.size() == 1) {
-        return std::string("'") + arg + '\'';
+        return std::string(1, characterQuote) + arg + characterQuote;
     }
     // handle hex, binary or octal arguments
     if(arg.front() == '0') {
@@ -60,16 +60,20 @@ inline std::string convert_arg_for_ini(const std::string &arg) {
             }
         }
     }
-    if(arg.find_first_of('"') == std::string::npos) {
-        return std::string("\"") + arg + '"';
+    if(arg.find_first_of(stringQuote) == std::string::npos) {
+        return std::string(1, stringQuote) + arg + stringQuote;
     } else {
-        return std::string("'") + arg + '\'';
+        return characterQuote + arg + characterQuote;
     }
 }
 
 /// Comma separated join, adds quotes if needed
-inline std::string
-ini_join(const std::vector<std::string> &args, char sepChar = ',', char arrayStart = '[', char arrayEnd = ']') {
+inline std::string ini_join(const std::vector<std::string> &args,
+                            char sepChar = ',',
+                            char arrayStart = '[',
+                            char arrayEnd = ']',
+                            char stringQuote = '"',
+                            char characterQuote = '\'') {
     std::string joined;
     if(args.size() > 1 && arrayStart != '\0') {
         joined.push_back(arrayStart);
@@ -82,7 +86,7 @@ ini_join(const std::vector<std::string> &args, char sepChar = ',', char arraySta
                 joined.push_back(' ');
             }
         }
-        joined.append(convert_arg_for_ini(arg));
+        joined.append(convert_arg_for_ini(arg, stringQuote, characterQuote));
     }
     if(args.size() > 1 && arrayEnd != '\0') {
         joined.push_back(arrayEnd);
@@ -90,17 +94,17 @@ ini_join(const std::vector<std::string> &args, char sepChar = ',', char arraySta
     return joined;
 }
 
-inline std::vector<std::string> generate_parents(const std::string &section, std::string &name) {
+inline std::vector<std::string> generate_parents(const std::string &section, std::string &name, char parentSeparator) {
     std::vector<std::string> parents;
     if(detail::to_lower(section) != "default") {
-        if(section.find('.') != std::string::npos) {
-            parents = detail::split(section, '.');
+        if(section.find(parentSeparator) != std::string::npos) {
+            parents = detail::split(section, parentSeparator);
         } else {
             parents = {section};
         }
     }
-    if(name.find('.') != std::string::npos) {
-        std::vector<std::string> plist = detail::split(name, '.');
+    if(name.find(parentSeparator) != std::string::npos) {
+        std::vector<std::string> plist = detail::split(name, parentSeparator);
         name = plist.back();
         detail::remove_quotes(name);
         plist.pop_back();
@@ -115,10 +119,11 @@ inline std::vector<std::string> generate_parents(const std::string &section, std
 }
 
 /// assuming non default segments do a check on the close and open of the segments in a configItem structure
-inline void checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection) {
+inline void
+checkParentSegments(std::vector<ConfigItem> &output, const std::string &currentSection, char parentSeparator) {
 
     std::string estring;
-    auto parents = detail::generate_parents(currentSection, estring);
+    auto parents = detail::generate_parents(currentSection, estring, parentSeparator);
     if(!output.empty() && output.back().name == "--") {
         std::size_t msize = (parents.size() > 1U) ? parents.size() : 2;
         while(output.back().parents.size() >= msize) {
@@ -166,43 +171,53 @@ inline void checkParentSegments(std::vector<ConfigItem> &output, const std::stri
 
 inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) const {
     std::string line;
-    std::string section = "default";
-
+    std::string currentSection = "default";
+    std::string previousSection = "default";
     std::vector<ConfigItem> output;
     bool isDefaultArray = (arrayStart == '[' && arrayEnd == ']' && arraySeparator == ',');
     bool isINIArray = (arrayStart == '\0' || arrayStart == ' ') && arrayStart == arrayEnd;
+    bool inSection{false};
     char aStart = (isINIArray) ? '[' : arrayStart;
     char aEnd = (isINIArray) ? ']' : arrayEnd;
     char aSep = (isINIArray && arraySeparator == ' ') ? ',' : arraySeparator;
-
+    int currentSectionIndex{0};
     while(getline(input, line)) {
         std::vector<std::string> items_buffer;
         std::string name;
 
         detail::trim(line);
         std::size_t len = line.length();
-        if(len > 1 && line.front() == '[' && line.back() == ']') {
-            if(section != "default") {
+        // lines have to be at least 3 characters to have any meaning to CLI just skip the rest
+        if(len < 3) {
+            continue;
+        }
+        if(line.front() == '[' && line.back() == ']') {
+            if(currentSection != "default") {
                 // insert a section end which is just an empty items_buffer
                 output.emplace_back();
-                output.back().parents = detail::generate_parents(section, name);
+                output.back().parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
                 output.back().name = "--";
             }
-            section = line.substr(1, len - 2);
+            currentSection = line.substr(1, len - 2);
             // deal with double brackets for TOML
-            if(section.size() > 1 && section.front() == '[' && section.back() == ']') {
-                section = section.substr(1, section.size() - 2);
+            if(currentSection.size() > 1 && currentSection.front() == '[' && currentSection.back() == ']') {
+                currentSection = currentSection.substr(1, currentSection.size() - 2);
             }
-            if(detail::to_lower(section) == "default") {
-                section = "default";
+            if(detail::to_lower(currentSection) == "default") {
+                currentSection = "default";
             } else {
-                detail::checkParentSegments(output, section);
+                detail::checkParentSegments(output, currentSection, parentSeparatorChar);
+            }
+            inSection = false;
+            if(currentSection == previousSection) {
+                ++currentSectionIndex;
+            } else {
+                currentSectionIndex = 0;
+                previousSection = currentSection;
             }
             continue;
         }
-        if(len == 0) {
-            continue;
-        }
+
         // comment lines
         if(line.front() == ';' || line.front() == '#' || line.front() == commentChar) {
             continue;
@@ -213,6 +228,11 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
         if(pos != std::string::npos) {
             name = detail::trim_copy(line.substr(0, pos));
             std::string item = detail::trim_copy(line.substr(pos + 1));
+            auto cloc = item.find(commentChar);
+            if(cloc != std::string::npos) {
+                item.erase(cloc, std::string::npos);
+                detail::trim(item);
+            }
             if(item.size() > 1 && item.front() == aStart) {
                 for(std::string multiline; item.back() != aEnd && std::getline(input, multiline);) {
                     detail::trim(multiline);
@@ -228,9 +248,15 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
             }
         } else {
             name = detail::trim_copy(line);
+            auto cloc = name.find(commentChar);
+            if(cloc != std::string::npos) {
+                name.erase(cloc, std::string::npos);
+                detail::trim(name);
+            }
+
             items_buffer = {"true"};
         }
-        if(name.find('.') == std::string::npos) {
+        if(name.find(parentSeparatorChar) == std::string::npos) {
             detail::remove_quotes(name);
         }
         // clean up quotes on the items
@@ -238,8 +264,20 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
             detail::remove_quotes(it);
         }
 
-        std::vector<std::string> parents = detail::generate_parents(section, name);
-
+        std::vector<std::string> parents = detail::generate_parents(currentSection, name, parentSeparatorChar);
+        if(parents.size() > maximumLayers) {
+            continue;
+        }
+        if(!configSection.empty() && !inSection) {
+            if(parents.empty() || parents.front() != configSection) {
+                continue;
+            }
+            if(configIndex >= 0 && currentSectionIndex != configIndex) {
+                continue;
+            }
+            parents.erase(parents.begin());
+            inSection = true;
+        }
         if(!output.empty() && name == output.back().name && parents == output.back().parents) {
             output.back().inputs.insert(output.back().inputs.end(), items_buffer.begin(), items_buffer.end());
         } else {
@@ -249,11 +287,11 @@ inline std::vector<ConfigItem> ConfigBase::from_config(std::istream &input) cons
             output.back().inputs = std::move(items_buffer);
         }
     }
-    if(section != "default") {
+    if(currentSection != "default") {
         // insert a section end which is just an empty items_buffer
         std::string ename;
         output.emplace_back();
-        output.back().parents = detail::generate_parents(section, ename);
+        output.back().parents = detail::generate_parents(currentSection, ename, parentSeparatorChar);
         output.back().name = "--";
         while(output.back().parents.size() > 1) {
             output.push_back(output.back());
@@ -273,8 +311,8 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
     std::vector<std::string> groups = app->get_groups();
     bool defaultUsed = false;
     groups.insert(groups.begin(), std::string("Options"));
-    if(write_description) {
-        out << commentLead << app->get_description() << '\n';
+    if(write_description && (app->get_configurable() || app->get_parent() == nullptr || app->get_name().empty())) {
+        out << commentLead << detail::fix_newlines(commentLead, app->get_description()) << '\n';
     }
     for(auto &group : groups) {
         if(group == "Options" || group.empty()) {
@@ -296,13 +334,16 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
                     }
                 }
                 std::string name = prefix + opt->get_single_name();
-                std::string value = detail::ini_join(opt->reduced_results(), arraySeparator, arrayStart, arrayEnd);
+                std::string value = detail::ini_join(
+                    opt->reduced_results(), arraySeparator, arrayStart, arrayEnd, stringQuote, characterQuote);
 
                 if(value.empty() && default_also) {
                     if(!opt->get_default_str().empty()) {
-                        value = detail::convert_arg_for_ini(opt->get_default_str());
+                        value = detail::convert_arg_for_ini(opt->get_default_str(), stringQuote, characterQuote);
                     } else if(opt->get_expected_min() == 0) {
                         value = "false";
+                    } else if(opt->get_run_callback_for_default()) {
+                        value = "\"\"";  // empty string default value
                     }
                 }
 
@@ -332,17 +373,18 @@ ConfigBase::to_config(const App *app, bool default_also, bool write_description,
                 if(!prefix.empty() || app->get_parent() == nullptr) {
                     out << '[' << prefix << subcom->get_name() << "]\n";
                 } else {
-                    std::string subname = app->get_name() + "." + subcom->get_name();
+                    std::string subname = app->get_name() + parentSeparatorChar + subcom->get_name();
                     auto p = app->get_parent();
                     while(p->get_parent() != nullptr) {
-                        subname = p->get_name() + "." + subname;
+                        subname = p->get_name() + parentSeparatorChar + subname;
                         p = p->get_parent();
                     }
                     out << '[' << subname << "]\n";
                 }
                 out << to_config(subcom, default_also, write_description, "");
             } else {
-                out << to_config(subcom, default_also, write_description, prefix + subcom->get_name() + ".");
+                out << to_config(
+                    subcom, default_also, write_description, prefix + subcom->get_name() + parentSeparatorChar);
             }
         }
     }
diff --git a/packages/CLI11/include/CLI/ConfigFwd.hpp b/packages/CLI11/include/CLI/ConfigFwd.hpp
index 2d9f9a39c7d0609ee524bb3d881a6c836f8c0ff9..ef2ac345f87986f8888512791a0255a9e1e5f1d3 100644
--- a/packages/CLI11/include/CLI/ConfigFwd.hpp
+++ b/packages/CLI11/include/CLI/ConfigFwd.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -87,6 +87,18 @@ class ConfigBase : public Config {
     char arraySeparator = ',';
     /// the character used separate the name from the value
     char valueDelimiter = '=';
+    /// the character to use around strings
+    char stringQuote = '"';
+    /// the character to use around single characters
+    char characterQuote = '\'';
+    /// the maximum number of layers to allow
+    uint8_t maximumLayers{255};
+    /// the separator used to separator parent layers
+    char parentSeparatorChar{'.'};
+    /// Specify the configuration index to use for arrayed sections
+    int16_t configIndex{-1};
+    /// Specify the configuration section that should be used
+    std::string configSection{};
 
   public:
     std::string
@@ -114,6 +126,41 @@ class ConfigBase : public Config {
         valueDelimiter = vSep;
         return this;
     }
+    /// Specify the quote characters used around strings and characters
+    ConfigBase *quoteCharacter(char qString, char qChar) {
+        stringQuote = qString;
+        characterQuote = qChar;
+        return this;
+    }
+    /// Specify the maximum number of parents
+    ConfigBase *maxLayers(uint8_t layers) {
+        maximumLayers = layers;
+        return this;
+    }
+    /// Specify the separator to use for parent layers
+    ConfigBase *parentSeparator(char sep) {
+        parentSeparatorChar = sep;
+        return this;
+    }
+    /// get a reference to the configuration section
+    std::string &sectionRef() { return configSection; }
+    /// get the section
+    const std::string &section() const { return configSection; }
+    /// specify a particular section of the configuration file to use
+    ConfigBase *section(const std::string &sectionName) {
+        configSection = sectionName;
+        return this;
+    }
+
+    /// get a reference to the configuration index
+    int16_t &indexRef() { return configIndex; }
+    /// get the section index
+    int16_t index() const { return configIndex; }
+    /// specify a particular index in the section to use (-1) for all sections to use
+    ConfigBase *index(int16_t sectionIndex) {
+        configIndex = sectionIndex;
+        return this;
+    }
 };
 
 /// the default Config is the TOML file format
diff --git a/packages/CLI11/include/CLI/Error.hpp b/packages/CLI11/include/CLI/Error.hpp
index e6a8325730deb119e7c37989cdf469e78d951f57..de3122bb431406cf4f16d61161f413b2f2ef29f0 100644
--- a/packages/CLI11/include/CLI/Error.hpp
+++ b/packages/CLI11/include/CLI/Error.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/include/CLI/Formatter.hpp b/packages/CLI11/include/CLI/Formatter.hpp
index cb9e92a57ab5a9bc68a1b156752a9549c28af9cc..e45aa25d9739a78d60542b2cfd989199201ade27 100644
--- a/packages/CLI11/include/CLI/Formatter.hpp
+++ b/packages/CLI11/include/CLI/Formatter.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/include/CLI/FormatterFwd.hpp b/packages/CLI11/include/CLI/FormatterFwd.hpp
index 6908d464447ae76081eb6240b7ac9bf86eadfcd2..728926c0f58909a61ffcf3af583ec28652e8fff0 100644
--- a/packages/CLI11/include/CLI/FormatterFwd.hpp
+++ b/packages/CLI11/include/CLI/FormatterFwd.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/include/CLI/Macros.hpp b/packages/CLI11/include/CLI/Macros.hpp
index 778fec7dcbdace22952977e0feb851e5be8cf9c5..f228c918cd408ce33aeed734ca3cc2b2d40f6edf 100644
--- a/packages/CLI11/include/CLI/Macros.hpp
+++ b/packages/CLI11/include/CLI/Macros.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -8,7 +8,7 @@
 
 // [CLI11:macros_hpp:verbatim]
 
-// The following version macro is very similar to the one in PyBind11
+// The following version macro is very similar to the one in pybind11
 #if !(defined(_MSC_VER) && __cplusplus == 199711L) && !defined(__INTEL_COMPILER)
 #if __cplusplus >= 201402L
 #define CLI11_CPP14
diff --git a/packages/CLI11/include/CLI/Option.hpp b/packages/CLI11/include/CLI/Option.hpp
index f09f61f9730f105383552b92a245df786a3c48c6..25a676055fba34d7448e263df4e94be526de002e 100644
--- a/packages/CLI11/include/CLI/Option.hpp
+++ b/packages/CLI11/include/CLI/Option.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -94,6 +94,9 @@ template <typename CRTP> class OptionBase {
 
     /// Changes the group membership
     CRTP *group(const std::string &name) {
+        if(!detail::valid_alias_name_string(name)) {
+            throw IncorrectConstruction("Group names may not contain newlines or null characters");
+        }
         group_ = name;
         return static_cast<CRTP *>(this);
     }
@@ -337,6 +340,10 @@ class Option : public OptionBase<Option> {
     bool run_callback_for_default_{false};
     /// flag indicating a separator needs to be injected after each argument call
     bool inject_separator_{false};
+    /// flag indicating that the option should trigger the validation and callback chain on each result when loaded
+    bool trigger_on_result_{false};
+    /// flag indicating that the option should force the callback regardless if any results present
+    bool force_callback_{false};
     ///@}
 
     /// Making an option by hand is not defined, it must be made by the App class
@@ -358,8 +365,8 @@ class Option : public OptionBase<Option> {
     /// True if the option was not passed
     bool empty() const { return results_.empty(); }
 
-    /// This class is true if option is passed.
-    explicit operator bool() const { return !empty(); }
+    /// This bool operator returns true if any arguments were passed or the option callback is forced
+    explicit operator bool() const { return !empty() || force_callback_; }
 
     /// Clear the parsed results (mostly for testing)
     void clear() {
@@ -420,6 +427,21 @@ class Option : public OptionBase<Option> {
     }
     /// Get the current value of allow extra args
     bool get_allow_extra_args() const { return allow_extra_args_; }
+    /// Set the value of trigger_on_parse which specifies that the option callback should be triggered on every parse
+    Option *trigger_on_parse(bool value = true) {
+        trigger_on_result_ = value;
+        return this;
+    }
+    /// The status of trigger on parse
+    bool get_trigger_on_parse() const { return trigger_on_result_; }
+
+    /// Set the value of force_callback
+    Option *force_callback(bool value = true) {
+        force_callback_ = value;
+        return this;
+    }
+    /// The status of force_callback
+    bool get_force_callback() const { return force_callback_; }
 
     /// Set the value of run_callback_for_default which controls whether the callback function should be called to set
     /// the default This is controlled automatically but could be manipulated by the user.
@@ -666,7 +688,7 @@ class Option : public OptionBase<Option> {
     /// The maximum number of arguments the option expects
     int get_type_size_max() const { return type_size_max_; }
 
-    /// The number of arguments the option expects
+    /// Return the inject_separator flag
     int get_inject_separator() const { return inject_separator_; }
 
     /// The environment variable associated to this value
@@ -759,7 +781,7 @@ class Option : public OptionBase<Option> {
     /// Use `get_name(true)` to get the positional name (replaces `get_pname`)
     std::string get_name(bool positional = false,  ///< Show the positional name
                          bool all_options = false  ///< Show every option
-                         ) const {
+    ) const {
         if(get_group().empty())
             return {};  // Hidden
 
@@ -818,7 +840,9 @@ class Option : public OptionBase<Option> {
 
     /// Process the callback
     void run_callback() {
-
+        if(force_callback_ && results_.empty()) {
+            add_result(default_str_);
+        }
         if(current_option_state_ == option_state::parsing) {
             _validate_results(results_);
             current_option_state_ = option_state::validated;
@@ -974,10 +998,10 @@ class Option : public OptionBase<Option> {
 
     /// Puts a result at the end
     Option *add_result(std::vector<std::string> s) {
+        current_option_state_ = option_state::parsing;
         for(auto &str : s) {
             _add_result(std::move(str), results_);
         }
-        current_option_state_ = option_state::parsing;
         return this;
     }
 
@@ -1136,7 +1160,8 @@ class Option : public OptionBase<Option> {
         results_.clear();
         try {
             add_result(val_str);
-            if(run_callback_for_default_) {
+            // if trigger_on_result_ is set the callback already ran
+            if(run_callback_for_default_ && !trigger_on_result_) {
                 run_callback();  // run callback sets the state we need to reset it again
                 current_option_state_ = option_state::parsing;
             } else {
diff --git a/packages/CLI11/include/CLI/Split.hpp b/packages/CLI11/include/CLI/Split.hpp
index 36c953de8471e2b4f414c3c877ff087b494638c2..83ef572c1c42e7a575d0d22c7e2515959bf9b566 100644
--- a/packages/CLI11/include/CLI/Split.hpp
+++ b/packages/CLI11/include/CLI/Split.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/include/CLI/StringTools.hpp b/packages/CLI11/include/CLI/StringTools.hpp
index 090445e5b1236ce92f3b009bd1d55cde9a5d0ebd..4718aedd9a9e301591afacc4ac22730e02e1d183 100644
--- a/packages/CLI11/include/CLI/StringTools.hpp
+++ b/packages/CLI11/include/CLI/StringTools.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -157,6 +157,22 @@ inline std::string &remove_quotes(std::string &str) {
     return str;
 }
 
+/// Add a leader to the beginning of all new lines (nothing is added
+/// at the start of the first line). `"; "` would be for ini files
+///
+/// Can't use Regex, or this would be a subs.
+inline std::string fix_newlines(const std::string &leader, std::string input) {
+    std::string::size_type n = 0;
+    while(n != std::string::npos && n < input.size()) {
+        n = input.find('\n', n);
+        if(n != std::string::npos) {
+            input = input.substr(0, n + 1) + leader + input.substr(n + 1);
+            n += leader.size();
+        }
+    }
+    return input;
+}
+
 /// Make a copy of the string and then trim it, any filter string can be used (any char in string is filtered)
 inline std::string trim_copy(const std::string &str, const std::string &filter) {
     std::string s = str;
@@ -191,7 +207,7 @@ inline std::ostream &format_aliases(std::ostream &out, const std::vector<std::st
             } else {
                 front = false;
             }
-            out << alias;
+            out << detail::fix_newlines("              ", alias);
         }
         out << "\n";
     }
@@ -199,23 +215,35 @@ inline std::ostream &format_aliases(std::ostream &out, const std::vector<std::st
 }
 
 /// Verify the first character of an option
-template <typename T> bool valid_first_char(T c) {
-    return std::isalnum(c, std::locale()) || c == '_' || c == '?' || c == '@';
-}
+/// - is a trigger character, ! has special meaning and new lines would just be annoying to deal with
+template <typename T> bool valid_first_char(T c) { return ((c != '-') && (c != '!') && (c != ' ') && c != '\n'); }
 
 /// Verify following characters of an option
-template <typename T> bool valid_later_char(T c) { return valid_first_char(c) || c == '.' || c == '-'; }
+template <typename T> bool valid_later_char(T c) {
+    // = and : are value separators, { has special meaning for option defaults,
+    // and \n would just be annoying to deal with in many places allowing space here has too much potential for
+    // inadvertent entry errors and bugs
+    return ((c != '=') && (c != ':') && (c != '{') && (c != ' ') && c != '\n');
+}
 
-/// Verify an option name
+/// Verify an option/subcommand name
 inline bool valid_name_string(const std::string &str) {
-    if(str.empty() || !valid_first_char(str[0]))
+    if(str.empty() || !valid_first_char(str[0])) {
         return false;
-    for(auto c : str.substr(1))
-        if(!valid_later_char(c))
+    }
+    auto e = str.end();
+    for(auto c = str.begin() + 1; c != e; ++c)
+        if(!valid_later_char(*c))
             return false;
     return true;
 }
 
+/// Verify an app name
+inline bool valid_alias_name_string(const std::string &str) {
+    static const std::string badChars(std::string("\n") + '\0');
+    return (str.find_first_of(badChars) == std::string::npos);
+}
+
 /// check if a string is a container segment separator (empty or "%%")
 inline bool is_separator(const std::string &str) {
     static const std::string sep("%%");
@@ -260,7 +288,7 @@ inline bool has_default_flag_values(const std::string &flags) {
 }
 
 inline void remove_default_flag_values(std::string &flags) {
-    auto loc = flags.find_first_of('{');
+    auto loc = flags.find_first_of('{', 2);
     while(loc != std::string::npos) {
         auto finish = flags.find_first_of("},", loc + 1);
         if((finish != std::string::npos) && (flags[finish] == '}')) {
@@ -367,22 +395,6 @@ inline std::vector<std::string> split_up(std::string str, char delimiter = '\0')
     return output;
 }
 
-/// Add a leader to the beginning of all new lines (nothing is added
-/// at the start of the first line). `"; "` would be for ini files
-///
-/// Can't use Regex, or this would be a subs.
-inline std::string fix_newlines(const std::string &leader, std::string input) {
-    std::string::size_type n = 0;
-    while(n != std::string::npos && n < input.size()) {
-        n = input.find('\n', n);
-        if(n != std::string::npos) {
-            input = input.substr(0, n + 1) + leader + input.substr(n + 1);
-            n += leader.size();
-        }
-    }
-    return input;
-}
-
 /// This function detects an equal or colon followed by an escaped quote after an argument
 /// then modifies the string to replace the equality with a space.  This is needed
 /// to allow the split up function to work properly and is intended to be used with the find_and_modify function
diff --git a/packages/CLI11/include/CLI/Timer.hpp b/packages/CLI11/include/CLI/Timer.hpp
index 69728b7dc89a4df2bed42755730a1b9f9184465a..429ca026c5ea3d66a2dfe786d7965f231cf36b7f 100644
--- a/packages/CLI11/include/CLI/Timer.hpp
+++ b/packages/CLI11/include/CLI/Timer.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/include/CLI/TypeTools.hpp b/packages/CLI11/include/CLI/TypeTools.hpp
index be0e89ab6f55a304e7171e26010fcb1b0b4dd983..0fa2299758d56b8b471fd8c6bc563aa08113459c 100644
--- a/packages/CLI11/include/CLI/TypeTools.hpp
+++ b/packages/CLI11/include/CLI/TypeTools.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -36,7 +36,7 @@ constexpr enabler dummy = {};
 /// A copy of enable_if_t from C++14, compatible with C++11.
 ///
 /// We could check to see if C++14 is being used, but it does not hurt to redefine this
-/// (even Google does this: https://github.com/google/skia/blob/master/include/private/SkTLogic.h)
+/// (even Google does this: https://github.com/google/skia/blob/main/include/private/SkTLogic.h)
 /// It is not in the std namespace anyway, so no harm done.
 template <bool B, class T = void> using enable_if_t = typename std::enable_if<B, T>::type;
 
@@ -1059,7 +1059,7 @@ bool lexical_cast(const std::string &input, T &output) {
     }
     // LCOV_EXCL_START
     // This version of cast is only used for odd cases in an older compilers the fail over
-    // from_stream is tested elsewhere an not relevent for coverage here
+    // from_stream is tested elsewhere an not relevant for coverage here
     return from_stream(input, output);
     // LCOV_EXCL_STOP
 }
diff --git a/packages/CLI11/include/CLI/Validators.hpp b/packages/CLI11/include/CLI/Validators.hpp
index 90b590f6b6f99cf3067683284146e849f673ec28..03eb77b6ffbe290ad130daedd7227892ebeec356 100644
--- a/packages/CLI11/include/CLI/Validators.hpp
+++ b/packages/CLI11/include/CLI/Validators.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -467,35 +467,35 @@ class Range : public Validator {
     /// Note that the constructor is templated, but the struct is not, so C++17 is not
     /// needed to provide nice syntax for Range(a,b).
     template <typename T>
-    Range(T min, T max, const std::string &validator_name = std::string{}) : Validator(validator_name) {
+    Range(T min_val, T max_val, const std::string &validator_name = std::string{}) : Validator(validator_name) {
         if(validator_name.empty()) {
             std::stringstream out;
-            out << detail::type_name<T>() << " in [" << min << " - " << max << "]";
+            out << detail::type_name<T>() << " in [" << min_val << " - " << max_val << "]";
             description(out.str());
         }
 
-        func_ = [min, max](std::string &input) {
+        func_ = [min_val, max_val](std::string &input) {
             T val;
             bool converted = detail::lexical_cast(input, val);
-            if((!converted) || (val < min || val > max))
-                return std::string("Value ") + input + " not in range " + std::to_string(min) + " to " +
-                       std::to_string(max);
+            if((!converted) || (val < min_val || val > max_val))
+                return std::string("Value ") + input + " not in range " + std::to_string(min_val) + " to " +
+                       std::to_string(max_val);
 
-            return std::string();
+            return std::string{};
         };
     }
 
     /// Range of one value is 0 to value
     template <typename T>
-    explicit Range(T max, const std::string &validator_name = std::string{})
-        : Range(static_cast<T>(0), max, validator_name) {}
+    explicit Range(T max_val, const std::string &validator_name = std::string{})
+        : Range(static_cast<T>(0), max_val, validator_name) {}
 };
 
 /// Check for a non negative number
-const Range NonNegativeNumber(std::numeric_limits<double>::max(), "NONNEGATIVE");
+const Range NonNegativeNumber((std::numeric_limits<double>::max)(), "NONNEGATIVE");
 
 /// Check for a positive valued number (val>0.0), min() her is the smallest positive number
-const Range PositiveNumber(std::numeric_limits<double>::min(), std::numeric_limits<double>::max(), "POSITIVE");
+const Range PositiveNumber((std::numeric_limits<double>::min)(), (std::numeric_limits<double>::max)(), "POSITIVE");
 
 /// Produce a bounded range (factory). Min and max are inclusive.
 class Bound : public Validator {
@@ -504,28 +504,28 @@ class Bound : public Validator {
     ///
     /// Note that the constructor is templated, but the struct is not, so C++17 is not
     /// needed to provide nice syntax for Range(a,b).
-    template <typename T> Bound(T min, T max) {
+    template <typename T> Bound(T min_val, T max_val) {
         std::stringstream out;
-        out << detail::type_name<T>() << " bounded to [" << min << " - " << max << "]";
+        out << detail::type_name<T>() << " bounded to [" << min_val << " - " << max_val << "]";
         description(out.str());
 
-        func_ = [min, max](std::string &input) {
+        func_ = [min_val, max_val](std::string &input) {
             T val;
             bool converted = detail::lexical_cast(input, val);
             if(!converted) {
                 return std::string("Value ") + input + " could not be converted";
             }
-            if(val < min)
-                input = detail::to_string(min);
-            else if(val > max)
-                input = detail::to_string(max);
+            if(val < min_val)
+                input = detail::to_string(min_val);
+            else if(val > max_val)
+                input = detail::to_string(max_val);
 
             return std::string{};
         };
     }
 
     /// Range of one value is 0 to value
-    template <typename T> explicit Bound(T max) : Bound(static_cast<T>(0), max) {}
+    template <typename T> explicit Bound(T max_val) : Bound(static_cast<T>(0), max_val) {}
 };
 
 namespace detail {
@@ -676,7 +676,7 @@ class IsMember : public Validator {
 
     /// This allows in-place construction using an initializer list
     template <typename T, typename... Args>
-    IsMember(std::initializer_list<T> values, Args &&... args)
+    IsMember(std::initializer_list<T> values, Args &&...args)
         : IsMember(std::vector<T>(values), std::forward<Args>(args)...) {}
 
     /// This checks to see if an item is in a set (empty function)
@@ -728,7 +728,7 @@ class IsMember : public Validator {
 
     /// You can pass in as many filter functions as you like, they nest (string only currently)
     template <typename T, typename... Args>
-    IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
+    IsMember(T &&set, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
         : IsMember(
               std::forward<T>(set),
               [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
@@ -745,7 +745,7 @@ class Transformer : public Validator {
 
     /// This allows in-place construction
     template <typename... Args>
-    Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
+    Transformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
         : Transformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
 
     /// direct map of std::string to std::string
@@ -789,7 +789,7 @@ class Transformer : public Validator {
 
     /// You can pass in as many filter functions as you like, they nest
     template <typename T, typename... Args>
-    Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
+    Transformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
         : Transformer(
               std::forward<T>(mapping),
               [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
@@ -803,7 +803,7 @@ class CheckedTransformer : public Validator {
 
     /// This allows in-place construction
     template <typename... Args>
-    CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&... args)
+    CheckedTransformer(std::initializer_list<std::pair<std::string, std::string>> values, Args &&...args)
         : CheckedTransformer(TransformPairs<std::string>(values), std::forward<Args>(args)...) {}
 
     /// direct map of std::string to std::string
@@ -865,7 +865,7 @@ class CheckedTransformer : public Validator {
 
     /// You can pass in as many filter functions as you like, they nest
     template <typename T, typename... Args>
-    CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&... other)
+    CheckedTransformer(T &&mapping, filter_fn_t filter_fn_1, filter_fn_t filter_fn_2, Args &&...other)
         : CheckedTransformer(
               std::forward<T>(mapping),
               [filter_fn_1, filter_fn_2](std::string a) { return filter_fn_2(filter_fn_1(a)); },
@@ -1100,12 +1100,35 @@ inline std::pair<std::string, std::string> split_program_name(std::string comman
         if(esp == std::string::npos) {
             // if we have reached the end and haven't found a valid file just assume the first argument is the
             // program name
-            esp = commandline.find_first_of(' ', 1);
+            if(commandline[0] == '"' || commandline[0] == '\'' || commandline[0] == '`') {
+                bool embeddedQuote = false;
+                auto keyChar = commandline[0];
+                auto end = commandline.find_first_of(keyChar, 1);
+                while((end != std::string::npos) && (commandline[end - 1] == '\\')) {  // deal with escaped quotes
+                    end = commandline.find_first_of(keyChar, end + 1);
+                    embeddedQuote = true;
+                }
+                if(end != std::string::npos) {
+                    vals.first = commandline.substr(1, end - 1);
+                    esp = end + 1;
+                    if(embeddedQuote) {
+                        vals.first = find_and_replace(vals.first, std::string("\\") + keyChar, std::string(1, keyChar));
+                    }
+                } else {
+                    esp = commandline.find_first_of(' ', 1);
+                }
+            } else {
+                esp = commandline.find_first_of(' ', 1);
+            }
+
             break;
         }
     }
-    vals.first = commandline.substr(0, esp);
-    rtrim(vals.first);
+    if(vals.first.empty()) {
+        vals.first = commandline.substr(0, esp);
+        rtrim(vals.first);
+    }
+
     // strip the program name
     vals.second = (esp != std::string::npos) ? commandline.substr(esp + 1) : std::string{};
     ltrim(vals.second);
diff --git a/packages/CLI11/include/CLI/Version.hpp b/packages/CLI11/include/CLI/Version.hpp
index dde959890da59b6640e9a16eefdc60b5a74e925f..4bc79a7737a558b264fac0f460c4dfcd324b7075 100644
--- a/packages/CLI11/include/CLI/Version.hpp
+++ b/packages/CLI11/include/CLI/Version.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -8,9 +8,9 @@
 
 // [CLI11:version_hpp:verbatim]
 
-#define CLI11_VERSION_MAJOR 1
-#define CLI11_VERSION_MINOR 9
-#define CLI11_VERSION_PATCH 1
-#define CLI11_VERSION "1.9.1"
+#define CLI11_VERSION_MAJOR 2
+#define CLI11_VERSION_MINOR 1
+#define CLI11_VERSION_PATCH 2
+#define CLI11_VERSION "2.1.2"
 
 // [CLI11:version_hpp:end]
diff --git a/packages/CLI11/scripts/MakeSingleHeader.py b/packages/CLI11/scripts/MakeSingleHeader.py
index 78714ffb5038a6a825de32b9a2d64f1314dd7da9..42e0ee7a9c590ae27614d5e77db2c9459170438b 100755
--- a/packages/CLI11/scripts/MakeSingleHeader.py
+++ b/packages/CLI11/scripts/MakeSingleHeader.py
@@ -39,12 +39,20 @@ DIR = os.path.dirname(os.path.abspath(__file__))
 
 class HeaderGroups(dict):
     def __init__(self, tag):
+        """
+        A dictionary that also can read headers given a tag expression.
+
+        TODO: might have gone overboard on this one, could maybe be two functions.
+        """
         self.re_matcher = re.compile(
             tag_str.format(tag=tag), re.MULTILINE | re.DOTALL | re.VERBOSE
         )
         super(HeaderGroups, self).__init__()
 
     def read_header(self, filename):
+        """
+        Read a header file in and add items to the dict, based on the item's action.
+        """
         with open(filename) as f:
             inner = f.read()
 
@@ -67,12 +75,18 @@ class HeaderGroups(dict):
                 raise RuntimeError("Action not understood, must be verbatim or set")
 
     def post_process(self):
+        """
+        Turn sets into multiple line strings.
+        """
         for key in self:
             if isinstance(self[key], set):
                 self[key] = "\n".join(self[key])
 
 
-def MakeHeader(output, main_header, files, tag, namespace, macro=None, version=None):
+def make_header(output, main_header, files, tag, namespace, macro=None, version=None):
+    """
+    Makes a single header given a main header template and a list of files.
+    """
     groups = HeaderGroups(tag)
 
     # Set tag if possible to class variable
@@ -129,7 +143,7 @@ if __name__ == "__main__":
     parser.add_argument("--version", help="Include this version in the generated file")
     args = parser.parse_args()
 
-    MakeHeader(
+    make_header(
         args.output,
         args.main,
         args.files,
diff --git a/packages/CLI11/scripts/UpdateDownloadProj.py b/packages/CLI11/scripts/UpdateDownloadProj.py
deleted file mode 100755
index e20797f4f6c942d9dcca25a2ae8afe6069e56269..0000000000000000000000000000000000000000
--- a/packages/CLI11/scripts/UpdateDownloadProj.py
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env python
-
-from __future__ import print_function, division
-
-from plumbum import local, cli, FG
-from plumbum.cmd import curl
-
-FILES = [
-    "https://raw.githubusercontent.com/Crascit/DownloadProject/master/DownloadProject.cmake",
-    "https://raw.githubusercontent.com/Crascit/DownloadProject/master/DownloadProject.CMakeLists.cmake.in",
-]
-
-DIR = local.path(__file__).dirname
-
-
-def download_file(path):
-    name = path.split("/")[-1]
-    (curl[path] > name) & FG
-
-
-class UpdateDownloadProj(cli.Application):
-    def main(self):
-        with local.cwd(DIR / "../cmake"):
-            for f in FILES:
-                download_file(f)
-
-
-if __name__ == "__main__":
-    UpdateDownloadProj()
diff --git a/packages/CLI11/scripts/mdlint_style.rb b/packages/CLI11/scripts/mdlint_style.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6fca85b1a3d05a43aba1fca2e3aa256676e1f11d
--- /dev/null
+++ b/packages/CLI11/scripts/mdlint_style.rb
@@ -0,0 +1,8 @@
+all
+
+exclude_rule 'MD013'  # Line length
+exclude_rule 'MD033'  # Inline HTML
+exclude_rule 'MD034'  # Bare URL (for now)
+
+rule 'MD026', punctuation: '.,;:!'  # Trailing punctuation in header (& in this case)
+rule 'MD029', style: :ordered
diff --git a/packages/CLI11/test_package/example.cpp b/packages/CLI11/test_package/example.cpp
index beb97fe7fafec9e224cc58e97843ed5d8dff6f23..464cf751896f183fa1bba19d965ac521c8b60a52 100644
--- a/packages/CLI11/test_package/example.cpp
+++ b/packages/CLI11/test_package/example.cpp
@@ -9,7 +9,7 @@ int main(int argc, char **argv) {
     CLI::App app("Some nice description");
 
     int x = 0;
-    app.add_option("-x", x, "an integer value", true /* show default */);
+    app.add_option("-x", x, "an integer value")->capture_default_str();
 
     bool flag;
     app.add_flag("-f,--flag", flag, "a flag option");
diff --git a/packages/CLI11/tests/AppTest.cpp b/packages/CLI11/tests/AppTest.cpp
index 6c3e71ced8ee59cbe4409698b86b44b56430a881..d425b414c08fcd287f07a9030810614f7b8292f5 100644
--- a/packages/CLI11/tests/AppTest.cpp
+++ b/packages/CLI11/tests/AppTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -127,6 +127,17 @@ TEST_CASE_METHOD(TApp, "DashedOptionsSingleString", "[app]") {
     CHECK(app.count("--that") == 2u);
 }
 
+TEST_CASE_METHOD(TApp, "StrangeFlagNames", "[app]") {
+    app.add_flag("-=");
+    app.add_flag("--t\tt");
+    app.add_flag("-{");
+    CHECK_THROWS_AS(app.add_flag("--t t"), CLI::ConstructionError);
+    args = {"-=", "--t\tt"};
+    run();
+    CHECK(app.count("-=") == 1u);
+    CHECK(app.count("--t\tt") == 1u);
+}
+
 TEST_CASE_METHOD(TApp, "RequireOptionsError", "[app]") {
     using Catch::Matchers::Contains;
 
@@ -582,6 +593,20 @@ TEST_CASE_METHOD(TApp, "SingleArgVector", "[app]") {
     CHECK("happy" == path);
 }
 
+TEST_CASE_METHOD(TApp, "StrangeOptionNames", "[app]") {
+    app.add_option("-:");
+    app.add_option("--t\tt");
+    app.add_option("--{}");
+    app.add_option("--:)");
+    CHECK_THROWS_AS(app.add_option("--t t"), CLI::ConstructionError);
+    args = {"-:)", "--{}", "5"};
+    run();
+    CHECK(app.count("-:") == 1u);
+    CHECK(app.count("--{}") == 1u);
+    CHECK(app["-:"]->as<char>() == ')');
+    CHECK(app["--{}"]->as<int>() == 5);
+}
+
 TEST_CASE_METHOD(TApp, "FlagLikeOption", "[app]") {
     bool val{false};
     auto opt = app.add_option("--flag", val)->type_size(0)->default_str("true");
@@ -809,7 +834,7 @@ TEST_CASE_METHOD(TApp, "TakeFirstOptMulti", "[app]") {
 
 TEST_CASE_METHOD(TApp, "ComplexOptMulti", "[app]") {
     std::complex<double> val;
-    app.add_complex("--long", val)->take_first()->allow_extra_args();
+    app.add_option("--long", val)->take_first()->allow_extra_args();
 
     args = {"--long", "1", "2", "3", "4"};
 
@@ -1933,7 +1958,6 @@ TEST_CASE_METHOD(TApp, "AllowExtrasArgModify", "[app]") {
     app.allow_extras();
     app.add_option("-f", v2);
     args = {"27", "-f", "45", "-x"};
-    auto cargs = args;
     app.parse(args);
     CHECK(std::vector<std::string>({"45", "-x"}) == args);
 
@@ -2219,7 +2243,7 @@ TEST_CASE_METHOD(TApp, "CustomUserSepParse3", "[app]") {
     CHECK(std::vector<int>({1, 2}) == vals);
     app.remove_option(opt);
 
-    app.add_option("--idx", vals, "", false)->delimiter(',');
+    app.add_option("--idx", vals)->delimiter(',');
     run();
     CHECK(std::vector<int>({1, 2}) == vals);
 }
@@ -2255,3 +2279,32 @@ TEST_CASE_METHOD(TApp, "CustomUserSepParse5", "[app]") {
     run();
     CHECK(std::vector<std::string>({"this", "is", "a", "test"}) == bar);
 }
+
+// #218
+TEST_CASE_METHOD(TApp, "logFormSingleDash", "[app]") {
+    bool verbose{false};
+    bool veryverbose{false};
+    bool veryveryverbose{false};
+    app.name("testargs");
+    app.allow_extras();
+    args = {"-v", "-vv", "-vvv"};
+    app.final_callback([&]() {
+        auto rem = app.remaining();
+        for(auto &arg : rem) {
+            if(arg == "-v") {
+                verbose = true;
+            }
+            if(arg == "-vv") {
+                veryverbose = true;
+            }
+            if(arg == "-vvv") {
+                veryveryverbose = true;
+            }
+        }
+    });
+    run();
+    CHECK(app.remaining().size() == 3U);
+    CHECK(verbose);
+    CHECK(veryverbose);
+    CHECK(veryveryverbose);
+}
diff --git a/packages/CLI11/tests/BoostOptionTypeTest.cpp b/packages/CLI11/tests/BoostOptionTypeTest.cpp
index 2110bcc6540bd881bb4474b0d00df3e21f0582c0..7ed096cd19f473cdb05ca1ec47ab586d93b272b4 100644
--- a/packages/CLI11/tests/BoostOptionTypeTest.cpp
+++ b/packages/CLI11/tests/BoostOptionTypeTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/CMakeLists.txt b/packages/CLI11/tests/CMakeLists.txt
index 4a86923f6dd7b74a9f51d02785e9f060e0617129..80c4f6a83ec93df312aaf0c19279141b02f9055e 100644
--- a/packages/CLI11/tests/CMakeLists.txt
+++ b/packages/CLI11/tests/CMakeLists.txt
@@ -1,34 +1,35 @@
 list(APPEND CMAKE_MODULE_PATH "${CLI11_SOURCE_DIR}/cmake")
 
 if(CLI11_SANITIZERS)
-    message(STATUS "Using arsenm/sanitizers-cmake")
-    FetchContent_Declare(sanitizers
-        GIT_REPOSITORY https://github.com/arsenm/sanitizers-cmake.git
-        GIT_SHALLOW 1
-        GIT_TAG 99e159e)
+  message(STATUS "Using arsenm/sanitizers-cmake")
+  FetchContent_Declare(
+    sanitizers
+    GIT_REPOSITORY https://github.com/arsenm/sanitizers-cmake.git
+    GIT_SHALLOW 1
+    GIT_TAG 99e159e)
 
-    FetchContent_GetProperties(sanitizers)
+  FetchContent_GetProperties(sanitizers)
 
-    if (NOT sanitizers_POPULATED)
-        FetchContent_Populate(sanitizers)
-    endif()
+  if(NOT sanitizers_POPULATED)
+    FetchContent_Populate(sanitizers)
+  endif()
 
-    list(APPEND CMAKE_MODULE_PATH "${sanitizers_SOURCE_DIR}/cmake")
+  list(APPEND CMAKE_MODULE_PATH "${sanitizers_SOURCE_DIR}/cmake")
 
-    find_package(Sanitizers)
-    if(SANITIZE_ADDRESS)
-        message(STATUS "You might want to use \"${ASan_WRAPPER}\" to run your program")
-    endif()
+  find_package(Sanitizers)
+  if(SANITIZE_ADDRESS)
+    message(STATUS "You might want to use \"${ASan_WRAPPER}\" to run your program")
+  endif()
 else()
-    macro(add_sanitizers)
-    endmacro()
-endif()
+  macro(add_sanitizers)
 
+  endmacro()
+endif()
 
 # Add boost to test boost::optional (currently explicitly requested)"
 option(CLI11_BOOST "Turn on boost test (currently may fail with Boost 1.70)" OFF)
 if(CLI11_BOOST)
-    find_package(Boost 1.61 REQUIRED)
+  find_package(Boost 1.61 REQUIRED)
 endif()
 set(boost-optional-def $<$<BOOL:${Boost_FOUND}>:CLI11_BOOST_OPTIONAL>)
 
@@ -50,88 +51,93 @@ set(CLI11_TESTS
     StringParseTest
     ComplexTypeTest
     TrueFalseTest
-    OptionGroupTest
-    )
+    OptionGroupTest)
 
 if(WIN32)
-    list(APPEND CLI11_TESTS WindowsTest)
+  list(APPEND CLI11_TESTS WindowsTest)
 endif()
 
-if (Boost_FOUND)
-   list(APPEND CLI11_TESTS BoostOptionTypeTest)
+if(Boost_FOUND)
+  list(APPEND CLI11_TESTS BoostOptionTypeTest)
 endif()
 
 set(CLI11_MULTIONLY_TESTS TimerTest)
 
-add_library(catch_main main.cpp)
+add_library(catch_main main.cpp catch.hpp)
 target_include_directories(catch_main PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
 
-# Currently a required download; could be make to look for existing Catch2, but
-# that would require changing the includes. FetchContent would be better, but
-# requires newer CMake.
+find_package(Catch2 CONFIG)
 
-set(url https://github.com/philsquared/Catch/releases/download/v2.13.4/catch.hpp)
-file(DOWNLOAD ${url} "${CMAKE_CURRENT_BINARY_DIR}/catch.hpp" STATUS status EXPECTED_HASH SHA256=6e0fa3dd160891a01c1f3b34e8bcd6e0140abe08eca022e390027f27dec2050b)
-list(GET status 0 error)
-if(error)
-    message(FATAL_ERROR "Could not download ${url}")
+if(Catch2_FOUND)
+  if(NOT TARGET Catch2::Catch2)
+    message(FATAL_ERROR "Found Catch2 at ${Catch2_DIR} but targets are missing.")
+  endif()
+  message(STATUS "Found Catch2")
+  target_link_libraries(catch_main PUBLIC Catch2::Catch2)
+else()
+  message(STATUS "Downloading Catch2")
+
+  # FetchContent would be better, but requires newer CMake.
+  file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/catch2")
+  set(url https://github.com/philsquared/Catch/releases/download/v2.13.7/catch.hpp)
+  file(
+    DOWNLOAD ${url} "${CMAKE_CURRENT_BINARY_DIR}/catch2/catch.hpp"
+    STATUS status
+    EXPECTED_HASH SHA256=ea379c4a3cb5799027b1eb451163dff065a3d641aaba23bf4e24ee6b536bd9bc)
+  list(GET status 0 error)
+  if(error)
+    message(FATAL_ERROR "Could not download ${url}, and Catch2 not found on your system.")
+  endif()
+  target_include_directories(catch_main PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
 endif()
-target_include_directories(catch_main PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
 
 # Target must already exist
 macro(add_catch_test TESTNAME)
-    target_link_libraries(${TESTNAME} PUBLIC catch_main)
-
-    add_test(${TESTNAME} ${TESTNAME})
-    set_target_properties(${TESTNAME} PROPERTIES FOLDER "Tests")
-    if (CLI11_FORCE_LIBCXX)
-       set_property(TARGET ${T} APPEND_STRING
-         PROPERTY LINK_FLAGS -stdlib=libc++)
-     endif()
+  target_link_libraries(${TESTNAME} PUBLIC catch_main)
+
+  add_test(${TESTNAME} ${TESTNAME})
+  set_target_properties(${TESTNAME} PROPERTIES FOLDER "Tests")
+  if(CLI11_FORCE_LIBCXX)
+    set_property(
+      TARGET ${T}
+      APPEND_STRING
+      PROPERTY LINK_FLAGS -stdlib=libc++)
+  endif()
 endmacro()
 
 foreach(T IN LISTS CLI11_TESTS)
-    if(CLI11_CUDA_TESTS)
-        set_property(
-            SOURCE ${T}.cpp
-            PROPERTY LANGUAGE CUDA
-            )
-    endif()
-    add_executable(${T} ${T}.cpp ${CLI11_headers})
-    add_sanitizers(${T})
-    if(NOT CLI11_CUDA_TESTS)
-        target_link_libraries(${T} PRIVATE CLI11_warnings)
-    endif()
-    target_link_libraries(${T} PRIVATE CLI11)
-    add_catch_test(${T})
-
-    if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
-        add_executable(${T}_Single ${T}.cpp)
-        target_link_libraries(${T}_Single PRIVATE CLI11_SINGLE)
-        add_catch_test(${T}_Single)
-        set_property(TARGET ${T}_Single PROPERTY FOLDER "Tests Single File")
-    endif()
+  if(CLI11_CUDA_TESTS)
+    set_property(SOURCE ${T}.cpp PROPERTY LANGUAGE CUDA)
+  endif()
+  add_executable(${T} ${T}.cpp ${CLI11_headers})
+  add_sanitizers(${T})
+  if(NOT CLI11_CUDA_TESTS)
+    target_link_libraries(${T} PRIVATE CLI11_warnings)
+  endif()
+  target_link_libraries(${T} PRIVATE CLI11)
+  add_catch_test(${T})
+
+  if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
+    add_executable(${T}_Single ${T}.cpp)
+    target_link_libraries(${T}_Single PRIVATE CLI11_SINGLE)
+    add_catch_test(${T}_Single)
+    set_property(TARGET ${T}_Single PROPERTY FOLDER "Tests Single File")
+  endif()
 endforeach()
 
 foreach(T IN LISTS CLI11_MULTIONLY_TESTS)
-    add_executable(${T} ${T}.cpp ${CLI11_headers})
-    add_sanitizers(${T})
-    target_link_libraries(${T} PUBLIC CLI11)
-    add_catch_test(${T})
+  add_executable(${T} ${T}.cpp ${CLI11_headers})
+  add_sanitizers(${T})
+  target_link_libraries(${T} PUBLIC CLI11)
+  add_catch_test(${T})
 endforeach()
 
 # Add -Wno-deprecated-declarations to DeprecatedTest
-set(no-deprecated-declarations
-    $<$<CXX_COMPILER_ID:MSVC>:/wd4996>
-    $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated-declarations>
-    )
-target_compile_options(DeprecatedTest
-    PRIVATE
-    ${no-deprecated-declarations})
-if (TARGET DeprecatedTest_Single)
-    target_compile_options(DeprecatedTest_Single
-        PRIVATE
-        ${no-deprecated-declarations})
+set(no-deprecated-declarations $<$<CXX_COMPILER_ID:MSVC>:/wd4996>
+                               $<$<NOT:$<CXX_COMPILER_ID:MSVC>>:-Wno-deprecated-declarations>)
+target_compile_options(DeprecatedTest PRIVATE ${no-deprecated-declarations})
+if(TARGET DeprecatedTest_Single)
+  target_compile_options(DeprecatedTest_Single PRIVATE ${no-deprecated-declarations})
 endif()
 
 # Link test (build error if inlines missing)
@@ -142,67 +148,74 @@ add_executable(link_test_2 link_test_2.cpp)
 target_link_libraries(link_test_2 PUBLIC CLI11 link_test_1)
 add_catch_test(link_test_2)
 if(CLI11_FORCE_LIBCXX)
-    set_property(TARGET link_test_1 APPEND_STRING
-        PROPERTY LINK_FLAGS -stdlib=libc++)
-    set_property(TARGET link_test_2 APPEND_STRING
-        PROPERTY LINK_FLAGS -stdlib=libc++)
+  set_property(
+    TARGET link_test_1
+    APPEND_STRING
+    PROPERTY LINK_FLAGS -stdlib=libc++)
+  set_property(
+    TARGET link_test_2
+    APPEND_STRING
+    PROPERTY LINK_FLAGS -stdlib=libc++)
 endif()
 
 # Add informational printout
 add_executable(informational informational.cpp)
 target_link_libraries(informational PUBLIC CLI11)
 if(CLI11_FORCE_LIBCXX)
-    set_property(TARGET informational APPEND_STRING
-        PROPERTY LINK_FLAGS -stdlib=libc++)
+  set_property(
+    TARGET informational
+    APPEND_STRING
+    PROPERTY LINK_FLAGS -stdlib=libc++)
 endif()
 
 # Force this to be in a standard location so CTest can find it
-set_target_properties(informational PROPERTIES
-    RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
-    RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}"
-    RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}"
-    RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}"
-    RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}"
-    )
+set_target_properties(
+  informational
+  PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}"
+             RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}"
+             RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}"
+             RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_BINARY_DIR}"
+             RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_BINARY_DIR}")
 
 # Adding this printout to CTest
 file(WRITE "${PROJECT_BINARY_DIR}/CTestCustom.cmake"
-    "set(CTEST_CUSTOM_PRE_TEST \"${CMAKE_BINARY_DIR}/informational\")"
-)
+     "set(CTEST_CUSTOM_PRE_TEST \"${CMAKE_BINARY_DIR}/informational\")")
 
 target_compile_definitions(informational PRIVATE ${boost-optional-def})
 target_compile_definitions(OptionalTest PRIVATE ${boost-optional-def})
 
-message(STATUS "Boost libs=${Boost_INCLUDE_DIRS}")
-
 if(TARGET Boost::boost)
-    message(STATUS "including boost target")
-    target_link_libraries(informational PRIVATE Boost::boost)
-    target_link_libraries(OptionalTest PRIVATE Boost::boost)
-    target_link_libraries(BoostOptionTypeTest PRIVATE Boost::boost)
-    if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
-       target_link_libraries(OptionalTest_Single PRIVATE Boost::boost)
-       target_link_libraries(BoostOptionTypeTest_Single PRIVATE Boost::boost)
-    endif()
+  message(STATUS "including boost target")
+  target_link_libraries(informational PRIVATE Boost::boost)
+  target_link_libraries(OptionalTest PRIVATE Boost::boost)
+  target_link_libraries(BoostOptionTypeTest PRIVATE Boost::boost)
+  if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
+    target_link_libraries(OptionalTest_Single PRIVATE Boost::boost)
+    target_link_libraries(BoostOptionTypeTest_Single PRIVATE Boost::boost)
+  endif()
+  message(STATUS "Boost libs=${Boost_INCLUDE_DIRS}")
 elseif(BOOST_FOUND)
-message(STATUS "no boost target")
-    target_include_directories(informational PRIVATE ${Boost_INCLUDE_DIRS})
-    target_include_directories(OptionalTest PRIVATE ${Boost_INCLUDE_DIRS})
-    target_include_directories(BoostOptionTypeTest PRIVATE ${Boost_INCLUDE_DIRS})
-    if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
-        target_include_directories(OptionalTest_Single PRIVATE ${Boost_INCLUDE_DIRS})
-        target_include_directories(BoostOptionTypeTest_Single PRIVATE ${Boost_INCLUDE_DIRS})
-     endif()
+  message(STATUS "no boost target")
+  target_include_directories(informational PRIVATE ${Boost_INCLUDE_DIRS})
+  target_include_directories(OptionalTest PRIVATE ${Boost_INCLUDE_DIRS})
+  target_include_directories(BoostOptionTypeTest PRIVATE ${Boost_INCLUDE_DIRS})
+  if(CLI11_SINGLE_FILE AND CLI11_SINGLE_FILE_TESTS)
+    target_include_directories(OptionalTest_Single PRIVATE ${Boost_INCLUDE_DIRS})
+    target_include_directories(BoostOptionTypeTest_Single PRIVATE ${Boost_INCLUDE_DIRS})
+  endif()
+  message(STATUS "Boost libs=${Boost_INCLUDE_DIRS}")
+else()
+  message(STATUS "Boost not found, not adding boost tests")
 endif()
 
 if(CMAKE_BUILD_TYPE STREQUAL Coverage)
-    include(CodeCoverage)
-    setup_target_for_coverage(
-        NAME CLI11_coverage
-        EXECUTABLE ctest
-        DEPENDENCIES
-          ${CLI11_TESTS}
-          ${CLI11_MULTIONLY_TESTS}
-        )
+  include(CodeCoverage)
+  setup_target_for_coverage(
+    NAME
+    CLI11_coverage
+    EXECUTABLE
+    ctest
+    DEPENDENCIES
+    ${CLI11_TESTS}
+    ${CLI11_MULTIONLY_TESTS})
 endif()
-
diff --git a/packages/CLI11/tests/ComplexTypeTest.cpp b/packages/CLI11/tests/ComplexTypeTest.cpp
index b9a5d4e51cfc2584b8f199ef9ced5c8be245f8a4..4806c544e8a5764f29e4ffe1745e2b694eeb4eb9 100644
--- a/packages/CLI11/tests/ComplexTypeTest.cpp
+++ b/packages/CLI11/tests/ComplexTypeTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/ConfigFileTest.cpp b/packages/CLI11/tests/ConfigFileTest.cpp
index 12fa88c10a3949c218b246c1b6a9ddd429f9f309..61c066eedaac5f0f2950b5a92101e681c20debaf 100644
--- a/packages/CLI11/tests/ConfigFileTest.cpp
+++ b/packages/CLI11/tests/ConfigFileTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -535,6 +535,10 @@ TEST_CASE_METHOD(TApp, "IniRequiredNoDefault", "[config]") {
     int two{0};
     app.add_option("--two", two);
     REQUIRE_THROWS_AS(run(), CLI::FileError);
+    // test to make sure help still gets called correctly
+    // GitHub issue #533 https://github.com/CLIUtils/CLI11/issues/553
+    args = {"--help"};
+    REQUIRE_THROWS_AS(run(), CLI::CallForHelp);
 }
 
 TEST_CASE_METHOD(TApp, "IniNotRequiredNoDefault", "[config]") {
@@ -794,6 +798,114 @@ TEST_CASE_METHOD(TApp, "IniRequired", "[config]") {
     CHECK_THROWS_AS(run(), CLI::RequiredError);
 }
 
+TEST_CASE_METHOD(TApp, "IniInlineComment", "[config]") {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini, "", true);
+    app.config_formatter(std::make_shared<CLI::ConfigINI>());
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=99 ; this is a two" << std::endl;
+        out << "three=3; this is a three" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0};
+    app.add_option("--one", one)->required();
+    app.add_option("--two", two)->required();
+    app.add_option("--three", three)->required();
+
+    args = {"--one=1"};
+
+    run();
+    CHECK(1 == one);
+    CHECK(99 == two);
+    CHECK(3 == three);
+
+    one = two = three = 0;
+    args = {"--one=1", "--two=2"};
+
+    CHECK_NOTHROW(run());
+    CHECK(1 == one);
+    CHECK(2 == two);
+    CHECK(3 == three);
+
+    args = {};
+
+    CHECK_THROWS_AS(run(), CLI::RequiredError);
+
+    args = {"--two=2"};
+
+    CHECK_THROWS_AS(run(), CLI::RequiredError);
+}
+
+TEST_CASE_METHOD(TApp, "TomlInlineComment", "[config]") {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini, "", true);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "two=99 # this is a two" << std::endl;
+        out << "three=3# this is a three" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0};
+    app.add_option("--one", one)->required();
+    app.add_option("--two", two)->required();
+    app.add_option("--three", three)->required();
+
+    args = {"--one=1"};
+
+    run();
+    CHECK(1 == one);
+    CHECK(99 == two);
+    CHECK(3 == three);
+
+    one = two = three = 0;
+    args = {"--one=1", "--two=2"};
+
+    CHECK_NOTHROW(run());
+    CHECK(1 == one);
+    CHECK(2 == two);
+    CHECK(3 == three);
+
+    args = {};
+
+    CHECK_THROWS_AS(run(), CLI::RequiredError);
+
+    args = {"--two=2"};
+
+    CHECK_THROWS_AS(run(), CLI::RequiredError);
+}
+
+TEST_CASE_METHOD(TApp, "ConfigModifiers", "[config]") {
+
+    app.set_config("--config", "test.ini", "", true);
+
+    auto cfgptr = app.get_config_formatter_base();
+
+    cfgptr->section("test");
+    CHECK(cfgptr->section() == "test");
+
+    CHECK(cfgptr->sectionRef() == "test");
+    auto &sref = cfgptr->sectionRef();
+    sref = "this";
+    CHECK(cfgptr->section() == "this");
+
+    cfgptr->index(5);
+    CHECK(cfgptr->index() == 5);
+
+    CHECK(cfgptr->indexRef() == 5);
+    auto &iref = cfgptr->indexRef();
+    iref = 7;
+    CHECK(cfgptr->index() == 7);
+}
+
 TEST_CASE_METHOD(TApp, "IniVector", "[config]") {
 
     TempFile tmpini{"TestIniTmp.ini"};
@@ -999,6 +1111,39 @@ TEST_CASE_METHOD(TApp, "IniLayered", "[config]") {
     CHECK(!*subcom);
 }
 
+TEST_CASE_METHOD(TApp, "IniLayeredStream", "[config]") {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[subcom]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "subsubcom.val=3" << std::endl;
+    }
+
+    int one{0}, two{0}, three{0};
+    app.add_option("--val", one);
+    auto subcom = app.add_subcommand("subcom");
+    subcom->add_option("--val", two);
+    auto subsubcom = subcom->add_subcommand("subsubcom");
+    subsubcom->add_option("--val", three);
+
+    std::ifstream in{tmpini};
+    app.parse_from_stream(in);
+
+    CHECK(one == 1);
+    CHECK(two == 2);
+    CHECK(three == 3);
+
+    CHECK(0U == subcom->count());
+    CHECK(!*subcom);
+}
+
 TEST_CASE_METHOD(TApp, "IniLayeredDotSection", "[config]") {
 
     TempFile tmpini{"TestIniTmp.ini"};
@@ -1030,6 +1175,45 @@ TEST_CASE_METHOD(TApp, "IniLayeredDotSection", "[config]") {
 
     CHECK(0U == subcom->count());
     CHECK(!*subcom);
+
+    three = 0;
+    // check maxlayers
+    app.get_config_formatter_base()->maxLayers(1);
+    run();
+    CHECK(three == 0);
+}
+
+TEST_CASE_METHOD(TApp, "IniLayeredCustomSectionSeparator", "[config]") {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[subcom]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "[subcom|subsubcom]" << std::endl;
+        out << "val=3" << std::endl;
+    }
+    app.get_config_formatter_base()->parentSeparator('|');
+    int one{0}, two{0}, three{0};
+    app.add_option("--val", one);
+    auto subcom = app.add_subcommand("subcom");
+    subcom->add_option("--val", two);
+    auto subsubcom = subcom->add_subcommand("subsubcom");
+    subsubcom->add_option("--val", three);
+
+    run();
+
+    CHECK(one == 1);
+    CHECK(two == 2);
+    CHECK(three == 3);
+
+    CHECK(0U == subcom->count());
+    CHECK(!*subcom);
 }
 
 TEST_CASE_METHOD(TApp, "IniSubcommandConfigurable", "[config]") {
@@ -1107,6 +1291,130 @@ TEST_CASE_METHOD(TApp, "IniSubcommandConfigurablePreParse", "[config]") {
     CHECK(0U == subcom2->count());
 }
 
+TEST_CASE_METHOD(TApp, "IniSection", "[config]") {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    app.get_config_formatter_base()->section("config");
+
+    {
+        std::ofstream out{tmpini};
+        out << "[config]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "subsubcom.val=3" << std::endl;
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+    }
+
+    int val{0};
+    app.add_option("--val", val);
+
+    run();
+
+    CHECK(2 == val);
+}
+
+TEST_CASE_METHOD(TApp, "IniSection2", "[config]") {
+
+    TempFile tmpini{"TestIniTmp.ini"};
+
+    app.set_config("--config", tmpini);
+    app.get_config_formatter_base()->section("config");
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[config]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "subsubcom.val=3" << std::endl;
+    }
+
+    int val{0};
+    app.add_option("--val", val);
+
+    run();
+
+    CHECK(2 == val);
+}
+
+TEST_CASE_METHOD(TApp, "jsonLikeParsing", "[config]") {
+
+    TempFile tmpjson{"TestJsonTmp.json"};
+
+    app.set_config("--config", tmpjson);
+    app.get_config_formatter_base()->valueSeparator(':');
+
+    {
+        std::ofstream out{tmpjson};
+        out << "{" << std::endl;
+        out << "\"val\":1," << std::endl;
+        out << "\"val2\":\"test\"," << std::endl;
+        out << "\"flag\":true" << std::endl;
+        out << "}" << std::endl;
+    }
+
+    int val{0};
+    app.add_option("--val", val);
+    std::string val2{0};
+    app.add_option("--val2", val2);
+
+    bool flag{false};
+    app.add_flag("--flag", flag);
+
+    run();
+
+    CHECK(1 == val);
+    CHECK(val2 == "test");
+    CHECK(flag);
+}
+
+TEST_CASE_METHOD(TApp, "TomlSectionNumber", "[config]") {
+
+    TempFile tmpini{"TestTomlTmp.toml"};
+
+    app.set_config("--config", tmpini);
+    app.get_config_formatter_base()->section("config")->index(0);
+
+    {
+        std::ofstream out{tmpini};
+        out << "[default]" << std::endl;
+        out << "val=1" << std::endl;
+        out << "[[config]]" << std::endl;
+        out << "val=2" << std::endl;
+        out << "subsubcom.val=3" << std::endl;
+        out << "[[config]]" << std::endl;
+        out << "val=4" << std::endl;
+        out << "subsubcom.val=3" << std::endl;
+        out << "[[config]]" << std::endl;
+        out << "val=6" << std::endl;
+        out << "subsubcom.val=3" << std::endl;
+    }
+
+    int val{0};
+    app.add_option("--val", val);
+
+    run();
+
+    CHECK(2 == val);
+
+    auto &index = app.get_config_formatter_base()->indexRef();
+    index = 1;
+    run();
+
+    CHECK(4 == val);
+
+    index = -1;
+    run();
+    // Take the first section in this case
+    CHECK(2 == val);
+    index = 2;
+    run();
+
+    CHECK(6 == val);
+}
+
 TEST_CASE_METHOD(TApp, "IniSubcommandConfigurableParseComplete", "[config]") {
 
     TempFile tmpini{"TestIniTmp.ini"};
@@ -1685,7 +1993,7 @@ TEST_CASE_METHOD(TApp, "TomlOutputHiddenOptions", "[config]") {
     const std::string description2 = "Second description.";
     app.add_flag("--" + flag1, description1)->group("group1");
     app.add_flag("--" + flag2, description2)->group("group2");
-    app.add_option("--dval", val, "", true)->group("");
+    app.add_option("--dval", val)->capture_default_str()->group("");
 
     run();
 
@@ -1701,6 +2009,16 @@ TEST_CASE_METHOD(TApp, "TomlOutputHiddenOptions", "[config]") {
     CHECK(std::string::npos == loc);
 }
 
+TEST_CASE_METHOD(TApp, "TomlOutputAppMultiLineDescription", "[config]") {
+    app.description("Some short app description.\n"
+                    "That has multiple lines.");
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    CHECK_THAT(str, Contains("# Some short app description.\n"));
+    CHECK_THAT(str, Contains("# That has multiple lines.\n"));
+}
+
 TEST_CASE_METHOD(TApp, "TomlOutputMultiLineDescription", "[config]") {
     std::string flag = "some_flag";
     const std::string description = "Some short description.\nThat has lines.";
@@ -1714,6 +2032,35 @@ TEST_CASE_METHOD(TApp, "TomlOutputMultiLineDescription", "[config]") {
     CHECK_THAT(str, Contains(flag + "=false\n"));
 }
 
+TEST_CASE_METHOD(TApp, "TomlOutputOptionGroupMultiLineDescription", "[config]") {
+    std::string flag = "flag";
+    const std::string description = "Short flag description.\n";
+    auto og = app.add_option_group("group");
+    og->description("Option group description.\n"
+                    "That has multiple lines.");
+    og->add_flag("--" + flag, description);
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    CHECK_THAT(str, Contains("# Option group description.\n"));
+    CHECK_THAT(str, Contains("# That has multiple lines.\n"));
+}
+
+TEST_CASE_METHOD(TApp, "TomlOutputSubcommandMultiLineDescription", "[config]") {
+    std::string flag = "flag";
+    const std::string description = "Short flag description.\n";
+    auto subcom = app.add_subcommand("subcommand");
+    subcom->configurable();
+    subcom->description("Subcommand description.\n"
+                        "That has multiple lines.");
+    subcom->add_flag("--" + flag, description);
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    CHECK_THAT(str, Contains("# Subcommand description.\n"));
+    CHECK_THAT(str, Contains("# That has multiple lines.\n"));
+}
+
 TEST_CASE_METHOD(TApp, "TomlOutputOptionGroup", "[config]") {
     std::string flag1 = "flagnr1";
     std::string flag2 = "flagnr2";
@@ -1723,7 +2070,7 @@ TEST_CASE_METHOD(TApp, "TomlOutputOptionGroup", "[config]") {
     app.add_flag("--" + flag1, description1)->group("group1");
     app.add_flag("--" + flag2, description2)->group("group2");
     auto og = app.add_option_group("group3", "g3 desc");
-    og->add_option("--dval", val, "", true)->group("");
+    og->add_option("--dval", val)->capture_default_str()->group("");
 
     run();
 
@@ -1809,7 +2156,7 @@ TEST_CASE_METHOD(TApp, "TomlOutputSet", "[config]") {
 TEST_CASE_METHOD(TApp, "TomlOutputDefault", "[config]") {
 
     int v{7};
-    app.add_option("--simple", v, "", true);
+    app.add_option("--simple", v)->capture_default_str();
 
     run();
 
@@ -1888,6 +2235,27 @@ TEST_CASE_METHOD(TApp, "TomlOutputSubsubcomConfigurable", "[config]") {
     CHECK(std::string::npos == str.find("sub2.newest=true"));
 }
 
+TEST_CASE_METHOD(TApp, "TomlOutputSubcomNonConfigurable", "[config]") {
+
+    app.add_flag("--simple");
+    auto subcom = app.add_subcommand("other", "other_descriptor")->configurable();
+    subcom->add_flag("--newer");
+
+    auto subcom2 = app.add_subcommand("sub2", "descriptor2");
+    subcom2->add_flag("--newest")->configurable(false);
+
+    args = {"--simple", "other", "--newer", "sub2", "--newest"};
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    CHECK_THAT(str, Contains("other_descriptor"));
+    CHECK_THAT(str, Contains("simple=true"));
+    CHECK_THAT(str, Contains("[other]"));
+    CHECK_THAT(str, Contains("newer=true"));
+    CHECK_THAT(str, !Contains("newest"));
+    CHECK_THAT(str, !Contains("descriptor2"));
+}
+
 TEST_CASE_METHOD(TApp, "TomlOutputSubsubcomConfigurableDeep", "[config]") {
 
     app.add_flag("--simple");
@@ -1934,10 +2302,10 @@ TEST_CASE_METHOD(TApp, "TomlOutputQuoted", "[config]") {
 TEST_CASE_METHOD(TApp, "DefaultsTomlOutputQuoted", "[config]") {
 
     std::string val1{"I am a string"};
-    app.add_option("--val1", val1, "", true);
+    app.add_option("--val1", val1)->capture_default_str();
 
     std::string val2{R"(I am a "confusing" string)"};
-    app.add_option("--val2", val2, "", true);
+    app.add_option("--val2", val2)->capture_default_str();
 
     run();
 
@@ -2068,7 +2436,7 @@ TEST_CASE_METHOD(TApp, "IniOutputHiddenOptions", "[config]") {
     const std::string description2 = "Second description.";
     app.add_flag("--" + flag1, description1)->group("group1");
     app.add_flag("--" + flag2, description2)->group("group2");
-    app.add_option("--dval", val, "", true)->group("");
+    app.add_option("--dval", val)->capture_default_str()->group("");
     app.config_formatter(std::make_shared<CLI::ConfigINI>());
     run();
 
@@ -2084,6 +2452,17 @@ TEST_CASE_METHOD(TApp, "IniOutputHiddenOptions", "[config]") {
     CHECK(std::string::npos == loc);
 }
 
+TEST_CASE_METHOD(TApp, "IniOutputAppMultiLineDescription", "[config]") {
+    app.description("Some short app description.\n"
+                    "That has multiple lines.");
+    app.config_formatter(std::make_shared<CLI::ConfigINI>());
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    CHECK_THAT(str, Contains("; Some short app description.\n"));
+    CHECK_THAT(str, Contains("; That has multiple lines.\n"));
+}
+
 TEST_CASE_METHOD(TApp, "IniOutputMultiLineDescription", "[config]") {
     std::string flag = "some_flag";
     const std::string description = "Some short description.\nThat has lines.";
@@ -2097,6 +2476,37 @@ TEST_CASE_METHOD(TApp, "IniOutputMultiLineDescription", "[config]") {
     CHECK_THAT(str, Contains(flag + "=false\n"));
 }
 
+TEST_CASE_METHOD(TApp, "IniOutputOptionGroupMultiLineDescription", "[config]") {
+    std::string flag = "flag";
+    const std::string description = "Short flag description.\n";
+    auto og = app.add_option_group("group");
+    og->description("Option group description.\n"
+                    "That has multiple lines.");
+    og->add_flag("--" + flag, description);
+    app.config_formatter(std::make_shared<CLI::ConfigINI>());
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    CHECK_THAT(str, Contains("; Option group description.\n"));
+    CHECK_THAT(str, Contains("; That has multiple lines.\n"));
+}
+
+TEST_CASE_METHOD(TApp, "IniOutputSubcommandMultiLineDescription", "[config]") {
+    std::string flag = "flag";
+    const std::string description = "Short flag description.\n";
+    auto subcom = app.add_subcommand("subcommand");
+    subcom->configurable();
+    subcom->description("Subcommand description.\n"
+                        "That has multiple lines.");
+    subcom->add_flag("--" + flag, description);
+    app.config_formatter(std::make_shared<CLI::ConfigINI>());
+    run();
+
+    std::string str = app.config_to_str(true, true);
+    CHECK_THAT(str, Contains("; Subcommand description.\n"));
+    CHECK_THAT(str, Contains("; That has multiple lines.\n"));
+}
+
 TEST_CASE_METHOD(TApp, "IniOutputOptionGroup", "[config]") {
     std::string flag1 = "flagnr1";
     std::string flag2 = "flagnr2";
@@ -2106,7 +2516,7 @@ TEST_CASE_METHOD(TApp, "IniOutputOptionGroup", "[config]") {
     app.add_flag("--" + flag1, description1)->group("group1");
     app.add_flag("--" + flag2, description2)->group("group2");
     auto og = app.add_option_group("group3", "g3 desc");
-    og->add_option("--dval", val, "", true)->group("");
+    og->add_option("--dval", val)->capture_default_str()->group("");
     app.config_formatter(std::make_shared<CLI::ConfigINI>());
     run();
 
@@ -2177,7 +2587,7 @@ TEST_CASE_METHOD(TApp, "IniOutputSet", "[config]") {
 TEST_CASE_METHOD(TApp, "IniOutputDefault", "[config]") {
 
     int v{7};
-    app.add_option("--simple", v, "", true);
+    app.add_option("--simple", v)->capture_default_str();
     app.config_formatter(std::make_shared<CLI::ConfigINI>());
     run();
 
@@ -2202,6 +2612,21 @@ TEST_CASE_METHOD(TApp, "IniOutputSubcom", "[config]") {
     CHECK_THAT(str, Contains("other.newer=true"));
 }
 
+TEST_CASE_METHOD(TApp, "IniOutputSubcomCustomSep", "[config]") {
+
+    app.add_flag("--simple");
+    auto subcom = app.add_subcommand("other");
+    subcom->add_flag("--newer");
+    app.config_formatter(std::make_shared<CLI::ConfigINI>());
+    app.get_config_formatter_base()->parentSeparator(':');
+    args = {"--simple", "other", "--newer"};
+    run();
+
+    std::string str = app.config_to_str();
+    CHECK_THAT(str, Contains("simple=true"));
+    CHECK_THAT(str, Contains("other:newer=true"));
+}
+
 TEST_CASE_METHOD(TApp, "IniOutputSubcomConfigurable", "[config]") {
 
     app.add_flag("--simple");
@@ -2235,6 +2660,24 @@ TEST_CASE_METHOD(TApp, "IniOutputSubsubcom", "[config]") {
     CHECK_THAT(str, Contains("other.sub2.newest=true"));
 }
 
+TEST_CASE_METHOD(TApp, "IniOutputSubsubcomCustomSep", "[config]") {
+
+    app.add_flag("--simple");
+    auto subcom = app.add_subcommand("other");
+    subcom->add_flag("--newer");
+    auto subsubcom = subcom->add_subcommand("sub2");
+    subsubcom->add_flag("--newest");
+    app.config_formatter(std::make_shared<CLI::ConfigINI>());
+    app.get_config_formatter_base()->parentSeparator('|');
+    args = {"--simple", "other", "--newer", "sub2", "--newest"};
+    run();
+
+    std::string str = app.config_to_str();
+    CHECK_THAT(str, Contains("simple=true"));
+    CHECK_THAT(str, Contains("other|newer=true"));
+    CHECK_THAT(str, Contains("other|sub2|newest=true"));
+}
+
 TEST_CASE_METHOD(TApp, "IniOutputSubsubcomConfigurable", "[config]") {
 
     app.add_flag("--simple");
@@ -2302,10 +2745,10 @@ TEST_CASE_METHOD(TApp, "IniOutputQuoted", "[config]") {
 TEST_CASE_METHOD(TApp, "DefaultsIniOutputQuoted", "[config]") {
 
     std::string val1{"I am a string"};
-    app.add_option("--val1", val1, "", true);
+    app.add_option("--val1", val1)->capture_default_str();
 
     std::string val2{R"(I am a "confusing" string)"};
-    app.add_option("--val2", val2, "", true);
+    app.add_option("--val2", val2)->capture_default_str();
     app.config_formatter(std::make_shared<CLI::ConfigINI>());
     run();
 
diff --git a/packages/CLI11/tests/CreationTest.cpp b/packages/CLI11/tests/CreationTest.cpp
index 2a70f70d419552e96e13e0cf3a3c6f524a64ce68..47900570744aaa55a49555ef9a40054c4655d03e 100644
--- a/packages/CLI11/tests/CreationTest.cpp
+++ b/packages/CLI11/tests/CreationTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -739,13 +739,13 @@ TEST_CASE_METHOD(TApp, "MakeUnstreamableOptions", "[creation]") {
     app.add_option("--value", value);
 
     // This used to fail to build, since it tries to stream from Unstreamable
-    app.add_option("--value2", value, "", false);
+    app.add_option("--value2", value);
 
     std::vector<Unstreamable> values;
     app.add_option("--values", values);
 
     // This used to fail to build, since it tries to stream from Unstreamable
-    app.add_option("--values2", values, "", false);
+    app.add_option("--values2", values);
 
     args = {"--value", "45"};
     run();
diff --git a/packages/CLI11/tests/DeprecatedTest.cpp b/packages/CLI11/tests/DeprecatedTest.cpp
index cf9987c6d97b6a8251630f60deff8e5466138e28..f763a89d4ab86209851789deff57702a955e03d3 100644
--- a/packages/CLI11/tests/DeprecatedTest.cpp
+++ b/packages/CLI11/tests/DeprecatedTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -12,214 +12,3 @@ TEST_CASE("Deprecated: Empty", "[deprecated]") {
     // No deprecated features at this time.
     CHECK(true);
 }
-
-// Classic sets
-
-TEST_CASE("THelp: Defaults", "[deprecated]") {
-    CLI::App app{"My prog"};
-
-    int one{1}, two{2};
-    app.add_option("--one", one, "Help for one", true);
-    app.add_option("--set", two, "Help for set", true)->check(CLI::IsMember({2, 3, 4}));
-
-    std::string help = app.help();
-
-    CHECK_THAT(help, Contains("--one"));
-    CHECK_THAT(help, Contains("--set"));
-    CHECK_THAT(help, Contains("1"));
-    CHECK_THAT(help, Contains("=2"));
-    CHECK_THAT(help, Contains("2,3,4"));
-}
-
-TEST_CASE("THelp: VectorOpts", "[deprecated]") {
-    CLI::App app{"My prog"};
-    std::vector<int> x = {1, 2};
-    app.add_option("-q,--quick", x, "", true);
-
-    std::string help = app.help();
-
-    CHECK_THAT(help, Contains("INT=[1,2] ..."));
-}
-
-TEST_CASE("THelp: SetLower", "[deprecated]") {
-    CLI::App app{"My prog"};
-
-    std::string def{"One"};
-    app.add_option("--set", def, "Help for set", true)->check(CLI::IsMember({"oNe", "twO", "THREE"}));
-
-    std::string help = app.help();
-
-    CHECK_THAT(help, Contains("--set"));
-    CHECK_THAT(help, Contains("=One"));
-    CHECK_THAT(help, Contains("oNe"));
-    CHECK_THAT(help, Contains("twO"));
-    CHECK_THAT(help, Contains("THREE"));
-}
-
-TEST_CASE("THelp: ChangingSetDefaulted", "[deprecated]") {
-    CLI::App app;
-
-    std::set<int> vals{1, 2, 3};
-    int val = 2;
-    app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals));
-
-    std::string help = app.help();
-
-    CHECK_THAT(help, Contains("1"));
-    CHECK_THAT(help, !Contains("4"));
-
-    vals.insert(4);
-    vals.erase(1);
-
-    help = app.help();
-
-    CHECK_THAT(help, !Contains("1"));
-    CHECK_THAT(help, Contains("4"));
-}
-
-TEST_CASE("THelp: ChangingCaselessSetDefaulted", "[deprecated]") {
-    CLI::App app;
-
-    std::set<std::string> vals{"1", "2", "3"};
-    std::string val = "2";
-    app.add_option("--val", val, "", true)->check(CLI::IsMember(&vals, CLI::ignore_case));
-
-    std::string help = app.help();
-
-    CHECK_THAT(help, Contains("1"));
-    CHECK_THAT(help, !Contains("4"));
-
-    vals.insert("4");
-    vals.erase("1");
-
-    help = app.help();
-
-    CHECK_THAT(help, !Contains("1"));
-    CHECK_THAT(help, Contains("4"));
-}
-
-TEST_CASE_METHOD(TApp, "DefaultOpts", "[deprecated]") {
-
-    int i = 3;
-    std::string s = "HI";
-
-    app.add_option("-i,i", i, "", false);
-    app.add_option("-s,s", s, "", true);
-
-    args = {"-i2", "9"};
-
-    run();
-
-    CHECK(app.count("i") == 1u);
-    CHECK(app.count("-s") == 1u);
-    CHECK(i == 2);
-    CHECK(s == "9");
-}
-
-TEST_CASE_METHOD(TApp, "VectorDefaultedFixedString", "[deprecated]") {
-    std::vector<std::string> strvec{"one"};
-    std::vector<std::string> answer{"mystring", "mystring2", "mystring3"};
-
-    CLI::Option *opt = app.add_option("-s,--string", strvec, "", true)->expected(3);
-    CHECK(opt->get_expected() == 3);
-
-    args = {"--string", "mystring", "mystring2", "mystring3"};
-    run();
-    CHECK(app.count("--string") == 3u);
-    CHECK(strvec == answer);
-}
-
-TEST_CASE_METHOD(TApp, "DefaultedResult", "[deprecated]") {
-    std::string sval = "NA";
-    int ival;
-    auto opts = app.add_option("--string", sval, "", true);
-    auto optv = app.add_option("--val", ival);
-    args = {};
-    run();
-    CHECK("NA" == sval);
-    std::string nString;
-    opts->results(nString);
-    CHECK("NA" == nString);
-    int newIval;
-    // CHECK_THROWS_AS (optv->results(newIval), CLI::ConversionError);
-    optv->default_str("442");
-    optv->results(newIval);
-    CHECK(442 == newIval);
-}
-
-TEST_CASE_METHOD(TApp, "OptionWithDefaults", "[deprecated]") {
-    int someint = 2;
-    app.add_option("-a", someint, "", true);
-
-    args = {"-a1", "-a2"};
-
-    CHECK_THROWS_AS(run(), CLI::ArgumentMismatch);
-}
-
-// #209
-TEST_CASE_METHOD(TApp, "CustomUserSepParse", "[deprecated]") {
-
-    std::vector<int> vals = {1, 2, 3};
-    args = {"--idx", "1,2,3"};
-    auto opt = app.add_option("--idx", vals)->delimiter(',');
-    run();
-    CHECK(std::vector<int>({1, 2, 3}) == vals);
-    std::vector<int> vals2;
-    // check that the results vector gets the results in the same way
-    opt->results(vals2);
-    CHECK(vals == vals2);
-
-    app.remove_option(opt);
-
-    app.add_option("--idx", vals, "", true)->delimiter(',');
-    run();
-    CHECK(std::vector<int>({1, 2, 3}) == vals);
-}
-
-// #209
-TEST_CASE_METHOD(TApp, "CustomUserSepParse2", "[deprecated]") {
-
-    std::vector<int> vals = {1, 2, 3};
-    args = {"--idx", "1,2,"};
-    auto opt = app.add_option("--idx", vals)->delimiter(',');
-    run();
-    CHECK(std::vector<int>({1, 2}) == vals);
-
-    app.remove_option(opt);
-
-    app.add_option("--idx", vals, "", true)->delimiter(',');
-    run();
-    CHECK(std::vector<int>({1, 2}) == vals);
-}
-//
-// #209
-TEST_CASE_METHOD(TApp, "CustomUserSepParse4", "[deprecated]") {
-
-    std::vector<int> vals;
-    args = {"--idx", "1,    2"};
-    auto opt = app.add_option("--idx", vals, "", true)->delimiter(',');
-    run();
-    CHECK(std::vector<int>({1, 2}) == vals);
-
-    app.remove_option(opt);
-
-    app.add_option("--idx", vals)->delimiter(',');
-    run();
-    CHECK(std::vector<int>({1, 2}) == vals);
-}
-
-// #218
-TEST_CASE_METHOD(TApp, "CustomUserSepParse5", "[deprecated]") {
-
-    std::vector<std::string> bar;
-    args = {"this", "is", "a", "test"};
-    auto opt = app.add_option("bar", bar, "bar");
-    run();
-    CHECK(std::vector<std::string>({"this", "is", "a", "test"}) == bar);
-
-    app.remove_option(opt);
-    args = {"this", "is", "a", "test"};
-    app.add_option("bar", bar, "bar", true);
-    run();
-    CHECK(std::vector<std::string>({"this", "is", "a", "test"}) == bar);
-}
diff --git a/packages/CLI11/tests/FormatterTest.cpp b/packages/CLI11/tests/FormatterTest.cpp
index 22da56f0e543af444e9caadd0c60a2932fe35018..1516945de96458ef905e1a301561152d3c49f8d4 100644
--- a/packages/CLI11/tests/FormatterTest.cpp
+++ b/packages/CLI11/tests/FormatterTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/HelpTest.cpp b/packages/CLI11/tests/HelpTest.cpp
index 48089f6fc76ea10a7446447ebf8114fdd9797553..cb8c81ab810ec78563dacaf9ec19a98e70c5853f 100644
--- a/packages/CLI11/tests/HelpTest.cpp
+++ b/packages/CLI11/tests/HelpTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -1155,6 +1155,7 @@ TEST_CASE("THelp: ChangingDefaults", "[help]") {
     std::vector<int> x = {1, 2};
     CLI::Option *opt = app.add_option("-q,--quick", x);
     x = {3, 4};
+    CHECK(x[0] == 3);
 
     opt->capture_default_str();
 
@@ -1162,6 +1163,7 @@ TEST_CASE("THelp: ChangingDefaults", "[help]") {
     std::string help = app.help();
 
     CHECK_THAT(help, Contains("INT=[3,4] ..."));
+    CHECK(x[0] == 5);
 }
 
 TEST_CASE("THelp: ChangingDefaultsWithAutoCapture", "[help]") {
@@ -1170,8 +1172,10 @@ TEST_CASE("THelp: ChangingDefaultsWithAutoCapture", "[help]") {
     app.option_defaults()->always_capture_default();
 
     std::vector<int> x = {1, 2};
+    CHECK(x[0] == 1);
     app.add_option("-q,--quick", x);
     x = {3, 4};
+    CHECK(x[0] == 3);
 
     std::string help = app.help();
 
@@ -1220,6 +1224,21 @@ TEST_CASE("TVersion: callback_flag", "[help]") {
     CHECK_THAT(vers, Contains("VERSION"));
 }
 
+TEST_CASE("TVersion: help", "[help]") {
+
+    CLI::App app;
+
+    app.set_version_flag("-v,--version", "version_string", "help_for_version");
+
+    auto hvers = app.help();
+    CHECK_THAT(hvers, Contains("help_for_version"));
+
+    app.set_version_flag(
+        "-v", []() { return std::string("VERSION2 " CLI11_VERSION); }, "help_for_version2");
+    hvers = app.help();
+    CHECK_THAT(hvers, Contains("help_for_version2"));
+}
+
 TEST_CASE("TVersion: parse_throw", "[help]") {
 
     CLI::App app;
diff --git a/packages/CLI11/tests/HelpersTest.cpp b/packages/CLI11/tests/HelpersTest.cpp
index 0c032fbce389899324025f4c8759e328e5ffaea2..7a497aa1cb05c0af6b77e5668063f57f9525c9bf 100644
--- a/packages/CLI11/tests/HelpersTest.cpp
+++ b/packages/CLI11/tests/HelpersTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -155,13 +155,14 @@ TEST_CASE("String: InvalidName", "[helpers]") {
     CHECK(CLI::detail::valid_name_string("valid"));
     CHECK_FALSE(CLI::detail::valid_name_string("-invalid"));
     CHECK(CLI::detail::valid_name_string("va-li-d"));
-    CHECK_FALSE(CLI::detail::valid_name_string("vali&d"));
+    CHECK_FALSE(CLI::detail::valid_name_string("valid{}"));
     CHECK(CLI::detail::valid_name_string("_valid"));
-    CHECK_FALSE(CLI::detail::valid_name_string("/valid"));
+    CHECK(CLI::detail::valid_name_string("/valid"));
     CHECK(CLI::detail::valid_name_string("vali?d"));
     CHECK(CLI::detail::valid_name_string("@@@@"));
     CHECK(CLI::detail::valid_name_string("b@d2?"));
     CHECK(CLI::detail::valid_name_string("2vali?d"));
+    CHECK_FALSE(CLI::detail::valid_name_string("!valid"));
 }
 
 TEST_CASE("StringTools: Modify", "[helpers]") {
@@ -997,7 +998,12 @@ TEST_CASE("Types: TypeName", "[helpers]") {
     std::string umapName = CLI::detail::type_name<std::unordered_map<int, std::tuple<std::string, double>>>();
     CHECK(umapName == "[INT,[TEXT,FLOAT]]");
 
+    // On older compilers, this may show up as other/TEXT
     vclass = CLI::detail::classify_object<std::atomic<int>>::value;
+    CHECK((CLI::detail::object_category::wrapper_value == vclass || CLI::detail::object_category::other == vclass));
+
+    std::string atomic_name = CLI::detail::type_name<std::atomic<int>>();
+    CHECK((atomic_name == "INT" || atomic_name == "TEXT"));
 }
 
 TEST_CASE("Types: OverflowSmall", "[helpers]") {
@@ -1128,8 +1134,8 @@ TEST_CASE("Types: LexicalConversionDouble", "[helpers]") {
     CHECK((float)x == Approx((float)9.12));
 
     CLI::results_t bad_input = {"hello"};
-    res = CLI::detail::lexical_conversion<long double, double>(input, x);
-    CHECK(res);
+    res = CLI::detail::lexical_conversion<long double, double>(bad_input, x);
+    CHECK_FALSE(res);
 }
 
 TEST_CASE("Types: LexicalConversionDoubleTuple", "[helpers]") {
@@ -1140,8 +1146,8 @@ TEST_CASE("Types: LexicalConversionDoubleTuple", "[helpers]") {
     CHECK(std::get<0>(x) == Approx(9.12));
 
     CLI::results_t bad_input = {"hello"};
-    res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(input, x);
-    CHECK(res);
+    res = CLI::detail::lexical_conversion<decltype(x), decltype(x)>(bad_input, x);
+    CHECK_FALSE(res);
 }
 
 TEST_CASE("Types: LexicalConversionVectorDouble", "[helpers]") {
diff --git a/packages/CLI11/tests/NewParseTest.cpp b/packages/CLI11/tests/NewParseTest.cpp
index d9d9dbf91ca777d8c70fa2f289316e535acec0f0..30a0c9f41239ebc619dab762206437ce4b883096 100644
--- a/packages/CLI11/tests/NewParseTest.cpp
+++ b/packages/CLI11/tests/NewParseTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -13,49 +13,9 @@ using Catch::Matchers::Contains;
 
 using cx = std::complex<double>;
 
-TEST_CASE_METHOD(TApp, "Complex", "[newparse]") {
-    cx comp{1, 2};
-    app.add_complex("-c,--complex", comp, "", true);
-
-    args = {"-c", "4", "3"};
-
-    std::string help = app.help();
-    CHECK_THAT(help, Contains("1"));
-    CHECK_THAT(help, Contains("2"));
-    CHECK_THAT(help, Contains("COMPLEX"));
-
-    CHECK(comp.real() == Approx(1));
-    CHECK(comp.imag() == Approx(2));
-
-    run();
-
-    CHECK(comp.real() == Approx(4));
-    CHECK(comp.imag() == Approx(3));
-}
-
 TEST_CASE_METHOD(TApp, "ComplexOption", "[newparse]") {
     cx comp{1, 2};
-    app.add_option("-c,--complex", comp, "", true);
-
-    args = {"-c", "4", "3"};
-
-    std::string help = app.help();
-    CHECK_THAT(help, Contains("1"));
-    CHECK_THAT(help, Contains("2"));
-    CHECK_THAT(help, Contains("COMPLEX"));
-
-    CHECK(comp.real() == Approx(1));
-    CHECK(comp.imag() == Approx(2));
-
-    run();
-
-    CHECK(comp.real() == Approx(4));
-    CHECK(comp.imag() == Approx(3));
-}
-
-TEST_CASE_METHOD(TApp, "ComplexFloat", "[newparse]") {
-    std::complex<float> comp{1, 2};
-    app.add_complex<std::complex<float>, float>("-c,--complex", comp, "", true);
+    app.add_option("-c,--complex", comp)->capture_default_str();
 
     args = {"-c", "4", "3"};
 
@@ -75,7 +35,7 @@ TEST_CASE_METHOD(TApp, "ComplexFloat", "[newparse]") {
 
 TEST_CASE_METHOD(TApp, "ComplexFloatOption", "[newparse]") {
     std::complex<float> comp{1, 2};
-    app.add_option("-c,--complex", comp, "", true);
+    app.add_option("-c,--complex", comp)->capture_default_str();
 
     args = {"-c", "4", "3"};
 
@@ -93,41 +53,9 @@ TEST_CASE_METHOD(TApp, "ComplexFloatOption", "[newparse]") {
     CHECK(comp.imag() == Approx(3));
 }
 
-TEST_CASE_METHOD(TApp, "ComplexWithDelimiter", "[newparse]") {
-    cx comp{1, 2};
-    app.add_complex("-c,--complex", comp, "", true)->delimiter('+');
-
-    args = {"-c", "4+3i"};
-
-    std::string help = app.help();
-    CHECK_THAT(help, Contains("1"));
-    CHECK_THAT(help, Contains("2"));
-    CHECK_THAT(help, Contains("COMPLEX"));
-
-    CHECK(comp.real() == Approx(1));
-    CHECK(comp.imag() == Approx(2));
-
-    run();
-
-    CHECK(comp.real() == Approx(4));
-    CHECK(comp.imag() == Approx(3));
-
-    args = {"-c", "5+-3i"};
-    run();
-
-    CHECK(comp.real() == Approx(5));
-    CHECK(comp.imag() == Approx(-3));
-
-    args = {"-c", "6", "-4i"};
-    run();
-
-    CHECK(comp.real() == Approx(6));
-    CHECK(comp.imag() == Approx(-4));
-}
-
 TEST_CASE_METHOD(TApp, "ComplexWithDelimiterOption", "[newparse]") {
     cx comp{1, 2};
-    app.add_option("-c,--complex", comp, "", true)->delimiter('+');
+    app.add_option("-c,--complex", comp)->capture_default_str()->delimiter('+');
 
     args = {"-c", "4+3i"};
 
@@ -157,18 +85,6 @@ TEST_CASE_METHOD(TApp, "ComplexWithDelimiterOption", "[newparse]") {
     CHECK(comp.imag() == Approx(-4));
 }
 
-TEST_CASE_METHOD(TApp, "ComplexIgnoreI", "[newparse]") {
-    cx comp{1, 2};
-    app.add_complex("-c,--complex", comp);
-
-    args = {"-c", "4", "3i"};
-
-    run();
-
-    CHECK(comp.real() == Approx(4));
-    CHECK(comp.imag() == Approx(3));
-}
-
 TEST_CASE_METHOD(TApp, "ComplexIgnoreIOption", "[newparse]") {
     cx comp{1, 2};
     app.add_option("-c,--complex", comp);
@@ -181,40 +97,6 @@ TEST_CASE_METHOD(TApp, "ComplexIgnoreIOption", "[newparse]") {
     CHECK(comp.imag() == Approx(3));
 }
 
-TEST_CASE_METHOD(TApp, "ComplexSingleArg", "[newparse]") {
-    cx comp{1, 2};
-    app.add_complex("-c,--complex", comp);
-
-    args = {"-c", "4"};
-    run();
-    CHECK(comp.real() == Approx(4));
-    CHECK(comp.imag() == Approx(0));
-
-    args = {"-c", "4-2i"};
-    run();
-    CHECK(comp.real() == Approx(4));
-    CHECK(comp.imag() == Approx(-2));
-    args = {"-c", "4+2i"};
-    run();
-    CHECK(comp.real() == Approx(4));
-    CHECK(comp.imag() == Approx(2));
-
-    args = {"-c", "-4+2j"};
-    run();
-    CHECK(comp.real() == Approx(-4));
-    CHECK(comp.imag() == Approx(2));
-
-    args = {"-c", "-4.2-2j"};
-    run();
-    CHECK(comp.real() == Approx(-4.2));
-    CHECK(comp.imag() == Approx(-2));
-
-    args = {"-c", "-4.2-2.7i"};
-    run();
-    CHECK(comp.real() == Approx(-4.2));
-    CHECK(comp.imag() == Approx(-2.7));
-}
-
 TEST_CASE_METHOD(TApp, "ComplexSingleArgOption", "[newparse]") {
     cx comp{1, 2};
     app.add_option("-c,--complex", comp);
@@ -249,29 +131,6 @@ TEST_CASE_METHOD(TApp, "ComplexSingleArgOption", "[newparse]") {
     CHECK(comp.imag() == Approx(-2.7));
 }
 
-TEST_CASE_METHOD(TApp, "ComplexSingleImag", "[newparse]") {
-    cx comp{1, 2};
-    app.add_complex("-c,--complex", comp);
-
-    args = {"-c", "4j"};
-    run();
-    CHECK(comp.real() == Approx(0));
-    CHECK(comp.imag() == Approx(4));
-
-    args = {"-c", "-4j"};
-    run();
-    CHECK(comp.real() == Approx(0));
-    CHECK(comp.imag() == Approx(-4));
-    args = {"-c", "-4"};
-    run();
-    CHECK(comp.real() == Approx(-4));
-    CHECK(comp.imag() == Approx(0));
-    args = {"-c", "+4"};
-    run();
-    CHECK(comp.real() == Approx(4));
-    CHECK(comp.imag() == Approx(0));
-}
-
 TEST_CASE_METHOD(TApp, "ComplexSingleImagOption", "[newparse]") {
     cx comp{1, 2};
     app.add_option("-c,--complex", comp);
diff --git a/packages/CLI11/tests/OptionGroupTest.cpp b/packages/CLI11/tests/OptionGroupTest.cpp
index 175f31dc3daf5de4da456c7a304a235d31aec42e..ac6684f49407ff490a5878759ea8421449c3560d 100644
--- a/packages/CLI11/tests/OptionGroupTest.cpp
+++ b/packages/CLI11/tests/OptionGroupTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -23,6 +23,16 @@ TEST_CASE_METHOD(TApp, "BasicOptionGroup", "[optiongroup]") {
     CHECK(1u == app.count_all());
 }
 
+TEST_CASE_METHOD(TApp, "OptionGroupInvalidNames", "[optiongroup]") {
+    CHECK_THROWS_AS(app.add_option_group("clusters\ncluster2", "description"), CLI::IncorrectConstruction);
+
+    std::string groupName("group1");
+    groupName += '\0';
+    groupName.append("group2");
+
+    CHECK_THROWS_AS(app.add_option_group(groupName), CLI::IncorrectConstruction);
+}
+
 TEST_CASE_METHOD(TApp, "BasicOptionGroupExact", "[optiongroup]") {
     auto ogroup = app.add_option_group("clusters");
     int res{0};
@@ -593,7 +603,7 @@ TEST_CASE_METHOD(ManyGroups, "SameSubcommand", "[optiongroup]") {
     // so when the subcommands are disabled they can have the same name
     sub1->disabled(false);
     sub2->disabled(false);
-    // if they are reenabled they are not checked for overlap on enabling so they can have the same name
+    // if they are re-enabled they are not checked for overlap on enabling so they can have the same name
     args = {"sub1", "sub1", "sub1"};
 
     run();
diff --git a/packages/CLI11/tests/OptionTypeTest.cpp b/packages/CLI11/tests/OptionTypeTest.cpp
index a2f893bec598d56cbf17bd76c1de5c62b83eb19d..19b69746c825871713be3e0d5f14be005ba236ee 100644
--- a/packages/CLI11/tests/OptionTypeTest.cpp
+++ b/packages/CLI11/tests/OptionTypeTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -240,7 +240,7 @@ TEST_CASE_METHOD(TApp, "CharOption", "[optiontype]") {
 
 TEST_CASE_METHOD(TApp, "vectorDefaults", "[optiontype]") {
     std::vector<int> vals{4, 5};
-    auto opt = app.add_option("--long", vals, "", true);
+    auto opt = app.add_option("--long", vals)->capture_default_str();
 
     args = {"--long", "[1,2,3]"};
 
@@ -269,6 +269,17 @@ TEST_CASE_METHOD(TApp, "vectorDefaults", "[optiontype]") {
     CHECK(std::vector<int>({5}) == res);
 }
 
+TEST_CASE_METHOD(TApp, "mapInput", "[optiontype]") {
+    std::map<int, std::string> vals{};
+    app.add_option("--long", vals);
+
+    args = {"--long", "5", "test"};
+
+    run();
+
+    CHECK(vals.at(5) == "test");
+}
+
 TEST_CASE_METHOD(TApp, "CallbackBoolFlags", "[optiontype]") {
 
     bool value{false};
@@ -320,8 +331,6 @@ TEST_CASE_METHOD(TApp, "pair_check", "[optiontype]") {
     CHECK_THROWS_AS(run(), CLI::ValidationError);
 }
 
-// this will require that modifying the multi-option policy for tuples be allowed which it isn't at present
-
 TEST_CASE_METHOD(TApp, "pair_check_take_first", "[optiontype]") {
     std::string myfile{"pair_check_file2.txt"};
     bool ok = static_cast<bool>(std::ofstream(myfile.c_str()).put('a'));  // create file
@@ -915,3 +924,76 @@ TEST_CASE_METHOD(TApp, "vectorDoubleArg", "[optiontype]") {
     CHECK(2U == cv.size());
     CHECK(2U == extras.size());
 }
+
+TEST_CASE_METHOD(TApp, "OnParseCall", "[optiontype]") {
+
+    int cnt{0};
+
+    auto *opt = app.add_option("-c",
+                               [&cnt](const CLI::results_t &) {
+                                   ++cnt;
+                                   return true;
+                               })
+                    ->expected(1, 20)
+                    ->trigger_on_parse();
+    std::vector<std::string> extras;
+    app.add_option("args", extras);
+    args = {"-c", "1", "-c", "2", "-c", "3"};
+    CHECK(opt->get_trigger_on_parse());
+    run();
+    CHECK(3 == cnt);
+}
+
+TEST_CASE_METHOD(TApp, "OnParseCallVector", "[optiontype]") {
+
+    std::vector<std::string> vec;
+
+    app.add_option("-c", vec)->trigger_on_parse();
+    args = {"-c", "1", "2", "3", "-c", "2", "-c", "3", "4", "5"};
+    run();
+    CHECK(vec.size() == 3U);
+}
+
+TEST_CASE_METHOD(TApp, "force_callback", "[optiontype]") {
+
+    int cnt{0};
+
+    auto *opt = app.add_option("-c",
+                               [&cnt](const CLI::results_t &) {
+                                   ++cnt;
+                                   return true;
+                               })
+                    ->expected(1, 20)
+                    ->force_callback()
+                    ->default_str("5");
+    std::vector<std::string> extras;
+    app.add_option("args", extras);
+    args = {};
+    CHECK(opt->get_force_callback());
+    run();
+    CHECK(1 == cnt);
+    cnt = 0;
+    args = {"-c", "10"};
+    run();
+    CHECK(1 == cnt);
+}
+
+TEST_CASE_METHOD(TApp, "force_callback2", "[optiontype]") {
+
+    int cnt{0};
+
+    app.add_option("-c", cnt)->force_callback()->default_val(5);
+    args = {};
+    run();
+    CHECK(5 == cnt);
+}
+
+TEST_CASE_METHOD(TApp, "force_callback3", "[optiontype]") {
+
+    int cnt{10};
+
+    app.add_option("-c", cnt)->force_callback();
+    args = {};
+    run();
+    CHECK(0 == cnt);
+}
diff --git a/packages/CLI11/tests/OptionalTest.cpp b/packages/CLI11/tests/OptionalTest.cpp
index 4d8da020b67b1f3531cce758d0eaa0e238bec745..5ea20be9dbd8beab8ebfed186ac88434a05da55a 100644
--- a/packages/CLI11/tests/OptionalTest.cpp
+++ b/packages/CLI11/tests/OptionalTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -258,11 +258,13 @@ TEST_CASE_METHOD(TApp, "BoostOptionalVector", "[optional]") {
            "-v,--vec", [&opt](const std::vector<int> &v) { opt = v; }, "some vector")
         ->expected(3);
     run();
-    CHECK(!opt);
+    bool checkOpt = static_cast<bool>(opt);
+    CHECK(!checkOpt);
 
     args = {"-v", "1", "4", "5"};
     run();
-    CHECK(opt);
+    checkOpt = static_cast<bool>(opt);
+    CHECK(checkOpt);
     std::vector<int> expV{1, 4, 5};
     CHECK(expV == *opt);
 }
@@ -272,14 +274,17 @@ TEST_CASE_METHOD(TApp, "BoostOptionalVectorEmpty", "[optional]") {
     app.add_option<decltype(opt), std::vector<int>>("-v,--vec", opt)->expected(0, 3)->allow_extra_args();
     // app.add_option("-v,--vec", opt)->expected(0, 3)->allow_extra_args();
     run();
-    CHECK(!opt);
+    bool checkOpt = static_cast<bool>(opt);
+    CHECK(!checkOpt);
     args = {"-v"};
     opt = std::vector<int>{4, 3};
     run();
-    CHECK(!opt);
+    checkOpt = static_cast<bool>(opt);
+    CHECK(!checkOpt);
     args = {"-v", "1", "4", "5"};
     run();
-    CHECK(opt);
+    checkOpt = static_cast<bool>(opt);
+    CHECK(checkOpt);
     std::vector<int> expV{1, 4, 5};
     CHECK(expV == *opt);
 }
@@ -289,14 +294,17 @@ TEST_CASE_METHOD(TApp, "BoostOptionalVectorEmptyDirect", "[optional]") {
     app.add_option_no_stream("-v,--vec", opt)->expected(0, 3)->allow_extra_args();
     // app.add_option("-v,--vec", opt)->expected(0, 3)->allow_extra_args();
     run();
-    CHECK(!opt);
+    bool checkOpt = static_cast<bool>(opt);
+    CHECK(!checkOpt);
     args = {"-v"};
     opt = std::vector<int>{4, 3};
     run();
-    CHECK(!opt);
+    checkOpt = static_cast<bool>(opt);
+    CHECK(!checkOpt);
     args = {"-v", "1", "4", "5"};
     run();
-    CHECK(opt);
+    checkOpt = static_cast<bool>(opt);
+    CHECK(checkOpt);
     std::vector<int> expV{1, 4, 5};
     CHECK(expV == *opt);
 }
diff --git a/packages/CLI11/tests/SetTest.cpp b/packages/CLI11/tests/SetTest.cpp
index d34a67a64f2163a7e1704fbc99a1952188611e37..5d225ff63ff1fa8692dbdd240da50fdd505d0104 100644
--- a/packages/CLI11/tests/SetTest.cpp
+++ b/packages/CLI11/tests/SetTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -373,7 +373,7 @@ TEST_CASE_METHOD(TApp, "NumericalSets", "[set]") {
 
 TEST_CASE_METHOD(TApp, "SetWithDefaults", "[set]") {
     int someint{2};
-    app.add_option("-a", someint, "", true)->check(CLI::IsMember({1, 2, 3, 4}));
+    app.add_option("-a", someint)->capture_default_str()->check(CLI::IsMember({1, 2, 3, 4}));
 
     args = {"-a1", "-a2"};
 
@@ -382,7 +382,7 @@ TEST_CASE_METHOD(TApp, "SetWithDefaults", "[set]") {
 
 TEST_CASE_METHOD(TApp, "SetWithDefaultsConversion", "[set]") {
     int someint{2};
-    app.add_option("-a", someint, "", true)->check(CLI::IsMember({1, 2, 3, 4}));
+    app.add_option("-a", someint)->capture_default_str()->check(CLI::IsMember({1, 2, 3, 4}));
 
     args = {"-a", "hi"};
 
@@ -391,7 +391,7 @@ TEST_CASE_METHOD(TApp, "SetWithDefaultsConversion", "[set]") {
 
 TEST_CASE_METHOD(TApp, "SetWithDefaultsIC", "[set]") {
     std::string someint = "ho";
-    app.add_option("-a", someint, "", true)->check(CLI::IsMember({"Hi", "Ho"}));
+    app.add_option("-a", someint)->capture_default_str()->check(CLI::IsMember({"Hi", "Ho"}));
 
     args = {"-aHi", "-aHo"};
 
@@ -415,7 +415,7 @@ TEST_CASE_METHOD(TApp, "InSet", "[set]") {
 TEST_CASE_METHOD(TApp, "InSetWithDefault", "[set]") {
 
     std::string choice = "one";
-    app.add_option("-q,--quick", choice, "", true)->check(CLI::IsMember({"one", "two", "three"}));
+    app.add_option("-q,--quick", choice)->capture_default_str()->check(CLI::IsMember({"one", "two", "three"}));
 
     run();
     CHECK(choice == "one");
@@ -432,7 +432,9 @@ TEST_CASE_METHOD(TApp, "InSetWithDefault", "[set]") {
 TEST_CASE_METHOD(TApp, "InCaselessSetWithDefault", "[set]") {
 
     std::string choice = "one";
-    app.add_option("-q,--quick", choice, "", true)->transform(CLI::IsMember({"one", "two", "three"}, CLI::ignore_case));
+    app.add_option("-q,--quick", choice)
+        ->capture_default_str()
+        ->transform(CLI::IsMember({"one", "two", "three"}, CLI::ignore_case));
 
     run();
     CHECK(choice == "one");
@@ -494,7 +496,7 @@ TEST_CASE_METHOD(TApp, "FailMutableSet", "[set]") {
     int choice{0};
     auto vals = std::shared_ptr<std::set<int>>(new std::set<int>({1, 2, 3}));
     app.add_option("-q,--quick", choice)->check(CLI::IsMember(vals));
-    app.add_option("-s,--slow", choice, "", true)->check(CLI::IsMember(vals));
+    app.add_option("-s,--slow", choice)->capture_default_str()->check(CLI::IsMember(vals));
 
     args = {"--quick=hello"};
     CHECK_THROWS_AS(run(), CLI::ValidationError);
@@ -651,7 +653,7 @@ TEST_CASE_METHOD(TApp, "AddRemoveSetItems", "[set]") {
 
     std::string type1, type2;
     app.add_option("--type1", type1)->check(CLI::IsMember(&items));
-    app.add_option("--type2", type2, "", true)->check(CLI::IsMember(&items));
+    app.add_option("--type2", type2)->capture_default_str()->check(CLI::IsMember(&items));
 
     args = {"--type1", "TYPE1", "--type2", "TYPE2"};
 
@@ -682,7 +684,7 @@ TEST_CASE_METHOD(TApp, "AddRemoveSetItemsNoCase", "[set]") {
 
     std::string type1, type2;
     app.add_option("--type1", type1)->transform(CLI::IsMember(&items, CLI::ignore_case));
-    app.add_option("--type2", type2, "", true)->transform(CLI::IsMember(&items, CLI::ignore_case));
+    app.add_option("--type2", type2)->capture_default_str()->transform(CLI::IsMember(&items, CLI::ignore_case));
 
     args = {"--type1", "TYPe1", "--type2", "TyPE2"};
 
diff --git a/packages/CLI11/tests/SimpleTest.cpp b/packages/CLI11/tests/SimpleTest.cpp
index 92262b5e64ef7f30a65fe50d7a38592d33ac1809..3051a46d4c1c7924f357688da59a9e372ac8ba70 100644
--- a/packages/CLI11/tests/SimpleTest.cpp
+++ b/packages/CLI11/tests/SimpleTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/StringParseTest.cpp b/packages/CLI11/tests/StringParseTest.cpp
index 3a9f7371d87bd0412554eb49d2fe559ea060c95c..a9842481139218f4ef47e9f6cda8dedc6ba5e50f 100644
--- a/packages/CLI11/tests/StringParseTest.cpp
+++ b/packages/CLI11/tests/StringParseTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -78,3 +78,31 @@ TEST_CASE_METHOD(TApp, "ExistingExeCheckWithLotsOfSpace", "[stringparse]") {
 
     CHECK(std::string("./") + std::string(tmpexe) == app.get_name());
 }
+
+// From GitHub issue #591 https://github.com/CLIUtils/CLI11/issues/591
+TEST_CASE_METHOD(TApp, "ProgNameWithSpace", "[stringparse]") {
+
+    app.add_flag("--foo");
+    CHECK_NOTHROW(app.parse("\"Foo Bar\" --foo", true));
+
+    CHECK(app["--foo"]->as<bool>());
+    CHECK(app.get_name() == "Foo Bar");
+}
+
+TEST_CASE_METHOD(TApp, "ProgNameWithSpaceEmbeddedQuote", "[stringparse]") {
+
+    app.add_flag("--foo");
+    CHECK_NOTHROW(app.parse("\"Foo\\\" Bar\" --foo", true));
+
+    CHECK(app["--foo"]->as<bool>());
+    CHECK(app.get_name() == "Foo\" Bar");
+}
+
+TEST_CASE_METHOD(TApp, "ProgNameWithSpaceSingleQuote", "[stringparse]") {
+
+    app.add_flag("--foo");
+    CHECK_NOTHROW(app.parse(R"('Foo\' Bar' --foo)", true));
+
+    CHECK(app["--foo"]->as<bool>());
+    CHECK(app.get_name() == "Foo' Bar");
+}
diff --git a/packages/CLI11/tests/SubcommandTest.cpp b/packages/CLI11/tests/SubcommandTest.cpp
index eef67bca7d44994678b3534dd8c3d155171a7479..e1ffded6b9371c13d68e7f9c800c87fadd89e207 100644
--- a/packages/CLI11/tests/SubcommandTest.cpp
+++ b/packages/CLI11/tests/SubcommandTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
@@ -109,7 +109,7 @@ TEST_CASE_METHOD(TApp, "CrazyNameSubcommand", "[subcom]") {
 TEST_CASE_METHOD(TApp, "RequiredAndSubcommands", "[subcom]") {
 
     std::string baz;
-    app.add_option("baz", baz, "Baz Description", true)->required();
+    app.add_option("baz", baz, "Baz Description")->required()->capture_default_str();
     auto foo = app.add_subcommand("foo");
     auto bar = app.add_subcommand("bar");
 
@@ -811,6 +811,18 @@ TEST_CASE_METHOD(TApp, "RequiredPosInSubcommand", "[subcom]") {
     CHECK_THROWS_AS(run(), CLI::RequiredError);
 }
 
+TEST_CASE_METHOD(TApp, "invalidSubcommandName", "[subcom]") {
+
+    bool gotError{false};
+    try {
+        app.add_subcommand("!foo/foo", "Foo a bar");
+    } catch(const CLI::IncorrectConstruction &e) {
+        gotError = true;
+        CHECK_THAT(e.what(), Contains("!"));
+    }
+    CHECK(gotError);
+}
+
 struct SubcommandProgram : public TApp {
 
     CLI::App *start{nullptr};
@@ -1633,6 +1645,28 @@ TEST_CASE_METHOD(TApp, "OptionGroupAlias", "[subcom]") {
     CHECK(-3 == val);
 }
 
+TEST_CASE_METHOD(TApp, "OptionGroupAliasWithSpaces", "[subcom]") {
+    double val{0.0};
+    auto sub = app.add_option_group("sub1");
+    sub->alias("sub2 bb");
+    sub->alias("sub3/b");
+    sub->add_option("-v,--value", val);
+    args = {"sub1", "-v", "-3"};
+    CHECK_THROWS_AS(run(), CLI::ExtrasError);
+
+    args = {"sub2 bb", "--value", "-5"};
+    run();
+    CHECK(-5.0 == val);
+
+    args = {"sub3/b", "-v", "7"};
+    run();
+    CHECK(7 == val);
+
+    args = {"-v", "-3"};
+    run();
+    CHECK(-3 == val);
+}
+
 TEST_CASE_METHOD(TApp, "subcommand_help", "[subcom]") {
     auto sub1 = app.add_subcommand("help")->silent();
     bool flag{false};
@@ -1654,9 +1688,8 @@ TEST_CASE_METHOD(TApp, "AliasErrors", "[subcom]") {
     auto sub1 = app.add_subcommand("sub1");
     auto sub2 = app.add_subcommand("sub2");
 
-    CHECK_THROWS_AS(sub2->alias("this is a not a valid alias"), CLI::IncorrectConstruction);
-    CHECK_THROWS_AS(sub2->alias("-alias"), CLI::IncorrectConstruction);
-    CHECK_THROWS_AS(sub2->alias("alia$"), CLI::IncorrectConstruction);
+    CHECK_THROWS_AS(sub2->alias("this is a not\n a valid alias"), CLI::IncorrectConstruction);
+    CHECK_NOTHROW(sub2->alias("-alias"));  // this is allowed but would be unusable on command line parsers
 
     CHECK_THROWS_AS(app.add_subcommand("--bad_subcommand_name", "documenting the bad subcommand"),
                     CLI::IncorrectConstruction);
@@ -1862,3 +1895,63 @@ TEST_CASE_METHOD(ManySubcommands, "defaultEnabledSubcommand", "[subcom]") {
     CHECK(sub2->get_enabled_by_default());
     CHECK(!sub2->get_disabled());
 }
+
+// #572
+TEST_CASE_METHOD(TApp, "MultiFinalCallbackCounts", "[subcom]") {
+
+    int app_compl = 0;
+    int sub_compl = 0;
+    int subsub_compl = 0;
+    int app_final = 0;
+    int sub_final = 0;
+    int subsub_final = 0;
+
+    app.parse_complete_callback([&app_compl]() { app_compl++; });
+    app.final_callback([&app_final]() { app_final++; });
+
+    auto *sub = app.add_subcommand("sub");
+
+    sub->parse_complete_callback([&sub_compl]() { sub_compl++; });
+    sub->final_callback([&sub_final]() { sub_final++; });
+
+    auto *subsub = sub->add_subcommand("subsub");
+
+    subsub->parse_complete_callback([&subsub_compl]() { subsub_compl++; });
+    subsub->final_callback([&subsub_final]() { subsub_final++; });
+
+    SECTION("No specified subcommands") {
+        args = {};
+        run();
+
+        CHECK(app_compl == 1);
+        CHECK(app_final == 1);
+        CHECK(sub_compl == 0);
+        CHECK(sub_final == 0);
+        CHECK(subsub_compl == 0);
+        CHECK(subsub_final == 0);
+    }
+
+    SECTION("One layer of subcommands") {
+        args = {"sub"};
+        run();
+
+        CHECK(app_compl == 1);
+        CHECK(app_final == 1);
+        CHECK(sub_compl == 1);
+        CHECK(sub_final == 1);
+        CHECK(subsub_compl == 0);
+        CHECK(subsub_final == 0);
+    }
+
+    SECTION("Fully specified subcommands") {
+        args = {"sub", "subsub"};
+        run();
+
+        CHECK(app_compl == 1);
+        CHECK(app_final == 1);
+        CHECK(sub_compl == 1);
+        CHECK(sub_final == 1);
+        CHECK(subsub_compl == 1);
+        CHECK(subsub_final == 1);
+    }
+}
diff --git a/packages/CLI11/tests/TimerTest.cpp b/packages/CLI11/tests/TimerTest.cpp
index 8c88f478ace08a3e192c0439717ec44ad32b9ab9..11e6e12177815c0d18e228aedd3736927b47d79e 100644
--- a/packages/CLI11/tests/TimerTest.cpp
+++ b/packages/CLI11/tests/TimerTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/TransformTest.cpp b/packages/CLI11/tests/TransformTest.cpp
index 84ac544a4d003778cbcd73797034eb70c5612d98..76a1eff83012ae33b2b3c20ea37102796acf9b55 100644
--- a/packages/CLI11/tests/TransformTest.cpp
+++ b/packages/CLI11/tests/TransformTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/TrueFalseTest.cpp b/packages/CLI11/tests/TrueFalseTest.cpp
index aa8886c092ab21c1b464ec833c3fd00924378924..c3b556c3fdd89c252474e03fd8f4f0f6a120ebc3 100644
--- a/packages/CLI11/tests/TrueFalseTest.cpp
+++ b/packages/CLI11/tests/TrueFalseTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/WindowsTest.cpp b/packages/CLI11/tests/WindowsTest.cpp
index 847266649965e6fc03176cda5862d46d07dbe103..5a75908c20f95f9d4089cbcae72b68caa6338416 100644
--- a/packages/CLI11/tests/WindowsTest.cpp
+++ b/packages/CLI11/tests/WindowsTest.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/app_helper.hpp b/packages/CLI11/tests/app_helper.hpp
index 0f72adda27064e17d3cb97f242526e5b1b2b4501..91991682544dc465c44c989fe1a4e0e4dd2c56e9 100644
--- a/packages/CLI11/tests/app_helper.hpp
+++ b/packages/CLI11/tests/app_helper.hpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/catch.hpp b/packages/CLI11/tests/catch.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2aaeae76e3c58c517e0212ef0a31d0fe79db7dea
--- /dev/null
+++ b/packages/CLI11/tests/catch.hpp
@@ -0,0 +1,9 @@
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
+// under NSF AWARD 1414736 and by the respective contributors.
+// All rights reserved.
+//
+// SPDX-License-Identifier: BSD-3-Clause
+
+#pragma once
+
+#include <catch2/catch.hpp>
diff --git a/packages/CLI11/tests/informational.cpp b/packages/CLI11/tests/informational.cpp
index 92f7dc4c8d597e729ae62efadf0eb226aa66c31f..f0fc70ba1778262d1b6888075382181e919c246f 100644
--- a/packages/CLI11/tests/informational.cpp
+++ b/packages/CLI11/tests/informational.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/link_test_1.cpp b/packages/CLI11/tests/link_test_1.cpp
index be91037607cc1d090ca572a93803c527a8242975..447afbf4e94a62cb3980b6e5bf5f178d6852f6a6 100644
--- a/packages/CLI11/tests/link_test_1.cpp
+++ b/packages/CLI11/tests/link_test_1.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/link_test_2.cpp b/packages/CLI11/tests/link_test_2.cpp
index b8544ab47a8104b831b66cd409490c200261bcb7..abce5493b75cf4619f08d41daa83c2e997b5647d 100644
--- a/packages/CLI11/tests/link_test_2.cpp
+++ b/packages/CLI11/tests/link_test_2.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/main.cpp b/packages/CLI11/tests/main.cpp
index f1c2725f53e260069f3cc867bb1398ac21cb4b2f..f5fbebd81dad107729cb1e239e846f3a05d610f9 100644
--- a/packages/CLI11/tests/main.cpp
+++ b/packages/CLI11/tests/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/CLI11/tests/mesonTest/main.cpp b/packages/CLI11/tests/mesonTest/main.cpp
index 69813782ad87dd6c6d4b4725cc6700ad79a4da14..64d45eb6f1e7e909c64a5540f7bcd60ebe878327 100644
--- a/packages/CLI11/tests/mesonTest/main.cpp
+++ b/packages/CLI11/tests/mesonTest/main.cpp
@@ -1,4 +1,4 @@
-// Copyright (c) 2017-2020, University of Cincinnati, developed by Henry Schreiner
+// Copyright (c) 2017-2021, University of Cincinnati, developed by Henry Schreiner
 // under NSF AWARD 1414736 and by the respective contributors.
 // All rights reserved.
 //
diff --git a/packages/Catch2/.github/workflows/linux-other-builds.yml b/packages/Catch2/.github/workflows/linux-other-builds.yml
new file mode 100644
index 0000000000000000000000000000000000000000..61ebf3d7e2dc542b0e96ecacce6789c3d4a1f6a4
--- /dev/null
+++ b/packages/Catch2/.github/workflows/linux-other-builds.yml
@@ -0,0 +1,86 @@
+# The builds in this file are more complex (e.g. they need custom CMake
+# configuration) and thus are unsuitable to the simple build matrix
+# approach used in simple-builds
+name: Linux builds (complex)
+
+on: [push, pull_request]
+
+jobs:
+  build:
+    name: ${{matrix.build_description}}, ${{matrix.cxx}}, C++${{matrix.std}} ${{matrix.build_type}}
+    runs-on: ubuntu-20.04
+    strategy:
+      matrix:
+        # We add builds one by one in this case, because there are no
+        # dimensions that are shared across the builds
+        include:
+
+          # Single surrogate header build
+          - cxx: clang++-10
+            build_description: Surrogates build
+            build_type: Debug
+            std: 14
+            other_pkgs: clang-10
+            cmake_configurations: -DCATCH_BUILD_SURROGATES=ON
+
+          # Extras and examples with gcc-7
+          - cxx: g++-7
+            build_description: Extras + Examples
+            build_type: Debug
+            std: 14
+            other_pkgs: g++-7
+            cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
+          - cxx: g++-7
+            build_description: Extras + Examples
+            build_type: Release
+            std: 14
+            other_pkgs: g++-7
+            cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
+
+          # Extras and examples with Clang-10
+          - cxx: clang++-10
+            build_description: Extras + Examples
+            build_type: Debug
+            std: 17
+            other_pkgs: clang-10
+            cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
+          - cxx: clang++-10
+            build_description: Extras + Examples
+            build_type: Release
+            std: 17
+            other_pkgs: clang-10
+            cmake_configurations: -DCATCH_BUILD_EXTRA_TESTS=ON -DCATCH_BUILD_EXAMPLES=ON
+
+
+    steps:
+    - uses: actions/checkout@v2
+
+    - name: Prepare environment
+      run: sudo apt-get install -y ninja-build ${{matrix.other_pkgs}}
+
+    - name: Configure build
+      working-directory: ${{runner.workspace}}
+      env:
+        CXX: ${{matrix.cxx}}
+        CXXFLAGS: ${{matrix.cxxflags}}
+      # Note: $GITHUB_WORKSPACE is distinct from ${{runner.workspace}}.
+      #       This is important
+      run: |
+        cmake -Bbuild -H$GITHUB_WORKSPACE \
+              -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
+              -DCMAKE_CXX_STANDARD=${{matrix.std}} \
+              -DCMAKE_CXX_EXTENSIONS=OFF \
+              -DCATCH_DEVELOPMENT_BUILD=ON \
+              ${{matrix.cmake_configurations}} \
+              -G Ninja
+
+    - name: Build tests + lib
+      working-directory: ${{runner.workspace}}/build
+      run: ninja
+
+    - name: Run tests
+      env:
+          CTEST_OUTPUT_ON_FAILURE: 1
+      working-directory: ${{runner.workspace}}/build
+      # Hardcode 2 cores we know are there
+      run: ctest -C ${{matrix.build_type}} -j 2
diff --git a/packages/Catch2/.github/workflows/linux-builds.yml b/packages/Catch2/.github/workflows/linux-simple-builds.yml
similarity index 52%
rename from packages/Catch2/.github/workflows/linux-builds.yml
rename to packages/Catch2/.github/workflows/linux-simple-builds.yml
index 93ae21e6f7ca436246f227b17094d6148d058e99..d7d4bb07f37d249ed49c84f808e70147f1b6fb1f 100644
--- a/packages/Catch2/.github/workflows/linux-builds.yml
+++ b/packages/Catch2/.github/workflows/linux-simple-builds.yml
@@ -1,9 +1,10 @@
-name: Linux builds
+name: Linux builds (basic)
 
 on: [push, pull_request]
 
 jobs:
   build:
+    name: ${{matrix.cxx}}, C++${{matrix.std}}, ${{matrix.build_type}}
     runs-on: ubuntu-20.04
     strategy:
       matrix:
@@ -24,12 +25,44 @@ jobs:
         # cannot be installed on ubuntu-20.04 be default?
 #          - cxx: g++-6
 #            other_pkgs: g++-6
+          - cxx: g++-7
+            other_pkgs: g++-7
+          - cxx: g++-8
+            other_pkgs: g++-8
+          - cxx: g++-9
+            other_pkgs: g++-9
+          - cxx: g++-10
+            other_pkgs: g++-10
           - cxx: clang++-6.0
             other_pkgs: clang-6.0
           - cxx: clang++-7
             other_pkgs: clang-7
+          - cxx: clang++-8
+            other_pkgs: clang-8
+          - cxx: clang++-9
+            other_pkgs: clang-9
           - cxx: clang++-10
             other_pkgs: clang-10
+          # Clang 6 + C++17
+          # does not work with the default libstdc++ version thanks
+          # to a disagreement on variant implementation.
+          # - cxx: clang++-6.0
+          #   build_type: Debug
+          #   std: 17
+          #   other_pkgs: clang-6.0
+          # - cxx: clang++-6.0
+          #   build_type: Release
+          #   std: 17
+          #   other_pkgs: clang-6.0
+          # Clang 10 + C++17
+          - cxx: clang++-10
+            build_type: Debug
+            std: 17
+            other_pkgs: clang-10
+          - cxx: clang++-10
+            build_type: Release
+            std: 17
+            other_pkgs: clang-10
 
     steps:
     - uses: actions/checkout@v2
@@ -45,8 +78,11 @@ jobs:
       # Note: $GITHUB_WORKSPACE is distinct from ${{runner.workspace}}.
       #       This is important
       run: |
-        cmake -Bbuild -H$GITHUB_WORKSPACE -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
-              -DCMAKE_CXX_STANDARD=${{matrix.std}} -DCATCH_DEVELOPMENT_BUILD=ON \
+        cmake -Bbuild -H$GITHUB_WORKSPACE \
+              -DCMAKE_BUILD_TYPE=${{matrix.build_type}} \
+              -DCMAKE_CXX_STANDARD=${{matrix.std}} \
+              -DCMAKE_CXX_EXTENSIONS=OFF \
+              -DCATCH_DEVELOPMENT_BUILD=ON \
               -G Ninja
 
     - name: Build tests + lib
diff --git a/packages/Catch2/.gitrepo b/packages/Catch2/.gitrepo
index ebe025859982135cd6d8d666ef0df64a3d4977fc..3bb3f7649f68e0d2962f3f6b488cdd1cdb401f31 100644
--- a/packages/Catch2/.gitrepo
+++ b/packages/Catch2/.gitrepo
@@ -6,7 +6,7 @@
 [subrepo]
 	remote = git@github.com:catchorg/Catch2.git
 	branch = devel
-	commit = 2dbe63a6ba949c3f0a2078770f7d12cfe5adc10c
-	parent = 2179906885aa483f8d2b2cfa127d187dff80aea2
+	commit = fb4153e05e430921574d79d868ebc44dd9528629
+	parent = 57867f14916a3779d2de6004b203bd636dcfee88
 	method = merge
 	cmdver = 0.4.3
diff --git a/packages/Catch2/.travis.yml b/packages/Catch2/.travis.yml
deleted file mode 100644
index 811b12bf7fedbf4bc297907872e808b001ab8398..0000000000000000000000000000000000000000
--- a/packages/Catch2/.travis.yml
+++ /dev/null
@@ -1,161 +0,0 @@
-language: cpp
-dist: xenial
-
-
-branches:
-  except:
-  - /dev-appveyor.*/
-
-common_sources: &all_sources
-  - ubuntu-toolchain-r-test
-  - llvm-toolchain-xenial
-  - llvm-toolchain-xenial-3.8
-  - llvm-toolchain-xenial-3.9
-  - llvm-toolchain-xenial-4.0
-  - llvm-toolchain-xenial-5.0
-  - llvm-toolchain-xenial-6.0
-  - llvm-toolchain-xenial-7
-  - llvm-toolchain-xenial-8
-
-
-matrix:
-  include:
-    # Clang builds
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-3.8']
-      env: COMPILER='clang++-3.8' CPP14=1
-
-    - os: linux
-      compiler: clang
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['clang-3.8', 'lcov']
-      env: COMPILER='clang++-3.8' CPP14=1 EXAMPLES=1 COVERAGE=1 EXTRAS=1
-
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-3.9']
-      env: COMPILER='clang++-3.9' CPP14=1
-
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-4.0']
-      env: COMPILER='clang++-4.0' CPP14=1
-
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-5.0']
-      env: COMPILER='clang++-5.0' CPP14=1
-
-
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-6.0', 'libstdc++-8-dev']
-      env: COMPILER='clang++-6.0' CPP17=1
-
-
-    - os: linux
-      compiler: clang
-      addons:
-          apt:
-              sources: *all_sources
-              packages: ['clang-8', 'libstdc++-8-dev']
-      env: COMPILER='clang++-8' CPP17=1 EXAMPLES=1 COVERAGE=1 EXTRAS=1
-
-
-    # GCC builds
-    - os: linux
-      compiler: gcc
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['g++-5']
-      env: COMPILER='g++-5' CPP14=1
-
-    - os: linux
-      compiler: gcc
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['g++-6']
-      env: COMPILER='g++-6' CPP14=1
-
-    - os: linux
-      compiler: gcc
-      addons:
-        apt:
-          sources: *all_sources
-          packages: ['g++-7', 'lcov']
-      env: COMPILER='g++-7' CPP14=1 EXAMPLES=1 COVERAGE=1 EXTRAS=1
-
-
-    # Special builds, e.g. conan
-    - language: python
-      python:
-        - "3.7"
-      install:
-        - pip install conan-package-tools
-      env:
-        - CONAN_GCC_VERSIONS=8
-        - CONAN_DOCKER_IMAGE=conanio/gcc8
-        - CPP14=1
-      script:
-        - python .conan/build.py
-
-before_script:
-  - export CXX=${COMPILER}
-  - cd ${TRAVIS_BUILD_DIR}
-  # We want to regenerate the amalgamated header if the extra tests
-  # are enabled.
-  - |
-    if [[ ${EXTRAS} -eq 1 ]]; then
-      python3 ./tools/scripts/generateAmalgamatedFiles.py
-    fi
-
-  - |
-    if [[ ${CPP17} -eq 1 ]]; then
-      export CPP_STANDARD=17
-    elif [[ ${CPP14} -eq 1 ]]; then
-      export CPP_STANDARD=14
-    else
-      travis_terminate 4;
-    fi
-
-    # Use Debug builds for running Valgrind and building examples
-  - cmake -H. -BBuild-Debug -DCMAKE_BUILD_TYPE=Debug -Wdev -DCATCH_USE_VALGRIND=${VALGRIND} -DCATCH_BUILD_EXAMPLES=${EXAMPLES} -DCATCH_ENABLE_COVERAGE=${COVERAGE} -DCATCH_BUILD_EXTRA_TESTS=${EXTRAS} -DCMAKE_CXX_STANDARD=${CPP_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=On -DCMAKE_CXX_EXTENSIONS=OFF -DCATCH_DEVELOPMENT_BUILD=ON
-    # Don't bother with release build for coverage build
-  - cmake -H. -BBuild-Release -DCMAKE_BUILD_TYPE=Release -Wdev -DCMAKE_CXX_STANDARD=${CPP_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED=On -DCMAKE_CXX_EXTENSIONS=OFF -DCATCH_DEVELOPMENT_BUILD=ON
-
-
-script:
-  - cd Build-Debug
-  - make -j 2
-  - CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2
-    # Coverage collection does not work for OS X atm
-  - |
-    if [[ "${TRAVIS_OS_NAME}" == "linux" ]] && [[ "${COVERAGE}" == "1" ]]; then
-      make gcov
-      make lcov
-      bash <(curl -s https://codecov.io/bash) -X gcov || echo "Codecov did not collect coverage reports"
-    fi
-  - # Go to release build
-  - cd ../Build-Release
-  - make -j 2
-  - CTEST_OUTPUT_ON_FAILURE=1 ctest -j 2
diff --git a/packages/Catch2/CMake/MiscFunctions.cmake b/packages/Catch2/CMake/MiscFunctions.cmake
index 1f0ed865040cea32082f2b54ef02096a76eb760a..7548f287f9f14fd5a761da97693d34394d317d25 100644
--- a/packages/Catch2/CMake/MiscFunctions.cmake
+++ b/packages/Catch2/CMake/MiscFunctions.cmake
@@ -45,7 +45,7 @@ function(add_warnings_to_targets targets)
           "-Wglobal-constructors"
           "-Wmissing-noreturn"
           "-Wparentheses"
-          "-Wextra-semi-stmt"
+          "-Wextra-semi"
           "-Wunreachable-code"
           "-Wstrict-aliasing"
           "-Wreturn-std-move"
@@ -77,3 +77,13 @@ function(add_warnings_to_targets targets)
         endif()
     endif()
 endfunction()
+
+# Adds flags required for reproducible build to the target
+# Currently only supports GCC and Clang
+function(add_build_reproducibility_settings target)
+# Make the build reproducible on versions of g++ and clang that supports -ffile-prefix-map
+  if(("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" AND NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 8) OR
+     ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND NOT ${CMAKE_CXX_COMPILER_VERSION} VERSION_LESS 10))
+    target_compile_options(${target} PRIVATE "-ffile-prefix-map=${CATCH_DIR}=.")
+  endif()
+endfunction()
diff --git a/packages/Catch2/CMake/catch2.pc.in b/packages/Catch2/CMake/catch2.pc.in
index c37eb8ad345af246b83e40dea53e44a3c946bec2..bd1c95a1afd3238a5a856a30b50098f98c6af3e4 100644
--- a/packages/Catch2/CMake/catch2.pc.in
+++ b/packages/Catch2/CMake/catch2.pc.in
@@ -1,3 +1,5 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=${prefix}
 includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
 libdir=@CMAKE_INSTALL_FULL_LIBDIR@
 
diff --git a/packages/Catch2/CMakeLists.txt b/packages/Catch2/CMakeLists.txt
index 90d5806baeabe49d074d742d75aeb8ee1e15ddae..f1be3f59435e965a6f2007f8a9cb35dad3639f85 100644
--- a/packages/Catch2/CMakeLists.txt
+++ b/packages/Catch2/CMakeLists.txt
@@ -4,6 +4,8 @@ cmake_minimum_required(VERSION 3.5)
 # disable testsuite in that case
 if(NOT DEFINED PROJECT_NAME)
   set(NOT_SUBPROJECT ON)
+else()
+  set(NOT_SUBPROJECT OFF)
 endif()
 
 option(CATCH_INSTALL_DOCS "Install documentation alongside library" ON)
@@ -17,6 +19,8 @@ cmake_dependent_option(CATCH_BUILD_EXTRA_TESTS "Build extra tests" OFF "CATCH_DE
 cmake_dependent_option(CATCH_BUILD_FUZZERS "Build fuzzers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
 cmake_dependent_option(CATCH_ENABLE_COVERAGE "Generate coverage for codecov.io" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
 cmake_dependent_option(CATCH_ENABLE_WERROR "Enables Werror during build" ON "CATCH_DEVELOPMENT_BUILD" OFF)
+cmake_dependent_option(CATCH_BUILD_SURROGATES "Enable generating and building surrogate TUs for the main headers" OFF "CATCH_DEVELOPMENT_BUILD" OFF)
+
 
 # Catch2's build breaks if done in-tree. You probably should not build
 # things in tree anyway, but we can allow projects that include Catch2
@@ -37,7 +41,9 @@ project(Catch2 LANGUAGES CXX VERSION 3.0.0)
 list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/CMake")
 include(GNUInstallDirs)
 include(CMakePackageConfigHelpers)
-include(CTest)
+if(CATCH_DEVELOPMENT_BUILD)
+  include(CTest)
+endif()
 
 # This variable is used in some subdirectories, so we need it here, rather
 # than later in the install block
diff --git a/packages/Catch2/README.md b/packages/Catch2/README.md
index d8e08bb717cc4f80dbc37eabca3be574e5ee533b..31be055dbc6da5655c73eda39654f7918f1a9ccb 100644
--- a/packages/Catch2/README.md
+++ b/packages/Catch2/README.md
@@ -2,7 +2,9 @@
 ![Catch2 logo](data/artwork/catch2-logo-small.png)
 
 [![Github Releases](https://img.shields.io/github/release/catchorg/catch2.svg)](https://github.com/catchorg/catch2/releases)
-[![Build Status](https://travis-ci.org/catchorg/Catch2.svg?branch=devel)](https://travis-ci.org/catchorg/Catch2)
+[![Linux build status](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml/badge.svg)](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml)
+[![Linux build status](https://github.com/catchorg/Catch2/actions/workflows/linux-other-builds.yml/badge.svg)](https://github.com/catchorg/Catch2/actions/workflows/linux-other-builds.yml)
+[![MacOS build status](https://github.com/catchorg/Catch2/actions/workflows/mac-builds.yml/badge.svg)](https://github.com/catchorg/Catch2/actions/workflows/mac-builds.yml)
 [![Build Status](https://ci.appveyor.com/api/projects/status/github/catchorg/Catch2?svg=true&branch=devel)](https://ci.appveyor.com/project/catchorg/catch2)
 [![Code Coverage](https://codecov.io/gh/catchorg/Catch2/branch/devel/graph/badge.svg)](https://codecov.io/gh/catchorg/Catch2)
 [![Try online](https://img.shields.io/badge/try-online-blue.svg)](https://godbolt.org/z/9x9qoM)
diff --git a/packages/Catch2/appveyor.yml b/packages/Catch2/appveyor.yml
index 86a1df1885e213cd678348b0c65e0d95ee616910..d84d775c934258326aa2c2d74fcb569cd8b1d07d 100644
--- a/packages/Catch2/appveyor.yml
+++ b/packages/Catch2/appveyor.yml
@@ -51,8 +51,9 @@ test_script:
 # build explicitly.
 environment:
   matrix:
-    - FLAVOR: VS 2019 x64 Debug
+    - FLAVOR: VS 2019 x64 Debug Surrogates
       APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
+      surrogates: 1
       platform: x64
       configuration: Debug
     
diff --git a/packages/Catch2/docs/Readme.md b/packages/Catch2/docs/Readme.md
index 821e6fca0d7625a4637c60f47a8b586ef2fc7497..262edb5cf48eae5e06c9e69d35932ec8a404c2cc 100644
--- a/packages/Catch2/docs/Readme.md
+++ b/packages/Catch2/docs/Readme.md
@@ -29,14 +29,13 @@ Odds and ends:
 * [CI and other miscellaneous pieces](ci-and-misc.md#top)
 
 FAQ:
-* [Why are my tests slow to compile?](slow-compiles.md#top)
 * [Known limitations](limitations.md#top)
  
 Other:
-* [Why Catch?](why-catch.md#top)
+* [Why Catch2?](why-catch.md#top)
 * [Migrating from v2 to v3](migrate-v2-to-v3.md#top)
-* [Open Source Projects using Catch](opensource-users.md#top)
-* [Commercial Projects using Catch](commercial-users.md#top)
+* [Open Source Projects using Catch2](opensource-users.md#top)
+* [Commercial Projects using Catch2](commercial-users.md#top)
 * [Contributing](contributing.md#top)
 * [Release Notes](release-notes.md#top)
 * [Deprecations and incoming changes](deprecations.md#top)
diff --git a/packages/Catch2/docs/benchmarks.md b/packages/Catch2/docs/benchmarks.md
index ccc4175b6c346e2136fa379eae649c876c3da2d9..101539ddc5c82fcbec30ebe1ab7f91b3b6913f91 100644
--- a/packages/Catch2/docs/benchmarks.md
+++ b/packages/Catch2/docs/benchmarks.md
@@ -1,7 +1,7 @@
 <a id="top"></a>
 # Authoring benchmarks
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 Writing benchmarks is not easy. Catch simplifies certain aspects but you'll
 always need to take care about various aspects. Understanding a few things about
diff --git a/packages/Catch2/docs/ci-and-misc.md b/packages/Catch2/docs/ci-and-misc.md
index 40b7cec9270010cd631bc19b130a852544222a9d..0d016618cf8bf978c7e85c07c2f0d727833517f1 100644
--- a/packages/Catch2/docs/ci-and-misc.md
+++ b/packages/Catch2/docs/ci-and-misc.md
@@ -95,7 +95,7 @@ can use `pkg-config` to get its include path: `pkg-config --cflags catch2`.
 
 ### gdb and lldb scripts
 
-Catch2's `contrib` folder also contains two simple debugger scripts,
+Catch2's `extras` folder also contains two simple debugger scripts,
 `gdbinit` for `gdb` and `lldbinit` for `lldb`. If loaded into their
 respective debugger, these will tell it to step over Catch2's internals
 when stepping through code.
diff --git a/packages/Catch2/docs/cmake-integration.md b/packages/Catch2/docs/cmake-integration.md
index 59e06842c93a10887e6a629c18b79386952a138d..d8d08495e1bf27c69b2cc09a1e46fd69540f6c72 100644
--- a/packages/Catch2/docs/cmake-integration.md
+++ b/packages/Catch2/docs/cmake-integration.md
@@ -2,7 +2,7 @@
 # CMake integration
 
 **Contents**<br>
-[CMake target](#cmake-target)<br>
+[CMake targets](#cmake-targets)<br>
 [Automatic test registration](#automatic-test-registration)<br>
 [CMake project options](#cmake-project-options)<br>
 [Installing Catch2 from git repository](#installing-catch2-from-git-repository)<br>
@@ -15,28 +15,33 @@ integration points for our users.
 2) Catch2's repository contains CMake scripts for automatic registration
 of `TEST_CASE`s in CTest
 
-## CMake target
+## CMake targets
 
-Catch2's CMake build exports an interface target `Catch2::Catch2`. Linking
-against it will add the proper include path and all necessary capabilities
-to the resulting binary.
+Catch2's CMake build exports two targets, `Catch2::Catch2`, and
+`Catch2::Catch2WithMain`. If you do not need custom `main` function,
+you should be using the latter (and only the latter). Linking against
+it will add the proper include paths and link your target together with
+2 static libraries that implement Catch2 and its main respectively.
+If you need custom `main`, you should link only against `Catch2::Catch2`.
 
-This means that if Catch2 has been installed on the system, it should be
-enough to do:
+This means that if Catch2 has been installed on the system, it should
+be enough to do
 ```cmake
-find_package(Catch2 REQUIRED)
+find_package(Catch2 3 REQUIRED)
+# These tests can use the Catch2-provided main
 add_executable(tests test.cpp)
-target_link_libraries(tests PRIVATE Catch2::Catch2)
+target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)
+
+# These tests need their own main
+add_executable(custom-main-tests test.cpp test-main.cpp)
+target_link_libraries(custom-main-tests PRIVATE Catch2::Catch2)
 ```
 
+These targets are also provided when Catch2 is used as a subdirectory.
+Assuming Catch2 has been cloned to `lib/Catch2`, you only need to replace
+the `find_package` call with `add_subdirectory(lib/Catch2)` and the snippet
+above still works.
 
-This target is also provided when Catch2 is used as a subdirectory.
-Assuming that Catch2 has been cloned to `lib/Catch2`:
-```cmake
-add_subdirectory(lib/Catch2)
-add_executable(tests test.cpp)
-target_link_libraries(tests PRIVATE Catch2::Catch2)
-```
 
 Another possibility is to use [FetchContent](https://cmake.org/cmake/help/latest/module/FetchContent.html):
 ```cmake
@@ -45,14 +50,16 @@ Include(FetchContent)
 FetchContent_Declare(
   Catch2
   GIT_REPOSITORY https://github.com/catchorg/Catch2.git
-  GIT_TAG        v2.13.1)
+  GIT_TAG        v3.0.0-preview3
+)
 
 FetchContent_MakeAvailable(Catch2)
 
 add_executable(tests test.cpp)
-target_link_libraries(tests PRIVATE Catch2::Catch2)
+target_link_libraries(tests PRIVATE Catch2::Catch2WithMain)
 ```
 
+
 ## Automatic test registration
 
 Catch2's repository also contains two CMake scripts that help users
@@ -60,7 +67,7 @@ with automatically registering their `TEST_CASE`s with CTest. They
 can be found in the `extras` folder, and are
 
 1) `Catch.cmake` (and its dependency `CatchAddTests.cmake`)
-2) `ParseAndAddCatchTests.cmake`
+2) `ParseAndAddCatchTests.cmake` (deprecated)
 
 If Catch2 has been installed in system, both of these can be used after
 doing `find_package(Catch2 REQUIRED)`. Otherwise you need to add them
@@ -88,6 +95,18 @@ include(Catch)
 catch_discover_tests(foo)
 ```
 
+When using `FetchContent`, `include(Catch)` will fail unless
+`CMAKE_MODULE_PATH` is explicitly updated to include the extras
+directory.
+
+```cmake
+# ... FetchContent ...
+#
+list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
+include(CTest)
+include(Catch)
+catch_discover_tests()
+```
 
 #### Customization
 `catch_discover_tests` can be given several extra argumets:
@@ -179,10 +198,17 @@ the output file name e.g. ".xml".
 
 ### `ParseAndAddCatchTests.cmake`
 
+⚠ This script is [deprecated](https://github.com/catchorg/Catch2/pull/2120)
+in Catch2 2.13.4 and superseded by the above approach using `catch_discover_tests`.
+See [#2092](https://github.com/catchorg/Catch2/issues/2092) for details.
+
 `ParseAndAddCatchTests` works by parsing all implementation files
 associated with the provided target, and registering them via CTest's
 `add_test`. This approach has some limitations, such as the fact that
-commented-out tests will be registered anyway.
+commented-out tests will be registered anyway. More serious, only a
+subset of the assertion macros currently available in Catch can be
+detected by this script and tests with any macros that cannot be
+parsed are *silently ignored*.
 
 
 #### Usage
@@ -233,7 +259,21 @@ ParseAndAddCatchTests(bar)
 ## CMake project options
 
 Catch2's CMake project also provides some options for other projects
-that consume it. These are
+that consume it. These are:
+
+* `BUILD_TESTING` -- When `ON` and the project is not used as a subproject,
+Catch2's test binary will be built. Defaults to `ON`.
+* `CATCH_INSTALL_DOCS` -- When `ON`, Catch2's documentation will be
+included in the installation. Defaults to `ON`.
+* `CATCH_INSTALL_HELPERS` -- When `ON`, Catch2's extras folder will be
+included in the installation. Defaults to `ON`.
+* `CATCH_DEVELOPMENT_BUILD` -- When `ON`, configures the build for development
+of Catch2. This means enabling test projects, warnings and so on.
+Defaults to `OFF`.
+
+
+Enabling `CATCH_DEVELOPMENT_BUILD` also enables further configuration
+customization options:
 
 * `CATCH_BUILD_TESTING` -- When `ON`, Catch2's SelfTest project will be
 built. Defaults to `ON`. Note that Catch2 also obeys `BUILD_TESTING` CMake
@@ -241,12 +281,15 @@ variable, so _both_ of them need to be `ON` for the SelfTest to be built,
 and either of them can be set to `OFF` to disable building SelfTest.
 * `CATCH_BUILD_EXAMPLES` -- When `ON`, Catch2's usage examples will be
 built. Defaults to `OFF`.
-* `CATCH_INSTALL_DOCS` -- When `ON`, Catch2's documentation will be
-included in the installation. Defaults to `ON`.
-* `CATCH_INSTALL_HELPERS` -- When `ON`, Catch2's contrib folder will be
-included in the installation. Defaults to `ON`.
-* `BUILD_TESTING` -- When `ON` and the project is not used as a subproject,
-Catch2's test binary will be built. Defaults to `ON`.
+* `CATCH_BUILD_EXTRA_TESTS` -- When `ON`, Catch2's extra tests will be
+built. Defaults to `OFF`.
+* `CATCH_BUILD_FUZZERS` -- When `ON`, Catch2 fuzzing entry points will
+be built. Defaults to `OFF`.
+* `CATCH_ENABLE_WERROR` -- When `ON`, adds `-Werror` or equivalent flag
+to the compilation. Defaults to `ON`.
+* `CATCH_BUILD_SURROGATES` -- When `ON`, each header in Catch2 will be
+compiled separately to ensure that they are self-sufficient.
+Defaults to `OFF`.
 
 
 ## Installing Catch2 from git repository
diff --git a/packages/Catch2/docs/command-line.md b/packages/Catch2/docs/command-line.md
index a14acf7f2f35866be154790b904b9678dc34dd1c..14511db98105b9bda5c89ba74fec365c5a5f8fa6 100644
--- a/packages/Catch2/docs/command-line.md
+++ b/packages/Catch2/docs/command-line.md
@@ -93,7 +93,8 @@ Inclusions and exclusions are evaluated in left-to-right order.
 
 Test case examples:
 
-<pre>thisTestOnly            Matches the test case called, 'thisTestOnly'
+```
+thisTestOnly            Matches the test case called, 'thisTestOnly'
 "this test only"        Matches the test case called, 'this test only'
 these*                  Matches all cases starting with 'these'
 exclude:notThis         Matches all tests except, 'notThis'
@@ -101,8 +102,9 @@ exclude:notThis         Matches all tests except, 'notThis'
 ~*private*              Matches all tests except those that contain 'private'
 a* ~ab* abc             Matches all tests that start with 'a', except those that
                         start with 'ab', except 'abc', which is included
+~[tag1]                 Matches all tests except those tagged with '[tag1]'
 -# [#somefile]          Matches all tests from the file 'somefile.cpp'
-</pre>
+```
 
 Names within square brackets are interpreted as tags.
 A series of tags form an AND expression whereas a comma-separated sequence forms an OR expression. e.g.:
@@ -224,7 +226,7 @@ When set to ```yes``` Catch will report the duration of each test case, in milli
 
 <pre>-D, --min-duration &lt;value></pre>
 
-> `--min-duration` was [introduced](https://github.com/catchorg/Catch2/pull/1910) in Catch 2.13.0
+> `--min-duration` was [introduced](https://github.com/catchorg/Catch2/pull/1910) in Catch2 2.13.0
 
 When set, Catch will report the duration of each test case that took more
 than &lt;value> seconds, in milliseconds. This option is overriden by both
@@ -300,7 +302,7 @@ either before running any tests, after running all tests - or both, depending on
 ## Specify the number of benchmark samples to collect
 <pre>--benchmark-samples &lt;# of samples&gt;</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 When running benchmarks a number of "samples" is collected. This is the base data for later statistical analysis.
 Per sample a clock resolution dependent number of iterations of the user code is run, which is independent of the number of samples. Defaults to 100.
@@ -309,7 +311,7 @@ Per sample a clock resolution dependent number of iterations of the user code is
 ## Specify the number of resamples for bootstrapping
 <pre>--benchmark-resamples &lt;# of resamples&gt;</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 After the measurements are performed, statistical [bootstrapping] is performed
 on the samples. The number of resamples for that bootstrapping is configurable
@@ -324,7 +326,7 @@ defaults to 95%).
 ## Specify the confidence-interval for bootstrapping
 <pre>--benchmark-confidence-interval &lt;confidence-interval&gt;</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 The confidence-interval is used for statistical bootstrapping on the samples to
 calculate the upper and lower bounds of mean and standard deviation.
@@ -334,7 +336,7 @@ Must be between 0 and 1 and defaults to 0.95.
 ## Disable statistical analysis of collected benchmark samples
 <pre>--benchmark-no-analysis</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1616) in Catch2 2.9.0.
 
 When this flag is specified no bootstrapping or any other statistical analysis is performed.
 Instead the user code is only measured and the plain mean from the samples is reported.
@@ -343,7 +345,7 @@ Instead the user code is only measured and the plain mean from the samples is re
 ## Specify the amount of time in milliseconds spent on warming up each test
 <pre>--benchmark-warmup-time</pre>
 
-> [Introduced](https://github.com/catchorg/Catch2/pull/1844) in Catch 2.11.2.
+> [Introduced](https://github.com/catchorg/Catch2/pull/1844) in Catch2 2.11.2.
 
 Configure the amount of time spent warming up each test.
 
diff --git a/packages/Catch2/docs/commercial-users.md b/packages/Catch2/docs/commercial-users.md
index 7d2e87d387b52ed7c96aab1d5d30f60d07437dfd..ca77b67dbabbf739a5b9c2eff7d01346f521a5a4 100644
--- a/packages/Catch2/docs/commercial-users.md
+++ b/packages/Catch2/docs/commercial-users.md
@@ -1,22 +1,22 @@
 <a id="top"></a>
-# Commercial users of Catch
+# Commercial users of Catch2
 
-As well as [Open Source](opensource-users.md#top) users Catch is widely used within proprietary code bases too.
-Many organisations like to keep this information internal, and that's fine, 
-but if you're more open it would be great if we could list the names of as
-many organisations as possible that use Catch somewhere in their codebase. 
-Enterprise environments often tend to be far more conservative in their tool adoption - 
-and being aware that other companies are using Catch can ease the path in.
+Catch2 is also widely used in proprietary code bases. This page contains
+some of them that are willing to share this information.
 
-So if you are aware of Catch usage in your organisation, and are fairly confident there is no issue with sharing this
-fact then please let us know - either directly, via a PR or 
-[issue](https://github.com/philsquared/Catch/issues), or on the [forums](https://groups.google.com/forum/?fromgroups#!forum/catch-forum).
+If you want to add your organisation, please check that there is no issue
+with you sharing this fact.
  
  - Bloomberg
  - [Bloomlife](https://bloomlife.com)
- - NASA
  - [Inscopix Inc.](https://www.inscopix.com/)
+ - Locksley.CZ
  - [Makimo](https://makimo.pl/)
+ - NASA
  - [UX3D](https://ux3d.io)
  - [King](https://king.com)
  
+
+---
+
+[Home](Readme.md#top)
diff --git a/packages/Catch2/docs/configuration.md b/packages/Catch2/docs/configuration.md
index e80f695e78f22b847a436b6bf033828c1df9be9c..51f71bc763cc41ed43ab9c1bec32f8bc5334d3dc 100644
--- a/packages/Catch2/docs/configuration.md
+++ b/packages/Catch2/docs/configuration.md
@@ -117,7 +117,7 @@ Catch's selection, by defining either `CATCH_CONFIG_CPP11_TO_STRING` or
     CATCH_CONFIG_CPP17_OPTIONAL             // Override std::optional support detection (checked by CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER)
     CATCH_CONFIG_CPP17_BYTE                 // Override std::byte support detection (Catch provides a StringMaker specialization by default)
 
-> `CATCH_CONFIG_CPP17_STRING_VIEW` was [introduced](https://github.com/catchorg/Catch2/issues/1376) in Catch 2.4.1.
+> `CATCH_CONFIG_CPP17_STRING_VIEW` was [introduced](https://github.com/catchorg/Catch2/issues/1376) in Catch2 2.4.1.
 
 Catch contains basic compiler/standard detection and attempts to use
 some C++17 features whenever appropriate. This automatic detection
@@ -141,7 +141,7 @@ by using `_NO_` in the macro, e.g. `CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS`.
     CATCH_CONFIG_ANDROID_LOGWRITE           // Use android's logging system for debug output
     CATCH_CONFIG_GLOBAL_NEXTAFTER           // Use nextafter{,f,l} instead of std::nextafter
 
-> [`CATCH_CONFIG_ANDROID_LOGWRITE`](https://github.com/catchorg/Catch2/issues/1743) and [`CATCH_CONFIG_GLOBAL_NEXTAFTER`](https://github.com/catchorg/Catch2/pull/1739) were introduced in Catch 2.10.0
+> [`CATCH_CONFIG_ANDROID_LOGWRITE`](https://github.com/catchorg/Catch2/issues/1743) and [`CATCH_CONFIG_GLOBAL_NEXTAFTER`](https://github.com/catchorg/Catch2/pull/1739) were introduced in Catch2 2.10.0
 
 Currently Catch enables `CATCH_CONFIG_WINDOWS_SEH` only when compiled with MSVC, because some versions of MinGW do not have the necessary Win32 API support.
 
@@ -197,13 +197,13 @@ By default, Catch does not stringify some types from the standard library. This
     CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER // Provide StringMaker specialization for std::optional (on C++17)
     CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS     // Defines all of the above
 
-> `CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1380) in Catch 2.4.1.
+> `CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1380) in Catch2 2.4.1.
 
-> `CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1510) in Catch 2.6.0.
+> `CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER` was [introduced](https://github.com/catchorg/Catch2/issues/1510) in Catch2 2.6.0.
 
 ## Disabling exceptions
 
-> Introduced in Catch 2.4.0.
+> Introduced in Catch2 2.4.0.
 
 By default, Catch2 uses exceptions to signal errors and to abort tests
 when an assertion from the `REQUIRE` family of assertions fails. We also
@@ -239,7 +239,7 @@ namespace Catch {
 
 ## Overriding Catch's debug break (`-b`)
 
-> [Introduced](https://github.com/catchorg/Catch2/pull/1846) in Catch 2.11.2.
+> [Introduced](https://github.com/catchorg/Catch2/pull/1846) in Catch2 2.11.2.
 
 You can override Catch2's break-into-debugger code by defining the
 `CATCH_BREAK_INTO_DEBUGGER()` macro. This can be used if e.g. Catch2 does
diff --git a/packages/Catch2/docs/contributing.md b/packages/Catch2/docs/contributing.md
index ddf8dea26d4368a3d2833a7535c55f5ff081ab0f..fd45cc44478afe385877b6240bca3d9476d1d87a 100644
--- a/packages/Catch2/docs/contributing.md
+++ b/packages/Catch2/docs/contributing.md
@@ -26,7 +26,7 @@ Ongoing development happens in the `devel` branch for Catch2 v3, and in
 
 Commits should be small and atomic. A commit is atomic when, after it is
 applied, the codebase, tests and all, still works as expected. Small
-commits are also prefered, as they make later operations with git history,
+commits are also preferred, as they make later operations with git history,
 whether it is bisecting, reverting, or something else, easier.
 
 _When submitting a pull request please do not include changes to the
@@ -147,9 +147,9 @@ be replaced with the actual version upon release. There are 2 styles
 of placeholders used through the documentation, you should pick one that
 fits your text better (if in doubt, take a look at the existing version
 tags for other features).
-  * `> [Introduced](link-to-issue-or-PR) in Catch X.Y.Z` - this
+  * `> [Introduced](link-to-issue-or-PR) in Catch2 X.Y.Z` - this
   placeholder is usually used after a section heading
-  * `> X (Y and Z) was [introduced](link-to-issue-or-PR) in Catch X.Y.Z`
+  * `> X (Y and Z) was [introduced](link-to-issue-or-PR) in Catch2 X.Y.Z`
   - this placeholder is used when you need to tag a subpart of something,
   e.g. a list
 
@@ -190,6 +190,24 @@ If want to contribute code, this section contains some simple rules
 and tips on things like code formatting, code constructions to avoid,
 and so on.
 
+### C++ standard version
+
+Catch2 currently targets C++14 as the minimum supported C++ version.
+Features from higher language versions should be used only sparingly,
+when the benefits from using them outweight the maintenance overhead.
+
+Example of good use of polyfilling features is our use of `conjunction`,
+where if available we use `std::conjunction` and otherwise provide our
+own implementation. The reason it is good is that the surface area for
+maintenance is quite small, and `std::conjunction` can directly use
+compiler built-ins, thus providing significant compilation benefits.
+
+Example of bad use of polyfilling features would be to keep around two
+sets of metaprogramming in the stringification implementation, once
+using C++14 compliant TMP and once using C++17's `if constexpr`. While
+the C++17 would provide significant compilation speedups, the maintenance
+cost would be too high.
+
 
 ### Formatting
 
@@ -210,7 +228,7 @@ are problematic and are not always caught by our CI infrastructure.
 #### Naked exceptions and exceptions-related function
 
 If you are throwing an exception, it should be done via `CATCH_ERROR`
-or `CATCH_RUNTIME_ERROR` in `catch_enforce.h`. These macros will handle
+or `CATCH_RUNTIME_ERROR` in `internal/catch_enforce.hpp`. These macros will handle
 the differences between compilation with or without exceptions for you.
 However, some platforms (IAR) also have problems with exceptions-related
 functions, such as `std::current_exceptions`. We do not have IAR in our
@@ -219,6 +237,18 @@ However, if you do, they should be kept behind a
 `CATCH_CONFIG_DISABLE_EXCEPTIONS` macro.
 
 
+#### Avoid `std::move` and `std::forward`
+
+`std::move` and `std::forward` provide nice semantic name for a specific
+`static_cast`. However, being function templates they have surprisingly
+high cost during compilation, and can also have a negative performance
+impact for low-optimization builds.
+
+You should be using `CATCH_MOVE` and `CATCH_FORWARD` macros from
+`internal/catch_move_and_forward.hpp` instead. They expand into the proper
+`static_cast`, and avoid the overhead of `std::move` and `std::forward`.
+
+
 #### Unqualified usage of functions from C's stdlib
 
 If you are using a function from C's stdlib, please include the header
@@ -227,6 +257,23 @@ there is no difference is wrong, QNX and VxWorks won't compile if you
 include the header as `<cfoo>` and call the function unqualified.
 
 
+#### User-Defined Literals (UDL) for Catch2' types
+
+Due to messy standardese and ... not great ... implementation of
+`-Wreserved-identifier` in Clang, avoid declaring UDLs as
+```cpp
+Approx operator "" _a(long double);
+```
+and instead declare them as
+```cpp
+Approx operator ""_a(long double);
+```
+
+Notice that the second version does not have a space between the `""` and
+the literal suffix.
+
+
+
 ### New source file template
 
 If you are adding new source file, there is a template you should use.
diff --git a/packages/Catch2/docs/deprecations.md b/packages/Catch2/docs/deprecations.md
index c0e51b46dca59c7d7359e6b482ba2d852617f6a3..e56b197a298f07d11a4eb8e2d201e057130c47b1 100644
--- a/packages/Catch2/docs/deprecations.md
+++ b/packages/Catch2/docs/deprecations.md
@@ -9,16 +9,15 @@ either of these is a breaking change, and thus will not happen until
 at least the next major release.
 
 
+### `ParseAndAddCatchTests.cmake`
 
-## Planned changes
-
-### `CHECKED_IF` and `CHECKED_ELSE`
+The CMake/CTest integration using `ParseAndAddCatchTests.cmake` is deprecated,
+as it can be replaced by `Catch.cmake` that provides the function
+`catch_discover_tests` to get tests directly from a CMake target via the
+command line interface instead of parsing C++ code with regular expressions.
 
-To make the `CHECKED_IF` and `CHECKED_ELSE` macros more useful, they will
-be marked as "OK to fail" (`Catch::ResultDisposition::SuppressFail` flag
-will be added), which means that their failure will not fail the test,
-making the `else` actually useful.
 
+## Planned changes
 
 ### Console Colour API
 
diff --git a/packages/Catch2/docs/generators.md b/packages/Catch2/docs/generators.md
index d48cf06b4fd6785e589f3ed53513ee47542c74a1..b8164f829fcb1b6b5930b5697fc215a89f26e56a 100644
--- a/packages/Catch2/docs/generators.md
+++ b/packages/Catch2/docs/generators.md
@@ -1,7 +1,7 @@
 <a id="top"></a>
 # Data Generators
 
-> Introduced in Catch 2.6.0.
+> Introduced in Catch2 2.6.0.
 
 Data generators (also known as _data driven/parametrized test cases_)
 let you reuse the same set of assertions across different input values.
@@ -92,7 +92,7 @@ TEST_CASE("Complex mix of sections and generates") {
 }
 ```
 
-> The ability to place `GENERATE` between two `SECTION`s was [introduced](https://github.com/catchorg/Catch2/issues/1938) in Catch 2.13.0.
+> The ability to place `GENERATE` between two `SECTION`s was [introduced](https://github.com/catchorg/Catch2/issues/1938) in Catch2 2.13.0.
 
 ## Provided generators
 
@@ -117,9 +117,9 @@ a test case,
   * `RangeGenerator<T>` -- generates all values inside an arithmetic range
   * `IteratorGenerator<T>` -- copies and returns values from an iterator range
 
-> `ChunkGenerator<T>`, `RandomIntegerGenerator<Integral>`, `RandomFloatGenerator<Float>` and `RangeGenerator<T>` were introduced in Catch 2.7.0.
+> `ChunkGenerator<T>`, `RandomIntegerGenerator<Integral>`, `RandomFloatGenerator<Float>` and `RangeGenerator<T>` were introduced in Catch2 2.7.0.
 
-> `IteratorGenerator<T>` was introduced in Catch 2.10.0.
+> `IteratorGenerator<T>` was introduced in Catch2 2.10.0.
 
 The generators also have associated helper functions that infer their
 type, making their usage much nicer. These are
@@ -139,11 +139,11 @@ type, making their usage much nicer. These are
 * `from_range(InputIterator from, InputIterator to)` for `IteratorGenerator<T>`
 * `from_range(Container const&)` for `IteratorGenerator<T>`
 
-> `chunk()`, `random()` and both `range()` functions were introduced in Catch 2.7.0.
+> `chunk()`, `random()` and both `range()` functions were introduced in Catch2 2.7.0.
 
-> `from_range` has been introduced in Catch 2.10.0
+> `from_range` has been introduced in Catch2 2.10.0
 
-> `range()` for floating point numbers has been introduced in Catch 2.11.0
+> `range()` for floating point numbers has been introduced in Catch2 2.11.0
 
 And can be used as shown in the example below to create a generator
 that returns 100 odd random number:
@@ -176,7 +176,7 @@ scope and thus capturing references is dangerous. If you need to use
 variables inside the generator expression, make sure you thought through
 the lifetime implications and use `GENERATE_COPY` or `GENERATE_REF`.**
 
-> `GENERATE_COPY` and `GENERATE_REF` were introduced in Catch 2.7.1.
+> `GENERATE_COPY` and `GENERATE_REF` were introduced in Catch2 2.7.1.
 
 You can also override the inferred type by using `as<type>` as the first
 argument to the macro. This can be useful when dealing with string literals,
diff --git a/packages/Catch2/docs/logging.md b/packages/Catch2/docs/logging.md
index 476b1e08484c7ddbb438413867cc5cde8d29f4c0..dbd4f912ad553e71d1cb9e17f3a0cacd9c9d655b 100644
--- a/packages/Catch2/docs/logging.md
+++ b/packages/Catch2/docs/logging.md
@@ -30,7 +30,7 @@ When the last `CHECK` fails in the "Bar" test case, then only one message will b
 
 ## Logging without local scope
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch 2.7.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
 
 `UNSCOPED_INFO` is similar to `INFO` with two key differences:
 
@@ -106,7 +106,7 @@ This semicolon will be removed with next major version. It is highly advised to
 
 **UNSCOPED_INFO(** _message expression_ **)**
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch 2.7.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1522) in Catch2 2.7.0.
 
 Similar to `INFO`, but messages are not limited to their own scope: They are removed from the buffer after each assertion, section or test case, whichever comes first.
 
diff --git a/packages/Catch2/docs/matchers.md b/packages/Catch2/docs/matchers.md
index a5bf13f2cce72f5301625916576e768775276fd1..6fdb15bfa0c64c840fa640f95c915a8df7688b2b 100644
--- a/packages/Catch2/docs/matchers.md
+++ b/packages/Catch2/docs/matchers.md
@@ -58,7 +58,7 @@ to a use-after-free (UAF):
 
 ```cpp
 #include <catch2/catch_test_macros.hpp>
-#include <catch2/matchers/catch_matchers_string.h>
+#include <catch2/matchers/catch_matchers_string.hpp>
 
 TEST_CASE("Bugs, bugs, bugs", "[Bug]"){
     std::string str = "Bugs as a service";
@@ -99,9 +99,9 @@ somewhere inside it.
 The `Equals` matcher matches a string if (and only if) the argument
 string is equal to `str`.
 
-Finally, the `Matches` matcher performs an ECMASCript regex match using
+Finally, the `Matches` matcher performs an ECMAScript regex match using
 `str` against the argument string. It is important to know that
-the match is performed agains the string as a whole, meaning that
+the match is performed against the string as a whole, meaning that
 the regex `"abc"` will not match input string `"abcd"`. To match
 `"abcd"`, you need to use e.g. `"abc.*"` as your regex.
 
@@ -125,7 +125,7 @@ These are
  * `Equals` which checks whether the result is exactly equal (order matters) to a specific vector
  * `UnorderedEquals` which checks whether the result is equal to a specific vector under a permutation
  * `Approx` which checks whether the result is "approx-equal" (order matters, but comparison is done via `Approx`) to a specific vector
-> Approx matcher was [introduced](https://github.com/catchorg/Catch2/issues/1499) in Catch 2.7.2.
+> Approx matcher was [introduced](https://github.com/catchorg/Catch2/issues/1499) in Catch2 2.7.2.
 
 An example usage:
 ```cpp
@@ -145,10 +145,10 @@ Catch2 provides 3 matchers that target floating point numbers. These
 are:
 
 * `WithinAbs(double target, double margin)`,
-* `WithinUlps(FloatingPoint target, uint64_t maxUlpDiff)`, and
+* `WithinULP(FloatingPoint target, uint64_t maxUlpDiff)`, and
 * `WithinRel(FloatingPoint target, FloatingPoint eps)`.
 
-> `WithinRel` matcher was introduced in Catch 2.10.0
+> `WithinRel` matcher was introduced in Catch2 2.10.0
 
 
 `WithinAbs` creates a matcher that accepts floating point numbers whose
@@ -161,6 +161,11 @@ away from the `target` value. The short version of what this means
 is that there is no more than `maxUlpDiff - 1` representeable floating
 point numbers between the argument for matching and the `target` value.
 
+**Important**: The WithinULP matcher requires the platform to use the
+[IEEE-754](https://en.wikipedia.org/wiki/IEEE_754) representation for
+floating point numbers.
+
+
 `WithinRel` creates a matcher that accepts floating point numbers that
 are _approximately equal_ with the `target` with tolerance of `eps.`
 Specifically, it matches if
@@ -243,7 +248,7 @@ Note that `DerivedException` in the example above has to derive from
 
 ### Generic range Matchers
 
-> Generic range matchers were introduced in Catch X.Y.Z
+> Generic range matchers were introduced in Catch2 X.Y.Z
 
 Catch2 also provides some matchers that use the new style matchers
 definitions to handle generic range-like types. These are:
@@ -294,7 +299,7 @@ within certain range. We will call it `IsBetweenMatcher<T>`:
 
 ```c++
 #include <catch2/catch_test_macros.hpp>
-#include <catch2/matchers/catch_matchers.h>
+#include <catch2/matchers/catch_matchers.hpp>
 // ...
 
 
@@ -345,7 +350,7 @@ style matchers arbitrarily.
 
 ## Writing custom matchers (new style)
 
-> New style matchers were introduced in Catch X.Y.Z
+> New style matchers were introduced in Catch2 X.Y.Z
 
 To create a new-style matcher, you have to create your own type that
 derives from `Catch::Matchers::MatcherGenericBase`. Your type has to
diff --git a/packages/Catch2/docs/migrate-v2-to-v3.md b/packages/Catch2/docs/migrate-v2-to-v3.md
index b9590b1f1700c9ea803843bc6090190ee971745f..41c4d470e546d86ae3ef7af1a6c7f60e8e71d999 100644
--- a/packages/Catch2/docs/migrate-v2-to-v3.md
+++ b/packages/Catch2/docs/migrate-v2-to-v3.md
@@ -1,7 +1,34 @@
 <a id="top"></a>
 # Migrating from v2 to v3
 
-If you want to migrate to v3, there are two basic approaches to do so.
+v3 is the next major version of Catch2 and brings three significant changes:
+ * Catch2 is now split into multiple headers
+ * Catch2 is now compiled as a static library
+ * C++14 is the minimum required C++ version
+
+There are many reasons why we decided to go from the old single-header
+distribution model to a more standard library distribution model. The
+big one is compile-time performance, but moving over to a split header
+distribution model also improves the future maintainability and
+extendability of the codebase. For example v3 adds a new kind of matchers
+without impacting the compilation times of users that do not use matchers
+in their tests. The new model is also more friendly towards package
+managers, such as vcpkg and Conan.
+
+The result of this move is a significant improvement in compilation
+times, e.g. the inclusion overhead of Catch2 in the common case has been
+reduced by roughly 80%. The improved ease of maintenance also led to
+various runtime performance improvements and the introduction of new features.
+For details, look at [the release notes of 3.0.1](release-notes.md#301).
+
+_Note that we still provide one header + one TU distribution but do
+not consider it the primarily supported option. You should also expect
+that the compilation times will be worse if you use this option._
+
+
+## How to migrate projects from v2 to v3
+
+To migrate to v3, there are two basic approaches to do so.
 
 1. Use `catch_amalgamated.hpp` and `catch_amalgamated.cpp`.
 2. Build Catch2 as a proper (static) library, and move to piecewise headers
@@ -54,4 +81,4 @@ reporter or listener, you might need to modify them a bit.
 
 ---
 
-[Home](Readme.md#top)
\ No newline at end of file
+[Home](Readme.md#top)
diff --git a/packages/Catch2/docs/opensource-users.md b/packages/Catch2/docs/opensource-users.md
index 17483f8b07a4033abf31d7d6c3aefeec30f1fe48..f709aa6e7d27b91a11b27aedba8d6752b6e8482a 100644
--- a/packages/Catch2/docs/opensource-users.md
+++ b/packages/Catch2/docs/opensource-users.md
@@ -1,22 +1,25 @@
 <a id="top"></a>
-# Open Source projects using Catch
+# Open Source projects using Catch2
 
-Catch is great for open source. With its [liberal license](../LICENSE.txt) and single-header, dependency-free, distribution
-it's easy to just drop the header into your project and start writing tests - what's not to like?
+Catch2 is great for open source. It is licensed under the [Boost Software
+License (BSL)](../LICENSE.txt), has no further dependencies and supports
+two file distribution.
 
-As a result Catch is now being used in many Open Source projects, including some quite well known ones.
-This page is an attempt to track those projects. Obviously it can never be complete.
-This effort largely relies on the maintainers of the projects themselves updating this page and submitting a PR
-(or, if you prefer contact one of the maintainers of Catch directly, use the
-[forums](https://groups.google.com/forum/?fromgroups#!forum/catch-forum)), or raise an [issue](https://github.com/philsquared/Catch/issues) to let us know).
-Of course users of those projects might want to update this page too. That's fine - as long you're confident the project maintainers won't mind.
-If you're an Open Source project maintainer and see your project listed here but would rather it wasn't -
-just let us know via any of the previously mentioned means - although I'm sure there won't be many who feel that way.
+As a result, Catch2 is used for testing in many different Open Source
+projects. This page lists at least some of them, even though it will
+obviously never be complete (and does not have the ambition to be
+complete). Note that the list below is intended to be in alphabetical
+order, to avoid implications of relative importance of the projects.
+
+_Please only add projects here if you are their maintainer, or have the
+maintainer's explicit consent._
 
-Listing a project here does not imply endorsement and the plan is to keep these ordered alphabetically to avoid an implication of relative importance.
 
 ## Libraries & Frameworks
 
+### [alpaka](https://github.com/alpaka-group/alpaka)
+A header-only C++14 abstraction library for accelerator development.
+
 ### [ApprovalTests.cpp](https://github.com/approvals/ApprovalTests.cpp)
 C++11 implementation of Approval Tests, for quick, convenient testing of legacy code.
 
@@ -59,6 +62,9 @@ A header-only library that allows CPUs to execute unmodified HIP code. It is gen
 ### [Inja](https://github.com/pantor/inja)
 A header-only template engine for modern C++.
 
+### [LLAMA](https://github.com/alpaka-group/llama)
+A C++17 template header-only library for the abstraction of memory access patterns.
+
 ### [libcluon](https://github.com/chrberger/libcluon)
 A single-header-only library written in C++14 to glue distributed software components (UDP, TCP, shared memory) supporting natively Protobuf, LCM/ZCM, MsgPack, and JSON for dynamic message transformations in-between. 
 
@@ -103,6 +109,9 @@ A high available cloud native micro-service application management platform impl
 ### [ArangoDB](https://github.com/arangodb/arangodb)
 ArangoDB is a native multi-model database with flexible data models for documents, graphs, and key-values.
 
+### [d-SEAMS](https://github.com/d-SEAMS/seams-core)
+Open source molecular dynamics simulation structure analysis suite of tools in modern C++.
+
 ### [Giada - Your Hardcore Loop Machine](https://github.com/monocasual/giada)
 Minimal, open-source and cross-platform audio tool for live music production.
 
diff --git a/packages/Catch2/docs/other-macros.md b/packages/Catch2/docs/other-macros.md
index 994115f16d120b2f1af08fc54444e21b8fce8460..5e7f0af09f4d6a45a94edc35ba92c33d12aed4d8 100644
--- a/packages/Catch2/docs/other-macros.md
+++ b/packages/Catch2/docs/other-macros.md
@@ -15,6 +15,8 @@ stringification machinery to the _expr_ and records the result. As with
 evaluates to `true`. `CHECKED_ELSE( expr )` work similarly, but the block
 is entered only if the _expr_ evaluated to `false`.
 
+> `CHECKED_X` macros were changed to not count as failure in Catch2 X.Y.Z.
+
 Example:
 ```cpp
 int a = ...;
@@ -59,7 +61,7 @@ TEST_CASE( "SUCCEED showcase" ) {
 
 * `STATIC_REQUIRE`
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1362) in Catch 2.4.2.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1362) in Catch2 2.4.2.
 
 `STATIC_REQUIRE( expr )` is a macro that can be used the same way as a
 `static_assert`, but also registers the success with Catch2, so it is
@@ -117,24 +119,9 @@ is initiated. This means that it either needs to be done in a global
 constructor, or before Catch2's session is created in user's own main._
 
 
-* `ANON_TEST_CASE`
-
-`ANON_TEST_CASE` is a `TEST_CASE` replacement that will autogenerate
-unique name. The advantage of this is that you do not have to think
-of a name for the test case,`the disadvantage is that the name doesn't
-necessarily remain stable across different links, and thus it might be
-hard to run directly.
-
-Example:
-```cpp
-ANON_TEST_CASE() {
-    SUCCEED("Hello from anonymous test case");
-}
-```
-
 * `DYNAMIC_SECTION`
 
-> Introduced in Catch 2.3.0.
+> Introduced in Catch2 2.3.0.
 
 `DYNAMIC_SECTION` is a `SECTION` where the user can use `operator<<` to
 create the final name for that section. This can be useful with e.g.
diff --git a/packages/Catch2/docs/release-notes.md b/packages/Catch2/docs/release-notes.md
index b8538c288cef415729f02d841b35e0fac6130252..7e0f5a8f90d065d98e7a3ded517642a572795b94 100644
--- a/packages/Catch2/docs/release-notes.md
+++ b/packages/Catch2/docs/release-notes.md
@@ -3,6 +3,9 @@
 # Release notes
 **Contents**<br>
 [3.0.1](#301)<br>
+[2.13.6](#2136)<br>
+[2.13.5](#2135)<br>
+[2.13.4](#2134)<br>
 [2.13.3](#2133)<br>
 [2.13.2](#2132)<br>
 [2.13.1](#2131)<br>
@@ -47,7 +50,6 @@
 
 ## 3.0.1 (in progress)
 
-
 **Catch2 now uses statically compiled library as its distribution model.
 This also means that to get all of Catch2's functionality in a test file,
 you have to include multiple headers.**
@@ -74,12 +76,13 @@ new design.
 * Will Catch2 again distribute single-header version in the future?
   * No. But we do provide sqlite-style amalgamated distribution option. This means that you can download just 1 .cpp file and 1 header and place them next to your own sources. However, doing this has downsides similar to using the `catch_all.hpp` header.
 * Why the big breaking change caused by replacing `catch.hpp` with `catch_all.hpp`?
-  * The convenience header `catch_all.hpp` exists for two reasons. One of them is to provide a way for quick migration from Catch2, the second one is to provide a simple way to test things with Catch2. Using it for migration has one drawback in that it is **big**. This means that including it _will_ cause significant compile time drag, and so using it to migrate should be a concious decision by the user, not something they can just stumble into unknowingly.
+  * The convenience header `catch_all.hpp` exists for two reasons. One of them is to provide a way for quick migration from Catch2, the second one is to provide a simple way to test things with Catch2. Using it for migration has one drawback in that it is **big**. This means that including it _will_ cause significant compile time drag, and so using it to migrate should be a conscious decision by the user, not something they can just stumble into unknowingly.
 
 
 ### (Potentially) Breaking changes
 * **Catch2 now uses statically compiled library as its distribution model**
   * **Including `catch.hpp` no longer works**
+* **Catch2 now uses C++14 as the minimum support language version**
 * `ANON_TEST_CASE` has been removed, use `TEST_CASE` with no arguments instead (#1220)
 * `--list*` commands no longer have non-zero return code (#1410)
 * `--list-test-names-only` has been removed (#1190)
@@ -123,6 +126,11 @@ new design.
   * `EventListenerBase` now directly derives from `IStreamingReporter`, instead of deriving from `StreamingReporterBase`
 * `GENERATE` decays its arguments (#2012, #2040)
   * This means that `str` in `auto str = GENERATE("aa", "bb", "cc");` is inferred to `char const*` rather than `const char[2]`.
+* `--list-*` flags write their output to file specified by the `-o` flag
+* Many changes to reporter interfaces
+  * With the exception of the XmlReporter, the outputs of first party reporters should remain the same
+  * New pair of events were added
+  * One obsolete event was removed
 
 
 ### Improvements
@@ -130,16 +138,37 @@ new design.
   * This includes having templated `match` member function
   * See the [rewritten Matchers documentation](matchers.md#top) for details
   * Catch2 currently provides _some_ generic matchers, but there should be more before final release of v3
-    * So far, `IsEmpty`, `SizeIs`, and `Contains` are provided.
-    * At least `ElementsAre` and `UnorderedElementsAre` are planned.
-* Some runtime performance improvements
+    * `IsEmpty`, `SizeIs` which check that the range has specific properties
+    * `Contains`, which checks whether a range contains a specific element
+    * `AllMatch`, `AnyMatch`, `NoneMatch` range matchers, which apply matchers over a range of elements
 * Significant compilation time improvements
   * including `catch_test_macros.hpp` is 80% cheaper than including `catch.hpp`
+* Some runtime performance optimizations
+  * In all tested cases the v3 branch was faster, so the table below shows the speedup of v3 to v2 at the same task
+<a id="v3-runtime-optimization-table"></a>
+
+|                   task                      |  debug build | release build |
+|:------------------------------------------- | ------------:| -------------:|
+| Run 1M `REQUIRE(true)`                      |  1.10 ± 0.01 |   1.02 ± 0.06 |
+| Run 100 tests, 3^3 sections, 1 REQUIRE each |  1.27 ± 0.01 |   1.04 ± 0.01 |
+| Run 3k tests, no names, no tags             |  1.29 ± 0.01 |   1.05 ± 0.01 |
+| Run 3k tests, names, tags                   |  1.49 ± 0.01 |   1.22 ± 0.01 |
+| Run 1 out of 3k tests no names, no tags     |  1.68 ± 0.02 |   1.19 ± 0.22 |
+| Run 1 out of 3k tests, names, tags          |  1.79 ± 0.02 |   2.06 ± 0.23 |
+
+
+* POSIX platforms use `gmtime_r`, rather than `gmtime` when constructing a date string (#2008, #2165)
+* `--list-*` flags write their output to file specified by the `-o` flag (#2061, #2163)
+* `Approx::operator()` is now properly `const`
+* Catch2's internal helper variables no longer use reserved identifiers (#578)
 
 
 ### Fixes
 * The `INFO` macro no longer contains superfluous semicolon (#1456)
 * The `--list*` family of command line flags now return 0 on success (#1410, #1146)
+* Various ways of failing a benchmark are now counted and reporter properly
+* The ULP matcher now handles comparing numbers with different signs properly (#2152)
+* Universal ADL-found operators should no longer break decomposition (#2121)
 
 
 ### Other changes
@@ -156,6 +185,49 @@ new design.
 
 
 
+## 2.13.6
+
+### Fixes
+* Disabling all signal handlers no longer breaks compilation  (#2212, #2213)
+
+### Miscellaneous
+* `catch_discover_tests` should handle escaped semicolon (`;`) better (#2214, #2215)
+
+
+## 2.13.5
+
+### Improvements
+* Detection of MAC and IPHONE platforms has been improved (#2140, #2157)
+* Added workaround for bug in XLC 16.1.0.1 (#2155)
+* Add detection for LCC when it is masquerading as GCC (#2199)
+* Modified posix signal handling so it supports newer libcs (#2178)
+  * `MINSIGSTKSZ` was no longer usable in constexpr context.
+
+### Fixes
+* Fixed compilation of benchmarking when `min` and `max` macros are defined (#2159)
+  * Including `windows.h` without `NOMINMAX` remains a really bad idea, don't do it
+
+### Miscellaneous
+* The check whether Catch2 is being built as a subproject is now more reliable (#2202, #2204)
+  * The problem was that if the variable name used internally was defined the project including Catch2 as subproject, it would not be properly overwritten for Catch2's CMake.
+
+
+## 2.13.4
+
+### Improvements
+* Improved the hashing algorithm used for shuffling test cases (#2070)
+  * `TEST_CASE`s that differ only in the last character should be properly shuffled
+  * Note that this means that v2.13.4 gives you a different order of test cases than 2.13.3, even given the same seed.
+
+### Miscellaneous
+* Deprecated `ParseAndAddCatchTests` CMake integration (#2092)
+  * It is impossible to implement it properly for all the different test case variants Catch2 provides, and there are better options provided.
+  * Use `catch_discover_tests` instead, which uses runtime information about available tests.
+* Fixed bug in `catch_discover_tests` that would cause it to fail when used in specific project structures (#2119)
+* Added Bazel build file
+* Added an experimental static library target to CMake
+
+
 ## 2.13.3
 
 ### Fixes
diff --git a/packages/Catch2/docs/slow-compiles.md b/packages/Catch2/docs/slow-compiles.md
deleted file mode 100644
index 230f533059035150416e0387b2da9efd26fff160..0000000000000000000000000000000000000000
--- a/packages/Catch2/docs/slow-compiles.md
+++ /dev/null
@@ -1,72 +0,0 @@
-<a id="top"></a>
-# Why do my tests take so long to compile?
-
-**Contents**<br>
-[Short answer](#short-answer)<br>
-[Long answer](#long-answer)<br>
-[Practical example](#practical-example)<br>
-[Other possible solutions](#other-possible-solutions)<br>
-
-Several people have reported that test code written with Catch takes much longer to compile than they would expect. Why is that?
-
-Catch is implemented entirely in headers. There is a little overhead due to this - but not as much as you might think - and you can minimise it simply by organising your test code as follows:
-
-## Short answer
-Exactly one source file must ```#define``` either ```CATCH_CONFIG_MAIN``` or ```CATCH_CONFIG_RUNNER``` before ```#include```-ing Catch. In this file *do not write any test cases*! In most cases that means this file will just contain two lines (the ```#define``` and the ```#include```).
-
-## Long answer
-
-Usually C++ code is split between a header file, containing declarations and prototypes, and an implementation file (.cpp) containing the definition, or implementation, code. Each implementation file, along with all the headers that it includes (and which those headers include, etc), is expanded into a single entity called a translation unit - which is then passed to the compiler and compiled down to an object file.
-
-But functions and methods can also be written inline in header files. The downside to this is that these definitions will then be compiled in *every* translation unit that includes the header.
-
-Because Catch is implemented *entirely* in headers you might think that the whole of Catch must be compiled into every translation unit that uses it! Actually it's not quite as bad as that. Catch mitigates this situation by effectively maintaining the traditional separation between the implementation code and declarations. Internally the implementation code is protected by ```#ifdef```s and is conditionally compiled into only one translation unit. This translation unit is that one that ```#define```s ```CATCH_CONFIG_MAIN``` or ```CATCH_CONFIG_RUNNER```. Let's call this the main source file.
-
-As a result the main source file *does* compile the whole of Catch every time! So it makes sense to dedicate this file to *only* ```#define```-ing the identifier and ```#include```-ing Catch (and implementing the runner code, if you're doing that). Keep all your test cases in other files. This way you won't pay the recompilation cost for the whole of Catch.
-
-## Practical example
-Assume you have the `Factorial` function from the [tutorial](tutorial.md#top) in `factorial.cpp` (with forward declaration in `factorial.h`) and want to test it and keep the compile times down when adding new tests. Then you should have 2 files, `tests-main.cpp` and `tests-factorial.cpp`:
-
-```cpp
-// tests-main.cpp
-#define CATCH_CONFIG_MAIN
-#include "catch.hpp"
-```
-
-```cpp
-// tests-factorial.cpp
-#include "catch.hpp"
-
-#include "factorial.h"
-
-TEST_CASE( "Factorials are computed", "[factorial]" ) {
-    REQUIRE( Factorial(1) == 1 );
-    REQUIRE( Factorial(2) == 2 );
-    REQUIRE( Factorial(3) == 6 );
-    REQUIRE( Factorial(10) == 3628800 );
-}
-```
-
-After compiling `tests-main.cpp` once, it is enough to link it with separately compiled `tests-factorial.cpp`. This means that adding more tests to `tests-factorial.cpp`, will not result in recompiling Catch's main and the resulting compilation times will decrease substantially.
-
-```
-$ g++ tests-main.cpp -c
-$ g++ factorial.cpp -c
-$ g++ tests-main.o factorial.o tests-factorial.cpp -o tests && ./tests -r compact
-Passed 1 test case with 4 assertions.
-```
-
-Now, the next time we change the file `tests-factorial.cpp` (say we add `REQUIRE( Factorial(0) == 1)`), it is enough to recompile the tests instead of recompiling main as well:
-
-```
-$ g++ tests-main.o factorial.o tests-factorial.cpp -o tests && ./tests -r compact
-tests-factorial.cpp:11: failed: Factorial(0) == 1 for: 0 == 1
-Failed 1 test case, failed 1 assertion.
-```
-
-## Other possible solutions
-You can also opt to sacrifice some features in order to speed-up Catch's compilation times. For details see the [documentation on Catch's compile-time configuration](configuration.md#other-toggles).
-
----
-
-[Home](Readme.md#top)
diff --git a/packages/Catch2/docs/test-cases-and-sections.md b/packages/Catch2/docs/test-cases-and-sections.md
index e9308c5ef0c78cb331959044b65d15157bf3d44c..7622eef36e8961adb7a157baddf512d649303ea4 100644
--- a/packages/Catch2/docs/test-cases-and-sections.md
+++ b/packages/Catch2/docs/test-cases-and-sections.md
@@ -90,15 +90,65 @@ This macro maps onto ```TEST_CASE``` and works in the same way, except that the
 * **WHEN(** _something_ **)**
 * **THEN(** _something_ **)**
 
-These macros map onto ```SECTION```s except that the section names are the _something_s prefixed by "given: ", "when: " or "then: " respectively.
+These macros map onto ```SECTION```s except that the section names are the _something_ texts prefixed by
+"given: ", "when: " or "then: " respectively. These macros also map onto the AAA or A<sup>3</sup> test pattern
+(standing either for [Assemble-Activate-Assert](http://wiki.c2.com/?AssembleActivateAssert) or
+[Arrange-Act-Assert](http://wiki.c2.com/?ArrangeActAssert)), and in this context, the macros provide both code
+documentation and reporting of these parts of a test case without the need for extra comments or code to do so.
+
+Semantically, a `GIVEN` clause may have multiple _independent_ `WHEN` clauses within it. This allows a test
+to have, e.g., one set of "given" objects and multiple subtests using those objects in various ways in each
+of the `WHEN` clauses without repeating the initialisation from the `GIVEN` clause. When there are _dependent_
+clauses -- such as a second `WHEN` clause that should only happen _after_ the previous `WHEN` clause has been
+executed and validated -- there are additional macros starting with `AND_`:
 
 * **AND_GIVEN(** _something_ **)**
 * **AND_WHEN(** _something_ **)**
 * **AND_THEN(** _something_ **)**
 
-Similar to ```GIVEN```, ```WHEN``` and ```THEN``` except that the prefixes start with "and ". These are used to chain ```GIVEN```s, ```WHEN```s and ```THEN```s together.
+These are used to chain ```GIVEN```s, ```WHEN```s and ```THEN```s together. The `AND_*` clause is placed
+_inside_ the clause on which it depends. There can be multiple _independent_ clauses that are all _dependent_
+on a single outer clause.
+```cpp
+SCENARIO( "vector can be sized and resized" ) {
+    GIVEN( "An empty vector" ) {
+        auto v = std::vector<std::string>{};
+
+        // Validate assumption of the GIVEN clause
+        THEN( "The size and capacity start at 0" ) {
+            REQUIRE( v.size() == 0 );
+            REQUIRE( v.capacity() == 0 );
+        }
+
+        // Validate one use case for the GIVEN object
+        WHEN( "push_back() is called" ) {
+            v.push_back("hullo");
+
+            THEN( "The size changes" ) {
+                REQUIRE( v.size() == 1 );
+                REQUIRE( v.capacity() >= 1 );
+            }
+        }
+    }
+}
+```
+
+This code will result in two runs through the scenario:
+```
+Scenario : vector can be sized and resized
+  Given  : An empty vector
+  Then   : The size and capacity start at 0
+
+Scenario : vector can be sized and resized
+  Given  : An empty vector
+  When   : push_back() is called
+  Then   : The size changes
+```
+
+See also [runnable example on godbolt](https://godbolt.org/z/e5vPPM),
+with a more complicated (and failing) example.
 
-> `AND_GIVEN` was [introduced](https://github.com/catchorg/Catch2/issues/1360) in Catch 2.4.0.
+> `AND_GIVEN` was [introduced](https://github.com/catchorg/Catch2/issues/1360) in Catch2 2.4.0.
 
 When any of these macros are used the console reporter recognises them and formats the test case header such that the Givens, Whens and Thens are aligned to aid readability.
 
@@ -112,7 +162,7 @@ by types, in the form of `TEMPLATE_TEST_CASE`,
 
 * **TEMPLATE_TEST_CASE(** _test name_ , _tags_,  _type1_, _type2_, ..., _typen_ **)**
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1437) in Catch 2.5.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1437) in Catch2 2.5.0.
 
 _test name_ and _tag_ are exactly the same as they are in `TEST_CASE`,
 with the difference that the tag string must be provided (however, it
@@ -164,7 +214,7 @@ TEMPLATE_TEST_CASE( "vectors can be sized and resized", "[vector][template]", in
 
 * **TEMPLATE_PRODUCT_TEST_CASE(** _test name_ , _tags_, (_template-type1_, _template-type2_, ..., _template-typen_), (_template-arg1_, _template-arg2_, ..., _template-argm_) **)**
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch 2.6.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1468) in Catch2 2.6.0.
 
 _template-type1_ through _template-typen_ is list of template template
 types which should be combined with each of _template-arg1_ through
@@ -209,7 +259,7 @@ is very high and should not be encountered in practice._
 
 * **TEMPLATE_LIST_TEST_CASE(** _test name_, _tags_, _type list_ **)**
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1627) in Catch 2.9.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1627) in Catch2 2.9.0.
 
 _type list_ is a generic list of types on which test case should be instantiated.
 List can be `std::tuple`, `boost::mpl::list`, `boost::mp11::mp_list` or anything with
@@ -229,7 +279,7 @@ TEMPLATE_LIST_TEST_CASE("Template test case with test types specified inside std
 
 ## Signature based parametrised test cases
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch 2.8.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch2 2.8.0.
 
 In addition to [type parametrised test cases](#type-parametrised-test-cases) Catch2 also supports
 signature base parametrised test cases, in form of `TEMPLATE_TEST_CASE_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_SIG`.
@@ -251,7 +301,7 @@ Currently Catch2 support up to 11 template parameters in signature
 
 * **TEMPLATE_TEST_CASE_SIG(** _test name_ , _tags_,  _signature_, _type1_, _type2_, ..., _typen_ **)**
 
-Inside `TEMPLATE_TEST_CASE_SIG` test case you can use the names of template parameters as defined in _signature_. 
+Inside `TEMPLATE_TEST_CASE_SIG` test case you can use the names of template parameters as defined in _signature_.
 
 ```cpp
 TEMPLATE_TEST_CASE_SIG("TemplateTestSig: arrays can be created from NTTP arguments", "[vector][template][nttp]",
diff --git a/packages/Catch2/docs/test-fixtures.md b/packages/Catch2/docs/test-fixtures.md
index 832bba128023811273e72b1569256d2d352f6fee..d797214fbeda8a2bdbbb38aceb66af58097c9444 100644
--- a/packages/Catch2/docs/test-fixtures.md
+++ b/packages/Catch2/docs/test-fixtures.md
@@ -88,7 +88,7 @@ the limit is very high and should not be encountered in practice._
 
 ## Signature-based parametrised test fixtures
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch 2.8.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1609) in Catch2 2.8.0.
 
 Catch2 also provides `TEMPLATE_TEST_CASE_METHOD_SIG` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG` to support
 fixtures using non-type template parameters. These test cases work similar to `TEMPLATE_TEST_CASE_METHOD` and `TEMPLATE_PRODUCT_TEST_CASE_METHOD`,
diff --git a/packages/Catch2/docs/tostring.md b/packages/Catch2/docs/tostring.md
index 87a0caf0dfecbe8948bfe123c661067c3634a682..adce3cc764feda479a26389fba74f64733058da3 100644
--- a/packages/Catch2/docs/tostring.md
+++ b/packages/Catch2/docs/tostring.md
@@ -71,7 +71,7 @@ CATCH_TRANSLATE_EXCEPTION( MyType const& ex ) {
 
 ## Enums
 
-> Introduced in Catch 2.8.0.
+> Introduced in Catch2 2.8.0.
 
 Enums that already have a `<<` overload for `std::ostream` will convert to strings as expected.
 If you only need to convert enums to strings for test reporting purposes you can provide a `StringMaker` specialisations as any other type.
@@ -110,7 +110,7 @@ TEST_CASE() {
 
 ## Floating point precision
 
-> [Introduced](https://github.com/catchorg/Catch2/issues/1614) in Catch 2.8.0.
+> [Introduced](https://github.com/catchorg/Catch2/issues/1614) in Catch2 2.8.0.
 
 Catch provides a built-in `StringMaker` specialization for both `float`
 and `double`. By default, it uses what we think is a reasonable precision,
diff --git a/packages/Catch2/docs/tutorial.md b/packages/Catch2/docs/tutorial.md
index 45cb9919b5b745096bc7f39a6fa3474d946d9104..f764321fe4c31309a85433d260eb038159eff1ae 100644
--- a/packages/Catch2/docs/tutorial.md
+++ b/packages/Catch2/docs/tutorial.md
@@ -10,33 +10,13 @@
 [Type parametrised test cases](#type-parametrised-test-cases)<br>
 [Next steps](#next-steps)<br>
 
-## Getting Catch2
-
-The simplest way to get Catch2 is to download the latest
-[amalgamated header](https://raw.githubusercontent.com/catchorg/Catch2/devel/extras/catch_amalgamated.hpp)
-and [amalgamated source file](https://raw.githubusercontent.com/catchorg/Catch2/devel/extras/catch_amalgamated.cpp).
-
-These two files are the result of merging all headers, respectively
-all source files, into one file. To use them, just drop them next to your
-own project. Using Catch2 like this has a significant disadvantage in terms
-of final compilation times, but it works well enough to get you started.
-
-Later, you should move towards using Catch2 as a proper library, preferably
-via CMake. See our documentation on the [CMake integration](cmake-integration.md#top).
-
-
-_If you have installed Catch2 from system package manager, or CMake
-package, you need to include the header as `#include <catch2/catch.hpp>`_
-
-
---------
 
-The rest of this page is currently waiting for rewrite for the v3
-release. It might not be accurate in places, and likely doesn't mention
-the proper header to include, or might refer to outdated functionality,
-like the `CATCH_CONFIG_MAIN` macro.
+## Getting Catch2
 
---------
+Ideally you should be using Catch2 through its [CMake integration](cmake-integration.md#top).
+Catch2 also provides pkg-config files and single TU distribution, but this
+documentation will assume you are using CMake. If you are using single-TU
+distribution instead, remember to replace the included header with `catch_amalgamated.hpp`.
 
 
 ## Writing tests
@@ -49,11 +29,8 @@ unsigned int Factorial( unsigned int number ) {
 }
 ```
 
-To keep things simple we'll put everything in a single file (<a href="#scaling-up">see later for more on how to structure your test files</a>).
-
 ```c++
-#define CATCH_CONFIG_MAIN  // This tells Catch to provide a main() - only do this in one cpp file
-#include "catch.hpp"
+#include <catch2/catch_test_macros.hpp>
 
 unsigned int Factorial( unsigned int number ) {
     return number <= 1 ? number : Factorial(number-1)*number;
@@ -69,13 +46,10 @@ TEST_CASE( "Factorials are computed", "[factorial]" ) {
 
 This will compile to a complete executable which responds to [command line arguments](command-line.md#top). If you just run it with no arguments it will execute all test cases (in this case there is just one), report any failures, report a summary of how many tests passed and failed and return the number of failed tests (useful for if you just want a yes/ no answer to: "did it work").
 
-If you run this as written it will pass. Everything is good. Right?
-Well, there is still a bug here. In fact the first version of this tutorial I posted here genuinely had the bug in! So it's not completely contrived (thanks to Daryle Walker (```@CTMacUser```) for pointing this out).
-
-What is the bug? Well what is the factorial of zero?
-[The factorial of zero is one](http://mathforum.org/library/drmath/view/57128.html) - which is just one of those things you have to know (and remember!).
-
-Let's add that to the test case:
+Anyway, as the tests above as written will pass, but there is a bug.
+The problem is that `Factorial(0)` should return 1 (due to [its
+definition](https://en.wikipedia.org/wiki/Factorial#Factorial_of_zero)).
+Let's add that as an assertion to the test case:
 
 ```c++
 TEST_CASE( "Factorials are computed", "[factorial]" ) {
@@ -87,7 +61,8 @@ TEST_CASE( "Factorials are computed", "[factorial]" ) {
 }
 ```
 
-Now we get a failure - something like:
+After another compile & run cycle, we will see a test failure. The output
+will look something like:
 
 ```
 Example.cpp:9: FAILED:
@@ -96,37 +71,51 @@ with expansion:
   0 == 1
 ```
 
-Note that we get the actual return value of Factorial(0) printed for us (0) - even though we used a natural expression with the == operator. That lets us immediately see what the problem is.
-
-Let's change the factorial function to:
+Note that the output contains both the original expression,
+`REQUIRE( Factorial(0) == 1 )` and the actual value returned by the call
+to the `Factorial` function: `0`.
 
+We can fix this bug by slightly modifying the `Factorial` function to:
 ```c++
 unsigned int Factorial( unsigned int number ) {
   return number > 1 ? Factorial(number-1)*number : 1;
 }
 ```
 
-Now all the tests pass.
-
-Of course there are still more issues to deal with. For example we'll hit problems when the return value starts to exceed the range of an unsigned int. With factorials that can happen quite quickly. You might want to add tests for such cases and decide how to handle them. We'll stop short of doing that here.
 
 ### What did we do here?
 
-Although this was a simple test it's been enough to demonstrate a few things about how Catch is used. Let's take a moment to consider those before we move on.
+Although this was a simple test it's been enough to demonstrate a few
+things about how Catch2 is used. Let's take a moment to consider those
+before we move on.
+
+* We introduce test cases with the `TEST_CASE` macro. This macro takes
+  one or two string arguments - a free form test name and, optionally,
+  one or more tags (for more see [Test cases and Sections](#test-cases-and-sections)).
+* The test automatically self-registers with the test runner, and user
+  does not have do anything more to ensure that it is picked up by the test
+  framework. _Note that you can run specific test, or set of tests,
+  through the [command line](command-line#top)._
+* The individual test assertions are written using the `REQUIRE` macro.
+  It accepts a boolean expression, and uses expression templates to
+  internally decompose it, so that it can be individually stringified
+  on test failure.
+  
+On the last point, note that there are more testing macros available,
+because not all useful checks can be expressed as a simple boolean
+expression. As an example, checking that an expression throws an exception
+is done with the `REQUIRE_THROWS` macro. More on that later.
 
-1. All we did was ```#define``` one identifier and ```#include``` one header and we got everything - even an implementation of ```main()``` that will [respond to command line arguments](command-line.md#top). You can only use that ```#define``` in one implementation file, for (hopefully) obvious reasons. Once you have more than one file with unit tests in you'll just ```#include "catch.hpp"``` and go. Usually it's a good idea to have a dedicated implementation file that just has ```#define CATCH_CONFIG_MAIN``` and ```#include "catch.hpp"```. You can also provide your own implementation of main and drive Catch yourself (see [Supplying-your-own-main()](own-main.md#top)).
-2. We introduce test cases with the ```TEST_CASE``` macro. This macro takes one or two arguments - a free form test name and, optionally, one or more tags (for more see <a href="#test-cases-and-sections">Test cases and Sections</a>). The test name must be unique. You can run sets of tests by specifying a wildcarded test name or a tag expression. See the [command line docs](command-line.md#top) for more information on running tests.
-3. The name and tags arguments are just strings. We haven't had to declare a function or method - or explicitly register the test case anywhere. Behind the scenes a function with a generated name is defined for you, and automatically registered using static registry classes. By abstracting the function name away we can name our tests without the constraints of identifier names.
-4. We write our individual test assertions using the ```REQUIRE``` macro. Rather than a separate macro for each type of condition we express the condition naturally using C/C++ syntax. Behind the scenes a simple set of expression templates captures the left-hand-side and right-hand-side of the expression so we can display the values in our test report. As we'll see later there _are_ other assertion macros - but because of this technique the number of them is drastically reduced.
 
-<a id="test-cases-and-sections"></a>
 ## Test cases and sections
 
-Most test frameworks have a class-based fixture mechanism. That is, test cases map to methods on a class and common setup and teardown can be performed in ```setup()``` and ```teardown()``` methods (or constructor/ destructor in languages, like C++, that support deterministic destruction).
-
-While Catch fully supports this way of working there are a few problems with the approach. In particular the way your code must be split up, and the blunt granularity of it, may cause problems. You can only have one setup/ teardown pair across a set of methods, but sometimes you want slightly different setup in each method, or you may even want several levels of setup (a concept which we will clarify later on in this tutorial). It was <a href="http://jamesnewkirk.typepad.com/posts/2007/09/why-you-should-.html">problems like these</a> that led James Newkirk, who led the team that built NUnit, to start again from scratch and <a href="http://jamesnewkirk.typepad.com/posts/2007/09/announcing-xuni.html">build xUnit</a>).
+Like most test frameworks, Catch2 supports a class-based fixture mechanism,
+where individual tests are methods on class and setup/teardown can be
+done in constructor/destructor of the type.
 
-Catch takes a different approach (to both NUnit and xUnit) that is a more natural fit for C++ and the C family of languages. This is best explained through an example ([code](../examples/100-Fix-Section.cpp)):
+However, idiomatic usage of Catch2 avoids using it in favour of free
+standing test cases using _sections_ to share setup and teardown code. 
+This is best explained through an example ([code](../examples/100-Fix-Section.cpp)):
 
 ```c++
 TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
@@ -163,29 +152,42 @@ TEST_CASE( "vectors can be sized and resized", "[vector]" ) {
 }
 ```
 
-For each ```SECTION``` the ```TEST_CASE``` is executed from the start - so as we enter each section we know that size is 5 and capacity is at least 5. We enforced those requirements with the ```REQUIRE```s at the top level so we can be confident in them.
-This works because the ```SECTION``` macro contains an if statement that calls back into Catch to see if the section should be executed. One leaf section is executed on each run through a ```TEST_CASE```. The other sections are skipped. Next time through the next section is executed, and so on until no new sections are encountered.
+For each `SECTION` the `TEST_CASE` is executed from the start. This means
+that each section is entered with a freshly constructed vector `v`, that
+we know has size 5 and capacity at least 5, because the two assertions
+are also checked before the section is entered. Each run through a test
+case will execute one, and only one, leaf section.
 
-So far so good - this is already an improvement on the setup/teardown approach because now we see our setup code inline and use the stack.
+Section can also be nested, in which case the parent section can be
+entered multiple times, once for each leaf section. Nested sections are
+most useful when you have multiple tests that share part of the set up.
+To continue on the vector example above, you could add a check that
+`std::vector::reserve` does not remove unused excess capacity, like this:
 
-The power of sections really shows, however, when we need to execute a sequence of checked operations. Continuing the vector example, we might want to verify that attempting to reserve a capacity smaller than the current capacity of the vector changes nothing. We can do that, naturally, like so:
-
-```c++
+```cpp
     SECTION( "reserving bigger changes capacity but not size" ) {
         v.reserve( 10 );
 
         REQUIRE( v.size() == 5 );
         REQUIRE( v.capacity() >= 10 );
-
-        SECTION( "reserving smaller again does not change capacity" ) {
+        SECTION( "reserving down unused capacity does not change capacity" ) {
             v.reserve( 7 );
-
+            REQUIRE( v.size() == 5 );
             REQUIRE( v.capacity() >= 10 );
         }
     }
 ```
 
-Sections can be nested to an arbitrary depth (limited only by your stack size). Each leaf section (i.e. a section that contains no nested sections) will be executed exactly once, on a separate path of execution from any other leaf section (so no leaf section can interfere with another). A failure in a parent section will prevent nested sections from running - but then that's the idea.
+Another way to look at sections is that they are a way to define a tree 
+of paths through the test. Each section represents a node, and the final
+tree is walked in depth-first manner, with each path only visiting only
+one leaf node.
+
+There is no practical limit on nesting sections, as long as your compiler
+can handle them, but keep in mind that overly nested sections can become
+unreadable. From experience, having section nest more than 3 levels is
+usually very hard to follow and not worth the removed duplication.
+
 
 ## BDD-Style
 
@@ -247,24 +249,6 @@ Scenario: vectors can be sized and resized
       Then: the capacity changes but not the size
 ```
 
-<a id="scaling-up"></a>
-## Scaling up
-
-To keep the tutorial simple we put all our code in a single file. This is fine to get started - and makes jumping into Catch even quicker and easier. As you write more real-world tests, though, this is not really the best approach.
-
-The requirement is that the following block of code ([or equivalent](own-main.md#top)):
-
-```c++
-#define CATCH_CONFIG_MAIN
-#include "catch.hpp"
-```
-
-appears in _exactly one_ source file. Use as many additional cpp files (or whatever you call your implementation files) as you need for your tests, partitioned however makes most sense for your way of working. Each additional file need only ```#include "catch.hpp"``` - do not repeat the ```#define```!
-
-In fact it is usually a good idea to put the block with the ```#define``` [in its own source file](slow-compiles.md#top) (code example [main](../examples/020-TestCase-1.cpp), [tests](../examples/020-TestCase-2.cpp)).
-
-Do not write your tests in header files!
-
 
 ## Type parametrised test cases
 
diff --git a/packages/Catch2/docs/why-catch.md b/packages/Catch2/docs/why-catch.md
index 86cc55bc56b52d8982a9e31ef788cae2102f8848..a4b8c2e2852ef987d6bec34cb034ccb585bb9ac9 100644
--- a/packages/Catch2/docs/why-catch.md
+++ b/packages/Catch2/docs/why-catch.md
@@ -6,40 +6,53 @@ including (but not limited to),
 [Google Test](http://code.google.com/p/googletest/),
 [Boost.Test](http://www.boost.org/doc/libs/1_49_0/libs/test/doc/html/index.html),
 [CppUnit](http://sourceforge.net/apps/mediawiki/cppunit/index.php?title=Main_Page),
-[Cute](http://www.cute-test.com),
+[Cute](http://www.cute-test.com), and
 [many, many more](http://en.wikipedia.org/wiki/List_of_unit_testing_frameworks#C.2B.2B).
 
-So what does Catch bring to the party that differentiates it from these? Apart from a Catchy name, of course.
+So what does Catch2 bring to the party that differentiates it from these? Apart from the catchy name, of course.
+
 
 ## Key Features
 
-* Quick and Really easy to get started. Just download catch.hpp, `#include` it and you're away.
-* No external dependencies. As long as you can compile C++11 and have a C++ standard library available.
+* Quick and easy to get started. Just download two files, add them into your project and you're away.
+* No external dependencies. As long as you can compile C++14 and have the C++ standard library available.
 * Write test cases as, self-registering, functions (or methods, if you prefer).
 * Divide test cases into sections, each of which is run in isolation (eliminates the need for fixtures).
 * Use BDD-style Given-When-Then sections as well as traditional unit test cases.
 * Only one core assertion macro for comparisons. Standard C/C++ operators are used for the comparison - yet the full expression is decomposed and lhs and rhs values are logged.
 * Tests are named using free-form strings - no more couching names in legal identifiers.
 
+
 ## Other core features
 
 * Tests can be tagged for easily running ad-hoc groups of tests.
-* Failures can (optionally) break into the debugger on Windows and Mac.
+* Failures can (optionally) break into the debugger on common platforms.
 * Output is through modular reporter objects. Basic textual and XML reporters are included. Custom reporters can easily be added.
 * JUnit xml output is supported for integration with third-party tools, such as CI servers.
 * A default main() function is provided, but you can supply your own for complete control (e.g. integration into your own test runner GUI).
 * A command line parser is provided and can still be used if you choose to provided your own main() function.
-* Catch can test itself.
 * Alternative assertion macro(s) report failures but don't abort the test case
-* Floating point tolerance comparisons are built in using an expressive Approx() syntax.
+* Good set of facilities for floating point comparisons (`Catch::Approx` and full set of matchers)
 * Internal and friendly macros are isolated so name clashes can be managed
-* Matchers
+* Data generators (data driven test support)
+* Hamcrest-style Matchers for testing complex properties
+* Microbenchmarking support
+
 
-## Who else is using Catch?
+## Who else is using Catch2?
 
-See the list of [open source projects using Catch](opensource-users.md#top).
+A whole lot of people. According to the 2021 Jetbrains C++ ecosystem survey,
+about 11% of C++ programmers use Catch2 for unit testing, making it the
+second most popular unit testing framework.
+
+You can also take a look at the (incomplete) list of [open source projects](opensource-users.md#top)
+or the (very incomplete) list of [commercial users of Catch2](commercial-users.md#top)
+for some idea on who else also uses Catch2.
+
+---
 
-See the [tutorial](tutorial.md#top) to get more of a taste of using Catch in practice 
+See the [tutorial](tutorial.md#top) to get more of a taste of using
+Catch2 in practice.
 
 ---
 
diff --git a/packages/Catch2/examples/210-Evt-EventListeners.cpp b/packages/Catch2/examples/210-Evt-EventListeners.cpp
index 7a025375abbcfe6fe9c1ed0ab33a92bc025f5a8e..a4b938c9032575a1e36fd6a19ac6919bed82e785 100644
--- a/packages/Catch2/examples/210-Evt-EventListeners.cpp
+++ b/packages/Catch2/examples/210-Evt-EventListeners.cpp
@@ -366,10 +366,9 @@ struct MyListener : Catch::EventListenerBase {
         print( std::cout, 1, "- assertionInfo", assertionInfo );
     }
 
-    bool assertionEnded( Catch::AssertionStats const& assertionStats ) override {
+    void assertionEnded( Catch::AssertionStats const& assertionStats ) override {
         std::cout << "\nEvent: assertionEnded:\n";
         print( std::cout, 1, "- assertionStats", assertionStats );
-        return true;
     }
 };
 
diff --git a/packages/Catch2/examples/302-Gen-Table.cpp b/packages/Catch2/examples/302-Gen-Table.cpp
index 8b4fc97481d788cfd2f1d89998127b3d900c7c5f..74319518372296cac498e3ddc25fc954611f346f 100644
--- a/packages/Catch2/examples/302-Gen-Table.cpp
+++ b/packages/Catch2/examples/302-Gen-Table.cpp
@@ -48,7 +48,7 @@ TEST_CASE("Table allows pre-computed test inputs and outputs", "[example][genera
  * (technically C++17 but does not require -std in GCC/Clang). See
  *   https://stackoverflow.com/questions/12436586/tuple-vector-and-initializer-list
  *
- * - In C++17 mode std::tie() and the preceeding variable delcarations can be
+ * - In C++17 mode std::tie() and the preceding variable delcarations can be
  * replaced by structured bindings: auto [test_input, expected] = GENERATE(
  * table<std::string, size_t>({ ...
  */
diff --git a/packages/Catch2/extras/Catch.cmake b/packages/Catch2/extras/Catch.cmake
index b3f90a79e8bf16cf7210af5f651a966334142a66..a3885162039121634d4b72eb96d8545ce797a32f 100644
--- a/packages/Catch2/extras/Catch.cmake
+++ b/packages/Catch2/extras/Catch.cmake
@@ -202,4 +202,5 @@ endfunction()
 
 set(_CATCH_DISCOVER_TESTS_SCRIPT
   ${CMAKE_CURRENT_LIST_DIR}/CatchAddTests.cmake
+  CACHE INTERNAL "Catch2 full path to CatchAddTests.cmake helper file"
 )
diff --git a/packages/Catch2/extras/CatchAddTests.cmake b/packages/Catch2/extras/CatchAddTests.cmake
index 3688b72fd13255bef358be79209b1794b20aef3d..9210f5a4df30c49f744cd47bb5dac9e08f8806b1 100644
--- a/packages/Catch2/extras/CatchAddTests.cmake
+++ b/packages/Catch2/extras/CatchAddTests.cmake
@@ -16,7 +16,10 @@ set(tests)
 
 function(add_command NAME)
   set(_args "")
-  foreach(_arg ${ARGN})
+  # use ARGV* instead of ARGN, because ARGN splits arrays into multiple arguments
+  math(EXPR _last_arg ${ARGC}-1)
+  foreach(_n RANGE 1 ${_last_arg})
+    set(_arg "${ARGV${_n}}")
     if(_arg MATCHES "[^-./:a-zA-Z0-9_]")
       set(_args "${_args} [==[${_arg}]==]") # form a bracket_argument
     else()
diff --git a/packages/Catch2/extras/ParseAndAddCatchTests.cmake b/packages/Catch2/extras/ParseAndAddCatchTests.cmake
index 3f3da83198f3aac7bc555dbb69f7c50b887b9971..4771e02996a6167251a671a4b24e4edbc6b42be1 100644
--- a/packages/Catch2/extras/ParseAndAddCatchTests.cmake
+++ b/packages/Catch2/extras/ParseAndAddCatchTests.cmake
@@ -200,7 +200,7 @@ function(ParseAndAddCatchTests_ParseFile SourceFile TestTarget)
             # Escape commas in the test spec
             string(REPLACE "," "\\," Name ${Name})
 
-            # Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were neccessary,
+            # Work around CMake 3.18.0 change in `add_test()`, before the escaped quotes were necessary,
             # only with CMake 3.18.0 the escaped double quotes confuse the call. This change is reverted in 3.18.1
             # And properly introduced in 3.19 with the CMP0110 policy
             if(_cmp0110_value STREQUAL "NEW" OR ${CMAKE_VERSION} VERSION_EQUAL "3.18")
@@ -241,6 +241,7 @@ endfunction()
 
 # entry point
 function(ParseAndAddCatchTests TestTarget)
+    message(DEPRECATION "ParseAndAddCatchTest: function deprecated because of possibility of missed test cases. Consider using 'catch_discover_tests' from 'Catch.cmake'")
     ParseAndAddCatchTests_PrintDebugMessage("Started parsing ${TestTarget}")
     get_target_property(SourceFiles ${TestTarget} SOURCES)
     ParseAndAddCatchTests_PrintDebugMessage("Found the following sources: ${SourceFiles}")
diff --git a/packages/Catch2/extras/catch_amalgamated.cpp b/packages/Catch2/extras/catch_amalgamated.cpp
index 43f3eed9123a05150041430f070d1fce7b5d24b3..b67149f08e72e92549da2b44ce5fa4533e043dc4 100644
--- a/packages/Catch2/extras/catch_amalgamated.cpp
+++ b/packages/Catch2/extras/catch_amalgamated.cpp
@@ -1165,7 +1165,7 @@ namespace Catch {
         makeTestCaseInfo(std::string const& _className,
                          NameAndTags const& nameAndTags,
                          SourceLineInfo const& _lineInfo ) {
-        return Detail::unique_ptr<TestCaseInfo>(new TestCaseInfo(_className, nameAndTags, _lineInfo));
+        return Detail::make_unique<TestCaseInfo>(_className, nameAndTags, _lineInfo);
     }
 
     TestCaseInfo::TestCaseInfo(std::string const& _className,
diff --git a/packages/Catch2/extras/catch_amalgamated.hpp b/packages/Catch2/extras/catch_amalgamated.hpp
index 7f832413c6be01d4ac834cdbc3a4361a40a375c7..dd77291a9d3df7f814954f75f66022d91ec05448 100644
--- a/packages/Catch2/extras/catch_amalgamated.hpp
+++ b/packages/Catch2/extras/catch_amalgamated.hpp
@@ -950,7 +950,7 @@ namespace Catch {
 #include <iosfwd>
 
 namespace Catch {
-        
+
     struct ITransientExpression;
 
     class LazyExpression {
@@ -1430,7 +1430,7 @@ namespace Catch {
         double outlierVariance;
 
         template <typename Duration2>
-        operator BenchmarkStats<Duration2>() const {
+        explicit operator BenchmarkStats<Duration2>() const {
             std::vector<Duration2> samples2;
             samples2.reserve(samples.size());
             for (auto const& sample : samples) {
@@ -2575,7 +2575,7 @@ namespace Catch {
                         analysis.outlier_variance,
                     };
                 } else {
-                    std::vector<Duration> samples; 
+                    std::vector<Duration> samples;
                     samples.reserve(last - first);
 
                     Duration mean = Duration(0);
diff --git a/packages/Catch2/src/CMakeLists.txt b/packages/Catch2/src/CMakeLists.txt
index f22236395c8468eba7ea842c21f31a20724d62e2..7e30c1a479805e6ebd0820ae8426029ffb4a0f7c 100644
--- a/packages/Catch2/src/CMakeLists.txt
+++ b/packages/Catch2/src/CMakeLists.txt
@@ -65,6 +65,8 @@ set(INTERNAL_HEADERS
     ${SOURCES_DIR}/internal/catch_errno_guard.hpp
     ${SOURCES_DIR}/internal/catch_exception_translator_registry.hpp
     ${SOURCES_DIR}/internal/catch_fatal_condition_handler.hpp
+    ${SOURCES_DIR}/internal/catch_floating_point_helpers.hpp
+    ${SOURCES_DIR}/internal/catch_unique_name.hpp
     ${SOURCES_DIR}/generators/catch_generator_exception.hpp
     ${SOURCES_DIR}/generators/catch_generators.hpp
     ${SOURCES_DIR}/generators/catch_generators_adapters.hpp
@@ -79,7 +81,6 @@ set(INTERNAL_HEADERS
     ${SOURCES_DIR}/interfaces/catch_interfaces_reporter.hpp
     ${SOURCES_DIR}/interfaces/catch_interfaces_reporter_factory.hpp
     ${SOURCES_DIR}/interfaces/catch_interfaces_reporter_registry.hpp
-    ${SOURCES_DIR}/interfaces/catch_interfaces_runner.hpp
     ${SOURCES_DIR}/interfaces/catch_interfaces_tag_alias_registry.hpp
     ${SOURCES_DIR}/interfaces/catch_interfaces_testcase.hpp
     ${SOURCES_DIR}/internal/catch_lazy_expr.hpp
@@ -98,6 +99,7 @@ set(INTERNAL_HEADERS
     ${SOURCES_DIR}/catch_message.hpp
     ${SOURCES_DIR}/internal/catch_message_info.hpp
     ${SOURCES_DIR}/internal/catch_meta.hpp
+    ${SOURCES_DIR}/internal/catch_move_and_forward.hpp
     ${SOURCES_DIR}/internal/catch_option.hpp
     ${SOURCES_DIR}/internal/catch_output_redirect.hpp
     ${SOURCES_DIR}/internal/catch_platform.hpp
@@ -160,6 +162,7 @@ set(IMPL_SOURCES
     ${SOURCES_DIR}/internal/catch_enum_values_registry.cpp
     ${SOURCES_DIR}/internal/catch_exception_translator_registry.cpp
     ${SOURCES_DIR}/internal/catch_fatal_condition_handler.cpp
+    ${SOURCES_DIR}/internal/catch_floating_point_helpers.cpp
     ${SOURCES_DIR}/generators/internal/catch_generators_combined_tu.cpp
     ${SOURCES_DIR}/interfaces/catch_interfaces_combined_tu.cpp
     ${SOURCES_DIR}/interfaces/catch_interfaces_reporter.cpp
@@ -239,12 +242,15 @@ add_library(Catch2 STATIC
   ${BENCHMARK_HEADERS}
   ${BENCHMARK_SOURCES}
 )
+add_build_reproducibility_settings(Catch2)
 add_library(Catch2::Catch2 ALIAS Catch2)
 
 if (ANDROID)
     target_link_libraries(Catch2 INTERFACE log)
 endif()
 
+set_target_properties(Catch2 PROPERTIES DEBUG_POSTFIX "d")
+
 # depend on bunch of C++11 and C++14 features to have C++14 enabled by default
 target_compile_features(Catch2
   PUBLIC
@@ -280,12 +286,14 @@ target_include_directories(Catch2
 add_library(Catch2WithMain STATIC
     ${SOURCES_DIR}/internal/catch_main.cpp
 )
+add_build_reproducibility_settings(Catch2WithMain)
 add_library(Catch2::Catch2WithMain ALIAS Catch2WithMain)
 target_link_libraries(Catch2WithMain PUBLIC Catch2)
 set_target_properties(Catch2WithMain
   PROPERTIES
     OUTPUT_NAME "Catch2Main"
 )
+set_target_properties(Catch2WithMain PROPERTIES DEBUG_POSTFIX "d")
 
 if (NOT_SUBPROJECT)
     # create and install an export set for catch target as Catch2::Catch
diff --git a/packages/Catch2/src/catch2/benchmark/catch_benchmark.hpp b/packages/Catch2/src/catch2/benchmark/catch_benchmark.hpp
index 8ff69b4ac88c443819985a08c6d0e351152076b3..28664b613e2484bd62bd9b96b1ac3e001b3256be 100644
--- a/packages/Catch2/src/catch2/benchmark/catch_benchmark.hpp
+++ b/packages/Catch2/src/catch2/benchmark/catch_benchmark.hpp
@@ -11,9 +11,11 @@
 #define CATCH_BENCHMARK_HPP_INCLUDED
 
 #include <catch2/interfaces/catch_interfaces_config.hpp>
+#include <catch2/internal/catch_compiler_capabilities.hpp>
 #include <catch2/internal/catch_context.hpp>
 #include <catch2/interfaces/catch_interfaces_reporter.hpp>
-
+#include <catch2/internal/catch_unique_name.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 #include <catch2/benchmark/catch_chronometer.hpp>
 #include <catch2/benchmark/catch_clock.hpp>
 #include <catch2/benchmark/catch_environment.hpp>
@@ -34,11 +36,11 @@ namespace Catch {
     namespace Benchmark {
         struct Benchmark {
             Benchmark(std::string&& benchmarkName)
-                : name(std::move(benchmarkName)) {}
+                : name(CATCH_MOVE(benchmarkName)) {}
 
             template <class FUN>
             Benchmark(std::string&& benchmarkName , FUN &&func)
-                : fun(std::move(func)), name(std::move(benchmarkName)) {}
+                : fun(CATCH_MOVE(func)), name(CATCH_MOVE(benchmarkName)) {}
 
             template <typename Clock>
             ExecutionPlan<FloatDuration<Clock>> prepare(const IConfig &cfg, Environment<FloatDuration<Clock>> env) const {
@@ -80,16 +82,18 @@ namespace Catch {
                     auto analysis = Detail::analyse(*cfg, env, samples.begin(), samples.end());
                     BenchmarkStats<FloatDuration<Clock>> stats{ info, analysis.samples, analysis.mean, analysis.standard_deviation, analysis.outliers, analysis.outlier_variance };
                     getResultCapture().benchmarkEnded(stats);
-
+                } CATCH_CATCH_ANON (TestFailureException) {
+                    getResultCapture().benchmarkFailed("Benchmark failed due to failed assertion"_sr);
                 } CATCH_CATCH_ALL{
-                    if (translateActiveException() != Detail::benchmarkErrorMsg) // benchmark errors have been reported, otherwise rethrow.
-                        std::rethrow_exception(std::current_exception());
+                    getResultCapture().benchmarkFailed(translateActiveException());
+                    // We let the exception go further up so that the
+                    // test case is marked as failed.
+                    std::rethrow_exception(std::current_exception());
                 }
             }
 
             // sets lambda to be used in fun *and* executes benchmark!
-            template <typename Fun,
-                typename std::enable_if<!Detail::is_related<Fun, Benchmark>::value, int>::type = 0>
+            template <typename Fun, std::enable_if_t<!Detail::is_related<Fun, Benchmark>::value, int> = 0>
                 Benchmark & operator=(Fun func) {
                 fun = Detail::BenchmarkFunction(func);
                 run();
@@ -121,16 +125,16 @@ namespace Catch {
 #if defined(CATCH_CONFIG_PREFIX_ALL)
 
 #define CATCH_BENCHMARK(...) \
-    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
 #define CATCH_BENCHMARK_ADVANCED(name) \
-    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)
+    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name)
 
 #else
 
 #define BENCHMARK(...) \
-    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
+    INTERNAL_CATCH_BENCHMARK(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), INTERNAL_CATCH_GET_1_ARG(__VA_ARGS__,,), INTERNAL_CATCH_GET_2_ARG(__VA_ARGS__,,))
 #define BENCHMARK_ADVANCED(name) \
-    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(____C_A_T_C_H____B_E_N_C_H____), name)
+    INTERNAL_CATCH_BENCHMARK_ADVANCED(INTERNAL_CATCH_UNIQUE_NAME(CATCH2_INTERNAL_BENCHMARK_), name)
 
 #endif
 
diff --git a/packages/Catch2/src/catch2/benchmark/catch_chronometer.hpp b/packages/Catch2/src/catch2/benchmark/catch_chronometer.hpp
index 10fd5a2753f0596c2438249a4eb844991089448f..7ef08cef0d5a72b9c8d4508474dd4df7e533263a 100644
--- a/packages/Catch2/src/catch2/benchmark/catch_chronometer.hpp
+++ b/packages/Catch2/src/catch2/benchmark/catch_chronometer.hpp
@@ -14,6 +14,7 @@
 #include <catch2/benchmark/catch_optimizer.hpp>
 #include <catch2/benchmark/detail/catch_complete_invoke.hpp>
 #include <catch2/internal/catch_meta.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
     namespace Benchmark {
@@ -42,7 +43,7 @@ namespace Catch {
         struct Chronometer {
         public:
             template <typename Fun>
-            void measure(Fun&& fun) { measure(std::forward<Fun>(fun), is_callable<Fun(int)>()); }
+            void measure(Fun&& fun) { measure(CATCH_FORWARD(fun), is_callable<Fun(int)>()); }
 
             int runs() const { return repeats; }
 
diff --git a/packages/Catch2/src/catch2/benchmark/catch_constructor.hpp b/packages/Catch2/src/catch2/benchmark/catch_constructor.hpp
index 59600f21c107f8b2bd5a783fcc28c5ececaf0478..9da1eaaf8c015b53a6e096eef4e992927934bfa6 100644
--- a/packages/Catch2/src/catch2/benchmark/catch_constructor.hpp
+++ b/packages/Catch2/src/catch2/benchmark/catch_constructor.hpp
@@ -10,6 +10,8 @@
 #ifndef CATCH_CONSTRUCTOR_HPP_INCLUDED
 #define CATCH_CONSTRUCTOR_HPP_INCLUDED
 
+#include <catch2/internal/catch_move_and_forward.hpp>
+
 #include <type_traits>
 
 namespace Catch {
@@ -18,7 +20,7 @@ namespace Catch {
             template <typename T, bool Destruct>
             struct ObjectStorage
             {
-                using TStorage = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
+                using TStorage = std::aligned_storage_t<sizeof(T), std::alignment_of<T>::value>;
 
                 ObjectStorage() : data() {}
 
@@ -29,7 +31,7 @@ namespace Catch {
 
                 ObjectStorage(ObjectStorage&& other)
                 {
-                    new(&data) T(std::move(other.stored_object()));
+                    new(&data) T(CATCH_MOVE(other.stored_object()));
                 }
 
                 ~ObjectStorage() { destruct_on_exit<T>(); }
@@ -37,11 +39,11 @@ namespace Catch {
                 template <typename... Args>
                 void construct(Args&&... args)
                 {
-                    new (&data) T(std::forward<Args>(args)...);
+                    new (&data) T(CATCH_FORWARD(args)...);
                 }
 
                 template <bool AllowManualDestruction = !Destruct>
-                typename std::enable_if<AllowManualDestruction>::type destruct()
+                std::enable_if_t<AllowManualDestruction> destruct()
                 {
                     stored_object().~T();
                 }
@@ -49,10 +51,10 @@ namespace Catch {
             private:
                 // If this is a constructor benchmark, destruct the underlying object
                 template <typename U>
-                void destruct_on_exit(typename std::enable_if<Destruct, U>::type* = 0) { destruct<true>(); }
+                void destruct_on_exit(std::enable_if_t<Destruct, U>* = 0) { destruct<true>(); }
                 // Otherwise, don't
                 template <typename U>
-                void destruct_on_exit(typename std::enable_if<!Destruct, U>::type* = 0) { }
+                void destruct_on_exit(std::enable_if_t<!Destruct, U>* = 0) { }
 
                 T& stored_object() {
                     return *static_cast<T*>(static_cast<void*>(&data));
diff --git a/packages/Catch2/src/catch2/benchmark/catch_execution_plan.hpp b/packages/Catch2/src/catch2/benchmark/catch_execution_plan.hpp
index 1dd0d9b01d595c5594d398b70f76f05e29fee978..bcb366ce6ab510f24d0092f94aea1cfc8907fe2d 100644
--- a/packages/Catch2/src/catch2/benchmark/catch_execution_plan.hpp
+++ b/packages/Catch2/src/catch2/benchmark/catch_execution_plan.hpp
@@ -18,6 +18,7 @@
 #include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
 
 #include <algorithm>
+#include <iterator>
 
 namespace Catch {
     namespace Benchmark {
diff --git a/packages/Catch2/src/catch2/benchmark/catch_optimizer.hpp b/packages/Catch2/src/catch2/benchmark/catch_optimizer.hpp
index 6f03ea0d475c1d94e6f67e9e56a1989764cdf822..b9af3ea1de55707af0601903441a8f9c5784d68e 100644
--- a/packages/Catch2/src/catch2/benchmark/catch_optimizer.hpp
+++ b/packages/Catch2/src/catch2/benchmark/catch_optimizer.hpp
@@ -14,8 +14,9 @@
 #   include <atomic> // atomic_thread_fence
 #endif
 
+#include <catch2/internal/catch_move_and_forward.hpp>
+
 #include <type_traits>
-#include <utility>
 
 namespace Catch {
     namespace Benchmark {
@@ -56,13 +57,13 @@ namespace Catch {
         }
 
         template <typename Fn, typename... Args>
-        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<!std::is_same<void, decltype(fn(args...))>::value>::type {
-            deoptimize_value(std::forward<Fn>(fn) (std::forward<Args...>(args...)));
+        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<!std::is_same<void, decltype(fn(args...))>::value> {
+            deoptimize_value(CATCH_FORWARD(fn) (CATCH_FORWARD(args)...));
         }
 
         template <typename Fn, typename... Args>
-        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> typename std::enable_if<std::is_same<void, decltype(fn(args...))>::value>::type {
-            std::forward<Fn>(fn) (std::forward<Args...>(args...));
+        inline auto invoke_deoptimized(Fn&& fn, Args&&... args) -> std::enable_if_t<std::is_same<void, decltype(fn(args...))>::value> {
+            CATCH_FORWARD(fn) (CATCH_FORWARD(args)...);
         }
     } // namespace Benchmark
 } // namespace Catch
diff --git a/packages/Catch2/src/catch2/benchmark/catch_sample_analysis.hpp b/packages/Catch2/src/catch2/benchmark/catch_sample_analysis.hpp
index 579e7af9547481846405a3520baca197f833672a..6204aaf1417454c81e130d8a4dbdeec108d06000 100644
--- a/packages/Catch2/src/catch2/benchmark/catch_sample_analysis.hpp
+++ b/packages/Catch2/src/catch2/benchmark/catch_sample_analysis.hpp
@@ -13,10 +13,10 @@
 #include <catch2/benchmark/catch_clock.hpp>
 #include <catch2/benchmark/catch_estimate.hpp>
 #include <catch2/benchmark/catch_outlier_classification.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <algorithm>
 #include <vector>
-#include <string>
 #include <iterator>
 
 namespace Catch {
@@ -35,7 +35,7 @@ namespace Catch {
                 samples2.reserve(samples.size());
                 std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](Duration d) { return Duration2(d); });
                 return {
-                    std::move(samples2),
+                    CATCH_MOVE(samples2),
                     mean,
                     standard_deviation,
                     outliers,
diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.hpp
index 0a52a8451ae4740fc681039be2b7426891e69980..cf96a3d28b9628d755b361a92df15469720c6d53 100644
--- a/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.hpp
+++ b/packages/Catch2/src/catch2/benchmark/detail/catch_analyse.hpp
@@ -11,8 +11,11 @@
 #define CATCH_ANALYSE_HPP_INCLUDED
 
 #include <catch2/benchmark/catch_clock.hpp>
+#include <catch2/benchmark/catch_environment.hpp>
 #include <catch2/benchmark/catch_sample_analysis.hpp>
 #include <catch2/benchmark/detail/catch_stats.hpp>
+#include <catch2/interfaces/catch_interfaces_config.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <algorithm>
 #include <iterator>
@@ -43,7 +46,7 @@ namespace Catch {
                     samples2.reserve(samples.size());
                     std::transform(samples.begin(), samples.end(), std::back_inserter(samples2), [](double d) { return Duration(d); });
                     return {
-                        std::move(samples2),
+                        CATCH_MOVE(samples2),
                         wrap_estimate(analysis.mean),
                         wrap_estimate(analysis.standard_deviation),
                         outliers,
@@ -62,7 +65,7 @@ namespace Catch {
                     mean /= i;
 
                     return {
-                        std::move(samples),
+                        CATCH_MOVE(samples),
                         Estimate<Duration>{mean, mean, mean, 0.0},
                         Estimate<Duration>{Duration(0), Duration(0), Duration(0), 0.0},
                         OutlierClassification{},
diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp
index 4e0c55ffcfdfb74c5366cfe0505dcac5d6c9f9f2..655c863508fcaa911d776f46dff161125d9da05b 100644
--- a/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp
+++ b/packages/Catch2/src/catch2/benchmark/detail/catch_benchmark_function.hpp
@@ -14,19 +14,16 @@
 #include <catch2/benchmark/detail/catch_complete_invoke.hpp>
 #include <catch2/internal/catch_meta.hpp>
 #include <catch2/internal/catch_unique_ptr.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
-#include <cassert>
 #include <type_traits>
-#include <utility>
 
 namespace Catch {
     namespace Benchmark {
         namespace Detail {
-            template <typename T>
-            using Decay = typename std::decay<T>::type;
             template <typename T, typename U>
             struct is_related
-                : std::is_same<Decay<T>, Decay<U>> {};
+                : std::is_same<std::decay_t<T>, std::decay_t<U>> {};
 
             /// We need to reinvent std::function because every piece of code that might add overhead
             /// in a measurement context needs to have consistent performance characteristics so that we
@@ -39,7 +36,7 @@ namespace Catch {
             private:
                 struct callable {
                     virtual void call(Chronometer meter) const = 0;
-                    virtual callable* clone() const = 0;
+                    virtual Catch::Detail::unique_ptr<callable> clone() const = 0;
                     virtual ~callable(); // = default;
 
                     callable() = default;
@@ -48,10 +45,12 @@ namespace Catch {
                 };
                 template <typename Fun>
                 struct model : public callable {
-                    model(Fun&& fun_) : fun(std::move(fun_)) {}
+                    model(Fun&& fun_) : fun(CATCH_MOVE(fun_)) {}
                     model(Fun const& fun_) : fun(fun_) {}
 
-                    model<Fun>* clone() const override { return new model<Fun>(*this); }
+                    Catch::Detail::unique_ptr<callable> clone() const override {
+                        return Catch::Detail::make_unique<model<Fun>>( *this );
+                    }
 
                     void call(Chronometer meter) const override {
                         call(meter, is_callable<Fun(Chronometer)>());
@@ -76,24 +75,24 @@ namespace Catch {
                     : f(new model<do_nothing>{ {} }) {}
 
                 template <typename Fun,
-                    typename std::enable_if<!is_related<Fun, BenchmarkFunction>::value, int>::type = 0>
+                    std::enable_if_t<!is_related<Fun, BenchmarkFunction>::value, int> = 0>
                     BenchmarkFunction(Fun&& fun)
-                    : f(new model<typename std::decay<Fun>::type>(std::forward<Fun>(fun))) {}
+                    : f(new model<std::decay_t<Fun>>(CATCH_FORWARD(fun))) {}
 
                 BenchmarkFunction( BenchmarkFunction&& that ) noexcept:
-                    f( std::move( that.f ) ) {}
+                    f( CATCH_MOVE( that.f ) ) {}
 
                 BenchmarkFunction(BenchmarkFunction const& that)
                     : f(that.f->clone()) {}
 
                 BenchmarkFunction&
                 operator=( BenchmarkFunction&& that ) noexcept {
-                    f = std::move( that.f );
+                    f = CATCH_MOVE( that.f );
                     return *this;
                 }
 
                 BenchmarkFunction& operator=(BenchmarkFunction const& that) {
-                    f.reset(that.f->clone());
+                    f = that.f->clone();
                     return *this;
                 }
 
diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp
index 8cb132112124a51760df577fed1b5776c7544b5b..747b37a9968b302c8deb0c8127bde314be1db214 100644
--- a/packages/Catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp
+++ b/packages/Catch2/src/catch2/benchmark/detail/catch_complete_invoke.hpp
@@ -10,13 +10,13 @@
 #ifndef CATCH_COMPLETE_INVOKE_HPP_INCLUDED
 #define CATCH_COMPLETE_INVOKE_HPP_INCLUDED
 
-#include <catch2/internal/catch_enforce.hpp>
+#include <catch2/internal/catch_test_failure_exception.hpp>
 #include <catch2/internal/catch_meta.hpp>
 #include <catch2/interfaces/catch_interfaces_capture.hpp>
 #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <type_traits>
-#include <utility>
 
 namespace Catch {
     namespace Benchmark {
@@ -33,14 +33,14 @@ namespace Catch {
             struct CompleteInvoker {
                 template <typename Fun, typename... Args>
                 static Result invoke(Fun&& fun, Args&&... args) {
-                    return std::forward<Fun>(fun)(std::forward<Args>(args)...);
+                    return CATCH_FORWARD(fun)(CATCH_FORWARD(args)...);
                 }
             };
             template <>
             struct CompleteInvoker<void> {
                 template <typename Fun, typename... Args>
                 static CompleteType_t<void> invoke(Fun&& fun, Args&&... args) {
-                    std::forward<Fun>(fun)(std::forward<Args>(args)...);
+                    CATCH_FORWARD(fun)(CATCH_FORWARD(args)...);
                     return {};
                 }
             };
@@ -48,20 +48,14 @@ namespace Catch {
             // invoke and not return void :(
             template <typename Fun, typename... Args>
             CompleteType_t<FunctionReturnType<Fun, Args...>> complete_invoke(Fun&& fun, Args&&... args) {
-                return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(std::forward<Fun>(fun), std::forward<Args>(args)...);
+                return CompleteInvoker<FunctionReturnType<Fun, Args...>>::invoke(CATCH_FORWARD(fun), CATCH_FORWARD(args)...);
             }
 
-            extern const std::string benchmarkErrorMsg;
         } // namespace Detail
 
         template <typename Fun>
         Detail::CompleteType_t<FunctionReturnType<Fun>> user_code(Fun&& fun) {
-            CATCH_TRY{
-                return Detail::complete_invoke(std::forward<Fun>(fun));
-            } CATCH_CATCH_ALL{
-                getResultCapture().benchmarkFailed(translateActiveException());
-                CATCH_RUNTIME_ERROR(Detail::benchmarkErrorMsg);
-            }
+            return Detail::complete_invoke(CATCH_FORWARD(fun));
         }
     } // namespace Benchmark
 } // namespace Catch
diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp
index 0a49e51374b29d65476de8830e8e73361393ed35..3bba791e4091de6a6f999d76323d8235e9e35ad5 100644
--- a/packages/Catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp
+++ b/packages/Catch2/src/catch2/benchmark/detail/catch_estimate_clock.hpp
@@ -16,6 +16,7 @@
 #include <catch2/benchmark/detail/catch_measure.hpp>
 #include <catch2/benchmark/detail/catch_run_for_at_least.hpp>
 #include <catch2/benchmark/catch_clock.hpp>
+#include <catch2/internal/catch_unique_ptr.hpp>
 
 #include <algorithm>
 #include <iterator>
@@ -66,7 +67,9 @@ namespace Catch {
             }
             template <typename Clock>
             EnvironmentEstimate<FloatDuration<Clock>> estimate_clock_cost(FloatDuration<Clock> resolution) {
-                auto time_limit = std::min(resolution * clock_cost_estimation_tick_limit, FloatDuration<Clock>(clock_cost_estimation_time_limit));
+                auto time_limit = (std::min)(
+                    resolution * clock_cost_estimation_tick_limit,
+                    FloatDuration<Clock>(clock_cost_estimation_time_limit));
                 auto time_clock = [](int k) {
                     return Detail::measure<Clock>([k] {
                         for (int i = 0; i < k; ++i) {
@@ -92,7 +95,14 @@ namespace Catch {
 
             template <typename Clock>
             Environment<FloatDuration<Clock>> measure_environment() {
-                static Environment<FloatDuration<Clock>>* env = nullptr;
+#if defined(__clang__)
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wexit-time-destructors"
+#endif
+                static Catch::Detail::unique_ptr<Environment<FloatDuration<Clock>>> env;
+#if defined(__clang__)
+#    pragma clang diagnostic pop
+#endif
                 if (env) {
                     return *env;
                 }
@@ -101,7 +111,7 @@ namespace Catch {
                 auto resolution = Detail::estimate_clock_resolution<Clock>(iters);
                 auto cost = Detail::estimate_clock_cost<Clock>(resolution.mean);
 
-                env = new Environment<FloatDuration<Clock>>{ resolution, cost };
+                env = Catch::Detail::make_unique<Environment<FloatDuration<Clock>>>( Environment<FloatDuration<Clock>>{resolution, cost} );
                 return *env;
             }
         } // namespace Detail
diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_measure.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_measure.hpp
index 49df259ad486e57471409a7c45b00e35f29eafef..739e91e58947d6445ab7f36ab219bdcee86613b4 100644
--- a/packages/Catch2/src/catch2/benchmark/detail/catch_measure.hpp
+++ b/packages/Catch2/src/catch2/benchmark/detail/catch_measure.hpp
@@ -13,8 +13,7 @@
 #include <catch2/benchmark/catch_clock.hpp>
 #include <catch2/benchmark/detail/catch_complete_invoke.hpp>
 #include <catch2/benchmark/detail/catch_timing.hpp>
-
-#include <utility>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
     namespace Benchmark {
@@ -22,10 +21,10 @@ namespace Catch {
             template <typename Clock, typename Fun, typename... Args>
             TimingOf<Clock, Fun, Args...> measure(Fun&& fun, Args&&... args) {
                 auto start = Clock::now();
-                auto&& r = Detail::complete_invoke(fun, std::forward<Args>(args)...);
+                auto&& r = Detail::complete_invoke(fun, CATCH_FORWARD(args)...);
                 auto end = Clock::now();
                 auto delta = end - start;
-                return { delta, std::forward<decltype(r)>(r), 1 };
+                return { delta, CATCH_FORWARD(r), 1 };
             }
         } // namespace Detail
     } // namespace Benchmark
diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_repeat.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_repeat.hpp
index cc2ea0241af72f5a2cf6075c40f3b7281703d096..360af09b6fa8bb729e792576600105265ed60f4a 100644
--- a/packages/Catch2/src/catch2/benchmark/detail/catch_repeat.hpp
+++ b/packages/Catch2/src/catch2/benchmark/detail/catch_repeat.hpp
@@ -11,7 +11,7 @@
 #define CATCH_REPEAT_HPP_INCLUDED
 
 #include <type_traits>
-#include <utility>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
     namespace Benchmark {
@@ -26,8 +26,8 @@ namespace Catch {
                 Fun fun;
             };
             template <typename Fun>
-            repeater<typename std::decay<Fun>::type> repeat(Fun&& fun) {
-                return { std::forward<Fun>(fun) };
+            repeater<std::decay_t<Fun>> repeat(Fun&& fun) {
+                return { CATCH_FORWARD(fun) };
             }
         } // namespace Detail
     } // namespace Benchmark
diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp
index da08a92ba06cb29cf38b77591af397fe78eb1fde..6b2f56e77bc055a4abe55bc7f828ee918bda62d3 100644
--- a/packages/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp
+++ b/packages/Catch2/src/catch2/benchmark/detail/catch_run_for_at_least.hpp
@@ -16,8 +16,8 @@
 #include <catch2/benchmark/detail/catch_complete_invoke.hpp>
 #include <catch2/benchmark/detail/catch_timing.hpp>
 #include <catch2/internal/catch_meta.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
-#include <utility>
 #include <type_traits>
 
 namespace Catch {
@@ -32,24 +32,27 @@ namespace Catch {
                 Detail::ChronometerModel<Clock> meter;
                 auto&& result = Detail::complete_invoke(fun, Chronometer(meter, iters));
 
-                return { meter.elapsed(), std::move(result), iters };
+                return { meter.elapsed(), CATCH_MOVE(result), iters };
             }
 
             template <typename Clock, typename Fun>
-            using run_for_at_least_argument_t = typename std::conditional<is_callable<Fun(Chronometer)>::value, Chronometer, int>::type;
+            using run_for_at_least_argument_t = std::conditional_t<is_callable<Fun(Chronometer)>::value, Chronometer, int>;
 
 
             [[noreturn]]
             void throw_optimized_away_error();
 
             template <typename Clock, typename Fun>
-            TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>> run_for_at_least(ClockDuration<Clock> how_long, int seed, Fun&& fun) {
-                auto iters = seed;
+            TimingOf<Clock, Fun, run_for_at_least_argument_t<Clock, Fun>>
+                run_for_at_least(ClockDuration<Clock> how_long,
+                                 const int initial_iterations,
+                                 Fun&& fun) {
+                auto iters = initial_iterations;
                 while (iters < (1 << 30)) {
                     auto&& Timing = measure_one<Clock>(fun, iters, is_callable<Fun(Chronometer)>());
 
                     if (Timing.elapsed >= how_long) {
-                        return { Timing.elapsed, std::move(Timing.result), iters };
+                        return { Timing.elapsed, CATCH_MOVE(Timing.result), iters };
                     }
                     iters *= 2;
                 }
diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_stats.cpp b/packages/Catch2/src/catch2/benchmark/detail/catch_stats.cpp
index 8e14bfd71ead287d73065f31031826db1095fcdd..45190da86e15194545a111926fea5a570174d567 100644
--- a/packages/Catch2/src/catch2/benchmark/detail/catch_stats.cpp
+++ b/packages/Catch2/src/catch2/benchmark/detail/catch_stats.cpp
@@ -171,7 +171,7 @@ namespace Catch {
                 double sb = stddev.point;
                 double mn = mean.point / n;
                 double mg_min = mn / 2.;
-                double sg = std::min(mg_min / 4., sb / std::sqrt(n));
+                double sg = (std::min)(mg_min / 4., sb / std::sqrt(n));
                 double sg2 = sg * sg;
                 double sb2 = sb * sb;
 
@@ -190,7 +190,7 @@ namespace Catch {
                     return (nc / n) * (sb2 - nc * sg2);
                 };
 
-                return std::min(var_out(1), var_out(std::min(c_max(0.), c_max(mg_min)))) / sb2;
+                return (std::min)(var_out(1), var_out((std::min)(c_max(0.), c_max(mg_min)))) / sb2;
             }
 
 
diff --git a/packages/Catch2/src/catch2/benchmark/detail/catch_stats.hpp b/packages/Catch2/src/catch2/benchmark/detail/catch_stats.hpp
index b8044131acf40186aa0fe43057e1b9908cd7a94a..8985670f0892a9eb7c5558b4dc24d30cb5d988b9 100644
--- a/packages/Catch2/src/catch2/benchmark/detail/catch_stats.hpp
+++ b/packages/Catch2/src/catch2/benchmark/detail/catch_stats.hpp
@@ -18,7 +18,6 @@
 #include <numeric>
 #include <tuple>
 #include <cmath>
-#include <utility>
 
 namespace Catch {
     namespace Benchmark {
@@ -116,8 +115,8 @@ namespace Catch {
                 double b2 = bias - z1;
                 double a1 = a(b1);
                 double a2 = a(b2);
-                auto lo = std::max(cumn(a1), 0);
-                auto hi = std::min(cumn(a2), n - 1);
+                auto lo = (std::max)(cumn(a1), 0);
+                auto hi = (std::min)(cumn(a2), n - 1);
 
                 return { point, resample[lo], resample[hi], confidence_level };
             }
diff --git a/packages/Catch2/src/catch2/benchmark/internal/catch_benchmark_combined_tu.cpp b/packages/Catch2/src/catch2/benchmark/internal/catch_benchmark_combined_tu.cpp
index c7234966fc23710250709140a109c2fe4b6d4966..ffbae6962821927d6813e8ac6e4d57498da13942 100644
--- a/packages/Catch2/src/catch2/benchmark/internal/catch_benchmark_combined_tu.cpp
+++ b/packages/Catch2/src/catch2/benchmark/internal/catch_benchmark_combined_tu.cpp
@@ -47,26 +47,6 @@ namespace Catch {
 } // namespace Catch
 
 
-////////////////////////////////////////////////
-// vvv formerly catch_complete_invoke.cpp vvv //
-////////////////////////////////////////////////
-
-#include <catch2/benchmark/detail/catch_complete_invoke.hpp>
-
-namespace Catch {
-    namespace Benchmark {
-        namespace Detail {
-            CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
-            CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
-            const std::string benchmarkErrorMsg = "a benchmark failed to run successfully";
-            CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
-        } // namespace Detail
-    } // namespace Benchmark
-} // namespace Catch
-
-
-
-
 /////////////////////////////////////////////////
 // vvv formerly catch_run_for_at_least.cpp vvv //
 /////////////////////////////////////////////////
diff --git a/packages/Catch2/src/catch2/catch_all.hpp b/packages/Catch2/src/catch2/catch_all.hpp
index 3bd6d9c85e16d134703c3775f549c7cede6db314..35bd9741ac947f63bc0ff5e4cd616bc69a37deb2 100644
--- a/packages/Catch2/src/catch2/catch_all.hpp
+++ b/packages/Catch2/src/catch2/catch_all.hpp
@@ -66,11 +66,13 @@
 #include <catch2/internal/catch_errno_guard.hpp>
 #include <catch2/internal/catch_exception_translator_registry.hpp>
 #include <catch2/internal/catch_fatal_condition_handler.hpp>
+#include <catch2/internal/catch_floating_point_helpers.hpp>
 #include <catch2/internal/catch_lazy_expr.hpp>
 #include <catch2/internal/catch_leak_detector.hpp>
 #include <catch2/internal/catch_list.hpp>
 #include <catch2/internal/catch_message_info.hpp>
 #include <catch2/internal/catch_meta.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 #include <catch2/internal/catch_noncopyable.hpp>
 #include <catch2/internal/catch_option.hpp>
 #include <catch2/internal/catch_output_redirect.hpp>
@@ -100,6 +102,7 @@
 #include <catch2/internal/catch_textflow.hpp>
 #include <catch2/internal/catch_to_string.hpp>
 #include <catch2/internal/catch_uncaught_exceptions.hpp>
+#include <catch2/internal/catch_unique_name.hpp>
 #include <catch2/internal/catch_unique_ptr.hpp>
 #include <catch2/internal/catch_wildcard_pattern.hpp>
 #include <catch2/internal/catch_windows_h_proxy.hpp>
diff --git a/packages/Catch2/src/catch2/catch_approx.hpp b/packages/Catch2/src/catch2/catch_approx.hpp
index 7483349477913e77663a96a97b1042408acf7383..7c3a17c031d88e8ee90adc99d77b52b01c47a4dd 100644
--- a/packages/Catch2/src/catch2/catch_approx.hpp
+++ b/packages/Catch2/src/catch2/catch_approx.hpp
@@ -30,7 +30,7 @@ namespace Catch {
         Approx operator-() const;
 
         template <typename T, typename = std::enable_if_t<std::is_constructible<double, T>::value>>
-        Approx operator()( T const& value ) {
+        Approx operator()( T const& value ) const {
             Approx approx( static_cast<double>(value) );
             approx.m_epsilon = m_epsilon;
             approx.m_margin = m_margin;
@@ -114,8 +114,8 @@ namespace Catch {
     };
 
 namespace literals {
-    Approx operator "" _a(long double val);
-    Approx operator "" _a(unsigned long long val);
+    Approx operator ""_a(long double val);
+    Approx operator ""_a(unsigned long long val);
 } // end namespace literals
 
 template<>
diff --git a/packages/Catch2/src/catch2/catch_assertion_result.cpp b/packages/Catch2/src/catch2/catch_assertion_result.cpp
index 4e1609153672edb0f5b1d0e617cb00681c2abc88..04e00d265c7c00a5db6dd580c7318ba315623534 100644
--- a/packages/Catch2/src/catch2/catch_assertion_result.cpp
+++ b/packages/Catch2/src/catch2/catch_assertion_result.cpp
@@ -91,7 +91,7 @@ namespace Catch {
                 : expr;
     }
 
-    std::string AssertionResult::getMessage() const {
+    StringRef AssertionResult::getMessage() const {
         return m_resultData.message;
     }
     SourceLineInfo AssertionResult::getSourceInfo() const {
diff --git a/packages/Catch2/src/catch2/catch_assertion_result.hpp b/packages/Catch2/src/catch2/catch_assertion_result.hpp
index 0afb0e02e2eb4daf1b90a270b39e824561b44c08..3008664c212b3e0cec06a8be23041d4ad7b624dc 100644
--- a/packages/Catch2/src/catch2/catch_assertion_result.hpp
+++ b/packages/Catch2/src/catch2/catch_assertion_result.hpp
@@ -46,7 +46,7 @@ namespace Catch {
         std::string getExpressionInMacro() const;
         bool hasExpandedExpression() const;
         std::string getExpandedExpression() const;
-        std::string getMessage() const;
+        StringRef getMessage() const;
         SourceLineInfo getSourceInfo() const;
         StringRef getTestMacroName() const;
 
diff --git a/packages/Catch2/src/catch2/catch_config.cpp b/packages/Catch2/src/catch2/catch_config.cpp
index 7a4a0bdaed38628e8ecca2281e87b455d723244c..92fcff2063cd040e6cdca0198b7bebe122573cb7 100644
--- a/packages/Catch2/src/catch2/catch_config.cpp
+++ b/packages/Catch2/src/catch2/catch_config.cpp
@@ -18,7 +18,7 @@ namespace Catch {
 
     Config::Config( ConfigData const& data )
     :   m_data( data ),
-        m_stream( openStream() )
+        m_stream( Catch::makeStream(m_data.outputFilename) )
     {
         // We need to trim filter specs to avoid trouble with superfluous
         // whitespace (esp. important for bdd macros, as those are manually
@@ -52,7 +52,6 @@ namespace Catch {
     bool Config::listTags() const           { return m_data.listTags; }
     bool Config::listReporters() const      { return m_data.listReporters; }
 
-    std::string Config::getProcessName() const { return m_data.processName; }
     std::string const& Config::getReporterName() const { return m_data.reporterName; }
 
     std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
@@ -66,7 +65,7 @@ namespace Catch {
     // IConfig interface
     bool Config::allowThrows() const                   { return !m_data.noThrow; }
     std::ostream& Config::stream() const               { return m_stream->stream(); }
-    std::string Config::name() const                   { return m_data.name.empty() ? m_data.processName : m_data.name; }
+    StringRef Config::name() const                     { return m_data.name.empty() ? m_data.processName : m_data.name; }
     bool Config::includeSuccessfulResults() const      { return m_data.showSuccessfulTests; }
     bool Config::warnAboutMissingAssertions() const    { return !!(m_data.warnings & WarnAbout::NoAssertions); }
     bool Config::warnAboutNoTests() const              { return !!(m_data.warnings & WarnAbout::NoTests); }
@@ -86,8 +85,4 @@ namespace Catch {
     unsigned int Config::benchmarkResamples() const               { return m_data.benchmarkResamples; }
     std::chrono::milliseconds Config::benchmarkWarmupTime() const { return std::chrono::milliseconds(m_data.benchmarkWarmupTime); }
 
-    IStream const* Config::openStream() {
-        return Catch::makeStream(m_data.outputFilename);
-    }
-
 } // end namespace Catch
diff --git a/packages/Catch2/src/catch2/catch_config.hpp b/packages/Catch2/src/catch2/catch_config.hpp
index 10d0487a84202388bff1ca262f6b4aa2526714a6..7daf61c713071d59aebba6b5444e642268cfc244 100644
--- a/packages/Catch2/src/catch2/catch_config.hpp
+++ b/packages/Catch2/src/catch2/catch_config.hpp
@@ -76,7 +76,6 @@ namespace Catch {
         bool listTags() const;
         bool listReporters() const;
 
-        std::string getProcessName() const;
         std::string const& getReporterName() const;
 
         std::vector<std::string> const& getTestsOrTags() const override;
@@ -90,7 +89,7 @@ namespace Catch {
         // IConfig interface
         bool allowThrows() const override;
         std::ostream& stream() const override;
-        std::string name() const override;
+        StringRef name() const override;
         bool includeSuccessfulResults() const override;
         bool warnAboutMissingAssertions() const override;
         bool warnAboutNoTests() const override;
@@ -110,8 +109,6 @@ namespace Catch {
         std::chrono::milliseconds benchmarkWarmupTime() const override;
 
     private:
-
-        IStream const* openStream();
         ConfigData m_data;
 
         Detail::unique_ptr<IStream const> m_stream;
diff --git a/packages/Catch2/src/catch2/catch_message.cpp b/packages/Catch2/src/catch2/catch_message.cpp
index c140c08e1bc6168d7fad0aeebcc522d8dda6df60..2ad4aea37ba0ad165c778fb3714306474d63ab59 100644
--- a/packages/Catch2/src/catch2/catch_message.cpp
+++ b/packages/Catch2/src/catch2/catch_message.cpp
@@ -9,6 +9,7 @@
 #include <catch2/interfaces/catch_interfaces_capture.hpp>
 #include <catch2/internal/catch_uncaught_exceptions.hpp>
 #include <catch2/internal/catch_enforce.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <cassert>
 #include <stack>
@@ -17,13 +18,6 @@ namespace Catch {
 
     ////////////////////////////////////////////////////////////////////////////
 
-    Catch::MessageBuilder::MessageBuilder( StringRef const& macroName,
-                                           SourceLineInfo const& lineInfo,
-                                           ResultWas::OfType type )
-        :m_info(macroName, lineInfo, type) {}
-
-    ////////////////////////////////////////////////////////////////////////////
-
 
     ScopedMessage::ScopedMessage( MessageBuilder const& builder ):
         m_info( builder.m_info ) {
@@ -32,7 +26,7 @@ namespace Catch {
     }
 
     ScopedMessage::ScopedMessage( ScopedMessage&& old ) noexcept:
-        m_info( std::move( old.m_info ) ) {
+        m_info( CATCH_MOVE( old.m_info ) ) {
         old.m_moved = true;
     }
 
diff --git a/packages/Catch2/src/catch2/catch_message.hpp b/packages/Catch2/src/catch2/catch_message.hpp
index db60b6bf8f385fd9aa2b4ef63e4bbafdc9bc8839..970ba438a4c73c47603227cbddbead9cb594a355 100644
--- a/packages/Catch2/src/catch2/catch_message.hpp
+++ b/packages/Catch2/src/catch2/catch_message.hpp
@@ -34,9 +34,11 @@ namespace Catch {
     };
 
     struct MessageBuilder : MessageStream {
-        MessageBuilder( StringRef const& macroName,
+        MessageBuilder( StringRef macroName,
                         SourceLineInfo const& lineInfo,
-                        ResultWas::OfType type );
+                        ResultWas::OfType type ):
+            m_info(macroName, lineInfo, type) {}
+
 
         template<typename T>
         MessageBuilder& operator << ( T const& value ) {
diff --git a/packages/Catch2/src/catch2/catch_registry_hub.cpp b/packages/Catch2/src/catch2/catch_registry_hub.cpp
index e3a222dfccd4b1e39cda717e4e79e671985a857b..edf9b2b52ec65d5ade457f330f560350ea131d37 100644
--- a/packages/Catch2/src/catch2/catch_registry_hub.cpp
+++ b/packages/Catch2/src/catch2/catch_registry_hub.cpp
@@ -19,6 +19,7 @@
 #include <catch2/catch_test_case_info.hpp>
 #include <catch2/internal/catch_noncopyable.hpp>
 #include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
 
@@ -48,16 +49,16 @@ namespace Catch {
 
         public: // IMutableRegistryHub
             void registerReporter( std::string const& name, IReporterFactoryPtr factory ) override {
-                m_reporterRegistry.registerReporter( name, std::move(factory) );
+                m_reporterRegistry.registerReporter( name, CATCH_MOVE(factory) );
             }
             void registerListener( IReporterFactoryPtr factory ) override {
-                m_reporterRegistry.registerListener( std::move(factory) );
+                m_reporterRegistry.registerListener( CATCH_MOVE(factory) );
             }
             void registerTest( Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker ) override {
-                m_testCaseRegistry.registerTest( std::move(testInfo), std::move(invoker) );
+                m_testCaseRegistry.registerTest( CATCH_MOVE(testInfo), CATCH_MOVE(invoker) );
             }
-            void registerTranslator( const IExceptionTranslator* translator ) override {
-                m_exceptionTranslatorRegistry.registerTranslator( translator );
+            void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) override {
+                m_exceptionTranslatorRegistry.registerTranslator( CATCH_MOVE(translator) );
             }
             void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override {
                 m_tagAliasRegistry.add( alias, tag, lineInfo );
diff --git a/packages/Catch2/src/catch2/catch_reporter_registrars.hpp b/packages/Catch2/src/catch2/catch_reporter_registrars.hpp
index e7375aa7b67bdb30326cd135722e425aee220f40..978d2925e24e34004281b2905286ab532b570a18 100644
--- a/packages/Catch2/src/catch2/catch_reporter_registrars.hpp
+++ b/packages/Catch2/src/catch2/catch_reporter_registrars.hpp
@@ -9,11 +9,15 @@
 #define CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
 
 #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
+#include <catch2/interfaces/catch_interfaces_reporter.hpp>
 #include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
 #include <catch2/internal/catch_unique_ptr.hpp>
 
 namespace Catch {
 
+    struct IStreamingReporter;
+    using IStreamingReporterPtr = Detail::unique_ptr<IStreamingReporter>;
+
     template <typename T>
     class ReporterFactory : public IReporterFactory {
 
diff --git a/packages/Catch2/src/catch2/catch_section_info.hpp b/packages/Catch2/src/catch2/catch_section_info.hpp
index df0732ec35400228e6bd2298a9f4b9895161afc3..1d206891d0d293dc9ec9b7c3de8851d0faa8859b 100644
--- a/packages/Catch2/src/catch2/catch_section_info.hpp
+++ b/packages/Catch2/src/catch2/catch_section_info.hpp
@@ -8,6 +8,7 @@
 #ifndef CATCH_SECTION_INFO_HPP_INCLUDED
 #define CATCH_SECTION_INFO_HPP_INCLUDED
 
+#include <catch2/internal/catch_move_and_forward.hpp>
 #include <catch2/internal/catch_source_line_info.hpp>
 #include <catch2/internal/catch_stringref.hpp>
 #include <catch2/catch_totals.hpp>
@@ -22,7 +23,7 @@ namespace Catch {
         // still use the `-c` flag comfortably.
         SectionInfo( SourceLineInfo const& _lineInfo, std::string _name,
                     const char* const = nullptr ):
-            name(std::move(_name)),
+            name(CATCH_MOVE(_name)),
             lineInfo(_lineInfo)
             {}
 
diff --git a/packages/Catch2/src/catch2/catch_session.cpp b/packages/Catch2/src/catch2/catch_session.cpp
index 5b1a50a9298e7a7ac871866f5e9f6fc4563ceb45..9738cbb4f994c910031dc99a47f9ec53c6fd1a1e 100644
--- a/packages/Catch2/src/catch2/catch_session.cpp
+++ b/packages/Catch2/src/catch2/catch_session.cpp
@@ -21,6 +21,7 @@
 #include <catch2/reporters/catch_reporter_listening.hpp>
 #include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
 #include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <algorithm>
 #include <iomanip>
@@ -43,19 +44,13 @@ namespace Catch {
                 return createReporter(config->getReporterName(), config);
             }
 
-            // On older platforms, returning unique_ptr<ListeningReporter>
-            // when the return type is unique_ptr<IStreamingReporter>
-            // doesn't compile without a std::move call. However, this causes
-            // a warning on newer platforms. Thus, we have to work around
-            // it a bit and downcast the pointer manually.
-            auto ret = Detail::unique_ptr<IStreamingReporter>(new ListeningReporter(config));
-            auto& multi = static_cast<ListeningReporter&>(*ret);
+            auto multi = Detail::make_unique<ListeningReporter>(config);
             auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
             for (auto const& listener : listeners) {
-                multi.addListener(listener->create(Catch::ReporterConfig(config)));
+                multi->addListener(listener->create(Catch::ReporterConfig(config)));
             }
-            multi.addReporter(createReporter(config->getReporterName(), config));
-            return ret;
+            multi->addReporter(createReporter(config->getReporterName(), config));
+            return multi;
         }
 
         class TestGroup {
@@ -63,7 +58,7 @@ namespace Catch {
             explicit TestGroup(IStreamingReporterPtr&& reporter, Config const* config):
                 m_reporter(reporter.get()),
                 m_config{config},
-                m_context{config, std::move(reporter)} {
+                m_context{config, CATCH_MOVE(reporter)} {
 
                 auto const& allTestCases = getAllTestCasesSorted(*m_config);
                 m_matches = m_config->testSpec().matchesByFilter(allTestCases, *m_config);
@@ -82,7 +77,6 @@ namespace Catch {
             Totals execute() {
                 auto const& invalidArgs = m_config->testSpec().getInvalidArgs();
                 Totals totals;
-                m_context.testGroupStarting(m_config->name(), 1, 1);
                 for (auto const& testCase : m_tests) {
                     if (!m_context.aborting())
                         totals += m_context.runTest(*testCase);
@@ -102,7 +96,6 @@ namespace Catch {
                          m_reporter->reportInvalidArguments(invalidArg);
                 }
 
-                m_context.testGroupEnded(m_config->name(), totals, 1, 1);
                 return totals;
             }
 
@@ -161,16 +154,16 @@ namespace Catch {
 
     void Session::showHelp() const {
         Catch::cout()
-                << "\nCatch v" << libraryVersion() << "\n"
-                << m_cli << std::endl
-                << "For more detailed usage please see the project docs\n" << std::endl;
+                << "\nCatch v" << libraryVersion() << '\n'
+                << m_cli << '\n'
+                << "For more detailed usage please see the project docs\n\n" << std::flush;
     }
     void Session::libIdentify() {
         Catch::cout()
                 << std::left << std::setw(16) << "description: " << "A Catch2 test executable\n"
                 << std::left << std::setw(16) << "category: " << "testframework\n"
                 << std::left << std::setw(16) << "framework: " << "Catch Test\n"
-                << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
+                << std::left << std::setw(16) << "version: " << libraryVersion() << '\n' << std::flush;
     }
 
     int Session::applyCommandLine( int argc, char const * const * argv ) {
@@ -186,7 +179,7 @@ namespace Catch {
                 << "\nError(s) in input:\n"
                 << TextFlow::Column( result.errorMessage() ).indent( 2 )
                 << "\n\n";
-            Catch::cerr() << "Run with -? for usage\n" << std::endl;
+            Catch::cerr() << "Run with -? for usage\n\n" << std::flush;
             return MaxExitCode;
         }
 
@@ -229,12 +222,12 @@ namespace Catch {
 
     int Session::run() {
         if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
-            Catch::cout() << "...waiting for enter/ return before starting" << std::endl;
+            Catch::cout() << "...waiting for enter/ return before starting\n" << std::flush;
             static_cast<void>(std::getchar());
         }
         int exitCode = runInternal();
         if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
-            Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl;
+            Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << '\n' << std::flush;
             static_cast<void>(std::getchar());
         }
         return exitCode;
@@ -283,7 +276,7 @@ namespace Catch {
                 return 0;
             }
 
-            TestGroup tests { std::move(reporter), m_config.get() };
+            TestGroup tests { CATCH_MOVE(reporter), m_config.get() };
             auto const totals = tests.execute();
 
             if( m_config->warnAboutNoTests() && totals.error == -1 )
@@ -296,7 +289,7 @@ namespace Catch {
         }
 #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
         catch( std::exception& ex ) {
-            Catch::cerr() << ex.what() << std::endl;
+            Catch::cerr() << ex.what() << '\n' << std::flush;
             return MaxExitCode;
         }
 #endif
diff --git a/packages/Catch2/src/catch2/catch_tag_alias_autoregistrar.hpp b/packages/Catch2/src/catch2/catch_tag_alias_autoregistrar.hpp
index 6b5c2f1c8ad315eaebf314454accf3b794b4106c..c742bcd3bc0f8f3d06b6fc6b60cae446e7403cad 100644
--- a/packages/Catch2/src/catch2/catch_tag_alias_autoregistrar.hpp
+++ b/packages/Catch2/src/catch2/catch_tag_alias_autoregistrar.hpp
@@ -9,6 +9,7 @@
 #define CATCH_TAG_ALIAS_AUTOREGISTRAR_HPP_INCLUDED
 
 #include <catch2/internal/catch_compiler_capabilities.hpp>
+#include <catch2/internal/catch_unique_name.hpp>
 #include <catch2/internal/catch_source_line_info.hpp>
 
 namespace Catch {
diff --git a/packages/Catch2/src/catch2/catch_test_case_info.cpp b/packages/Catch2/src/catch2/catch_test_case_info.cpp
index e8029e9c23a29040144a9d09d02c70bb9167cfe7..7dab6ae48146f833bc618f48addf7a9070520e48 100644
--- a/packages/Catch2/src/catch2/catch_test_case_info.cpp
+++ b/packages/Catch2/src/catch2/catch_test_case_info.cpp
@@ -101,13 +101,17 @@ namespace Catch {
             const size_t extras = 3 + 3;
             return extractFilenamePart(filepath).size() + extras;
         }
+    } // end unnamed namespace
+
+    bool operator<( Tag const& lhs, Tag const& rhs ) {
+        return lhs.original < rhs.original;
     }
 
     Detail::unique_ptr<TestCaseInfo>
         makeTestCaseInfo(std::string const& _className,
                          NameAndTags const& nameAndTags,
                          SourceLineInfo const& _lineInfo ) {
-        return Detail::unique_ptr<TestCaseInfo>(new TestCaseInfo(_className, nameAndTags, _lineInfo));
+        return Detail::make_unique<TestCaseInfo>(_className, nameAndTags, _lineInfo);
     }
 
     TestCaseInfo::TestCaseInfo(std::string const& _className,
@@ -146,6 +150,7 @@ namespace Catch {
                 // it over to backing storage and actually reference the
                 // backing storage in the saved tags
                 StringRef tagStr = originalTags.substr(tagStart+1, tagEnd - tagStart - 1);
+                CATCH_ENFORCE(!tagStr.empty(), "Empty tags are not allowed");
                 enforceNotReservedTag(tagStr, lineInfo);
                 properties |= parseSpecialTag(tagStr);
                 // When copying a tag to the backing storage, we need to
@@ -227,15 +232,19 @@ namespace Catch {
                           StringRef(backingLCaseTags.c_str() + backingStart, backingEnd - backingStart));
     }
 
-
-    bool TestCaseHandle::operator == ( TestCaseHandle const& rhs ) const {
-        return m_invoker == rhs.m_invoker
-            && m_info->name == rhs.m_info->name
-            && m_info->className == rhs.m_info->className;
-    }
-
-    bool TestCaseHandle::operator < ( TestCaseHandle const& rhs ) const {
-        return m_info->name < rhs.m_info->name;
+    bool operator<( TestCaseInfo const& lhs, TestCaseInfo const& rhs ) {
+        // We want to avoid redoing the string comparisons multiple times,
+        // so we store the result of a three-way comparison before using
+        // it in the actual comparison logic.
+        const auto cmpName = lhs.name.compare( rhs.name );
+        if ( cmpName != 0 ) {
+            return cmpName < 0;
+        }
+        const auto cmpClassName = lhs.className.compare( rhs.className );
+        if ( cmpClassName != 0 ) {
+            return cmpClassName < 0;
+        }
+        return lhs.tags < rhs.tags;
     }
 
     TestCaseInfo const& TestCaseHandle::getTestCaseInfo() const {
diff --git a/packages/Catch2/src/catch2/catch_test_case_info.hpp b/packages/Catch2/src/catch2/catch_test_case_info.hpp
index 1383f11b9efc732cf4457541273f53fb07818567..8048bd3a3027ee548d59ade9eeafe803277fe820 100644
--- a/packages/Catch2/src/catch2/catch_test_case_info.hpp
+++ b/packages/Catch2/src/catch2/catch_test_case_info.hpp
@@ -30,6 +30,8 @@ namespace Catch {
             original(original_), lowerCased(lowerCased_)
         {}
         StringRef original, lowerCased;
+
+        friend bool operator<( Tag const& lhs, Tag const& rhs );
     };
 
     struct ITestInvoker;
@@ -44,7 +46,15 @@ namespace Catch {
         Benchmark = 1 << 6
     };
 
-
+    /**
+     * Various metadata about the test case.
+     *
+     * A test case is uniquely identified by its (class)name and tags
+     * combination, with source location being ignored, and other properties
+     * being determined from tags.
+     *
+     * Tags are kept sorted.
+     */
     struct TestCaseInfo : Detail::NonCopyable {
 
         TestCaseInfo(std::string const& _className,
@@ -59,6 +69,10 @@ namespace Catch {
         // Adds the tag(s) with test's filename (for the -# flag)
         void addFilenameTag();
 
+        //! Orders by name, classname and tags
+        friend bool operator<( TestCaseInfo const& lhs,
+                               TestCaseInfo const& rhs );
+
 
         std::string tagsAsString() const;
 
@@ -75,6 +89,12 @@ namespace Catch {
         TestCaseProperties properties = TestCaseProperties::None;
     };
 
+    /**
+     * Wrapper over the test case information and the test case invoker
+     *
+     * Does not own either, and is specifically made to be cheap
+     * to copy around.
+     */
     class TestCaseHandle {
         TestCaseInfo* m_info;
         ITestInvoker* m_invoker;
@@ -87,9 +107,6 @@ namespace Catch {
         }
 
         TestCaseInfo const& getTestCaseInfo() const;
-
-        bool operator== ( TestCaseHandle const& rhs ) const;
-        bool operator < ( TestCaseHandle const& rhs ) const;
     };
 
     Detail::unique_ptr<TestCaseInfo> makeTestCaseInfo(  std::string const& className,
diff --git a/packages/Catch2/src/catch2/catch_test_macros.hpp b/packages/Catch2/src/catch2/catch_test_macros.hpp
index dcbe53db9ab8b947a9a76da0074ed91a2876b1fe..918fd4bfff071e8e4792ea8d515b16ae43bddd18 100644
--- a/packages/Catch2/src/catch2/catch_test_macros.hpp
+++ b/packages/Catch2/src/catch2/catch_test_macros.hpp
@@ -13,6 +13,8 @@
 #include <catch2/internal/catch_preprocessor.hpp>
 #include <catch2/internal/catch_section.hpp>
 #include <catch2/internal/catch_test_registry.hpp>
+#include <catch2/internal/catch_unique_name.hpp>
+
 
 // All of our user-facing macros support configuration toggle, that
 // forces them to be defined prefixed with CATCH_. We also like to
@@ -30,8 +32,8 @@
 
   #define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
   #define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
-  #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-  #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+  #define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+  #define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
   #define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
 
   #define CATCH_CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
@@ -87,8 +89,8 @@
   #define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
   #define CATCH_CHECK_NOTHROW( ... ) (void)(0)
 
-  #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-  #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+  #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
+  #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
   #define CATCH_METHOD_AS_TEST_CASE( method, ... )
   #define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
   #define CATCH_SECTION( ... )
@@ -101,8 +103,8 @@
   #define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
 
   // "BDD-style" convenience wrappers
-  #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-  #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
+  #define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
+  #define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className )
   #define CATCH_GIVEN( desc )
   #define CATCH_AND_GIVEN( desc )
   #define CATCH_WHEN( desc )
@@ -121,8 +123,8 @@
 
   #define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
   #define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
-  #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-  #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+  #define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
+  #define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
   #define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
 
   #define CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
@@ -177,8 +179,8 @@
   #define CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
   #define CHECK_NOTHROW( ... ) (void)(0)
 
-  #define TEST_CASE( ... )  INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__)
-  #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
+  #define TEST_CASE( ... )  INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__)
+  #define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ))
   #define METHOD_AS_TEST_CASE( method, ... )
   #define REGISTER_TEST_CASE( Function, ... ) (void)(0)
   #define SECTION( ... )
@@ -191,8 +193,8 @@
   #define STATIC_REQUIRE_FALSE( ... ) (void)(0)
 
   // "BDD-style" convenience wrappers
-  #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) )
-  #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
+  #define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ) )
+  #define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), className )
 
   #define GIVEN( desc )
   #define AND_GIVEN( desc )
diff --git a/packages/Catch2/src/catch2/catch_tostring.cpp b/packages/Catch2/src/catch2/catch_tostring.cpp
index 8b0ffe01b577bbf83f8daf6a99b084c26d6320eb..4c24234eef9f4222031193457a7722b51c75bc83 100644
--- a/packages/Catch2/src/catch2/catch_tostring.cpp
+++ b/packages/Catch2/src/catch2/catch_tostring.cpp
@@ -5,12 +5,6 @@
 //        https://www.boost.org/LICENSE_1_0.txt)
 
 // SPDX-License-Identifier: BSL-1.0
-#if defined(__clang__)
-#    pragma clang diagnostic push
-#    pragma clang diagnostic ignored "-Wexit-time-destructors"
-#    pragma clang diagnostic ignored "-Wglobal-constructors"
-#endif
-
 
 #include <catch2/catch_tostring.hpp>
 #include <catch2/interfaces/catch_interfaces_config.hpp>
@@ -24,8 +18,6 @@ namespace Catch {
 
 namespace Detail {
 
-    const std::string unprintableString = "{?}";
-
     namespace {
         const int hexThreshold = 255;
 
@@ -62,6 +54,48 @@ namespace Detail {
         }
     } // end unnamed namespace
 
+    std::string convertIntoString(StringRef string, bool escape_invisibles) {
+        std::string ret;
+        // This is enough for the "don't escape invisibles" case, and a good
+        // lower bound on the "escape invisibles" case.
+        ret.reserve(string.size() + 2);
+
+        if (!escape_invisibles) {
+            ret += '"';
+            ret += string;
+            ret += '"';
+            return ret;
+        }
+
+        ret += '"';
+        for (char c : string) {
+            switch (c) {
+            case '\r':
+                ret.append("\\r");
+                break;
+            case '\n':
+                ret.append("\\n");
+                break;
+            case '\t':
+                ret.append("\\t");
+                break;
+            case '\f':
+                ret.append("\\f");
+                break;
+            default:
+                ret.push_back(c);
+                break;
+            }
+        }
+        ret += '"';
+
+        return ret;
+    }
+
+    std::string convertIntoString(StringRef string) {
+        return convertIntoString(string, getCurrentContext().getConfig()->showInvisibles());
+    }
+
     std::string rawMemoryToString( const void *object, std::size_t size ) {
         // Reverse order for little endian architectures
         int i = 0, end = static_cast<int>( size ), inc = 1;
@@ -88,44 +122,25 @@ namespace Detail {
 //// ======================================================= ////
 
 std::string StringMaker<std::string>::convert(const std::string& str) {
-    if (!getCurrentContext().getConfig()->showInvisibles()) {
-        return '"' + str + '"';
-    }
-
-    std::string s("\"");
-    for (char c : str) {
-        switch (c) {
-        case '\n':
-            s.append("\\n");
-            break;
-        case '\t':
-            s.append("\\t");
-            break;
-        default:
-            s.push_back(c);
-            break;
-        }
-    }
-    s.append("\"");
-    return s;
+    return Detail::convertIntoString( str );
 }
 
 #ifdef CATCH_CONFIG_CPP17_STRING_VIEW
 std::string StringMaker<std::string_view>::convert(std::string_view str) {
-    return ::Catch::Detail::stringify(std::string{ str });
+    return Detail::convertIntoString( StringRef( str.data(), str.size() ) );
 }
 #endif
 
 std::string StringMaker<char const*>::convert(char const* str) {
     if (str) {
-        return ::Catch::Detail::stringify(std::string{ str });
+        return Detail::convertIntoString( str );
     } else {
         return{ "{null string}" };
     }
 }
 std::string StringMaker<char*>::convert(char* str) {
     if (str) {
-        return ::Catch::Detail::stringify(std::string{ str });
+        return Detail::convertIntoString( str );
     } else {
         return{ "{null string}" };
     }
@@ -237,8 +252,3 @@ std::string StringMaker<double>::convert(double value) {
 }
 
 } // end namespace Catch
-
-#if defined(__clang__)
-#    pragma clang diagnostic pop
-#endif
-
diff --git a/packages/Catch2/src/catch2/catch_tostring.hpp b/packages/Catch2/src/catch2/catch_tostring.hpp
index d0f799a0279d6106e03fe611786af262e67e8f24..e82552bf82c1fecaad23de3406dde800023cadad 100644
--- a/packages/Catch2/src/catch2/catch_tostring.hpp
+++ b/packages/Catch2/src/catch2/catch_tostring.hpp
@@ -13,6 +13,7 @@
 #include <cstddef>
 #include <type_traits>
 #include <string>
+#include <string.h>
 #include <catch2/internal/catch_compiler_capabilities.hpp>
 #include <catch2/internal/catch_config_wchar.hpp>
 #include <catch2/internal/catch_stream.hpp>
@@ -27,10 +28,26 @@
 #pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
 #endif
 
+// We need a dummy global operator<< so we can bring it into Catch namespace later
+struct Catch_global_namespace_dummy{};
+std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
+
 namespace Catch {
+    // Bring in global namespace operator<< for ADL lookup in
+    // `IsStreamInsertable` below.
+    using ::operator<<;
+
     namespace Detail {
 
-        extern const std::string unprintableString;
+
+        constexpr StringRef unprintableString = "{?}"_sr;
+
+        //! Encases `string in quotes, and optionally escapes invisibles
+        std::string convertIntoString( StringRef string, bool escapeInvisibles );
+
+        //! Encases `string` in quotes, and escapes invisibles if user requested
+        //! it via CLI
+        std::string convertIntoString( StringRef string );
 
         std::string rawMemoryToString( const void *object, std::size_t size );
 
@@ -59,7 +76,7 @@ namespace Catch {
         std::enable_if_t<
             !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
         std::string> convertUnstreamable( T const& ) {
-            return Detail::unprintableString;
+            return std::string(Detail::unprintableString);
         }
         template<typename T>
         std::enable_if_t<
@@ -186,24 +203,31 @@ namespace Catch {
     };
 #endif // CATCH_CONFIG_WCHAR
 
-    // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
-    //      while keeping string semantics?
     template<int SZ>
     struct StringMaker<char[SZ]> {
         static std::string convert(char const* str) {
-            return ::Catch::Detail::stringify(std::string{ str });
+            // Note that `strnlen` is not actually part of standard C++,
+            // but both POSIX and Windows cstdlib provide it.
+            return Detail::convertIntoString(
+                StringRef( str, strnlen( str, SZ ) ) );
         }
     };
     template<int SZ>
     struct StringMaker<signed char[SZ]> {
         static std::string convert(signed char const* str) {
-            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
+            // See the plain `char const*` overload
+            auto reinterpreted = reinterpret_cast<char const*>(str);
+            return Detail::convertIntoString(
+                StringRef(reinterpreted, strnlen(reinterpreted, SZ)));
         }
     };
     template<int SZ>
     struct StringMaker<unsigned char[SZ]> {
         static std::string convert(unsigned char const* str) {
-            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
+            // See the plain `char const*` overload
+            auto reinterpreted = reinterpret_cast<char const*>(str);
+            return Detail::convertIntoString(
+                StringRef(reinterpreted, strnlen(reinterpreted, SZ)));
         }
     };
 
@@ -451,24 +475,24 @@ namespace Catch {
     using std::begin;
     using std::end;
 
-    namespace detail {
+    namespace Detail {
         template <typename...>
         struct void_type {
             using type = void;
         };
 
+        template <typename... Ts>
+        using void_type_t = typename void_type<Ts...>::type;
+
         template <typename T, typename = void>
-        struct is_range_impl : std::false_type {
-        };
+        struct is_range_impl : std::false_type {};
 
         template <typename T>
-        struct is_range_impl<T, typename void_type<decltype(begin(std::declval<T>()))>::type> : std::true_type {
-        };
-    } // namespace detail
+        struct is_range_impl<T, void_type_t<decltype(begin(std::declval<T>()))>> : std::true_type {};
+    } // namespace Detail
 
     template <typename T>
-    struct is_range : detail::is_range_impl<T> {
-    };
+    struct is_range : Detail::is_range_impl<T> {};
 
 #if defined(_MANAGED) // Managed types are never ranges
     template <typename T>
@@ -625,7 +649,7 @@ struct ratio_string<std::milli> {
 #else
             std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
 #endif
-            return std::string(timeStamp);
+            return std::string(timeStamp, timeStampSize - 1);
         }
     };
 }
diff --git a/packages/Catch2/src/catch2/catch_translate_exception.hpp b/packages/Catch2/src/catch2/catch_translate_exception.hpp
index 412a1062e4b21755df39acf6b8c7c96c2cd2b27b..dfd95cd69619ef2cf9064201f944bb24aeb8b316 100644
--- a/packages/Catch2/src/catch2/catch_translate_exception.hpp
+++ b/packages/Catch2/src/catch2/catch_translate_exception.hpp
@@ -9,6 +9,8 @@
 #define CATCH_TRANSLATE_EXCEPTION_HPP_INCLUDED
 
 #include <catch2/interfaces/catch_interfaces_exception.hpp>
+#include <catch2/internal/catch_compiler_capabilities.hpp>
+#include <catch2/internal/catch_unique_name.hpp>
 
 #include <exception>
 
@@ -46,8 +48,9 @@ namespace Catch {
     public:
         template<typename T>
         ExceptionTranslatorRegistrar( std::string(*translateFunction)( T const& ) ) {
-            getMutableRegistryHub().registerTranslator
-                ( new ExceptionTranslator<T>( translateFunction ) );
+            getMutableRegistryHub().registerTranslator(
+                Detail::make_unique<ExceptionTranslator<T>>(translateFunction)
+            );
         }
     };
 
diff --git a/packages/Catch2/src/catch2/generators/catch_generators.hpp b/packages/Catch2/src/catch2/generators/catch_generators.hpp
index 4ac73005160cef2c58581ebe085844333b441a68..9ffb9b76d7e763173b5b6f6fdef75734b53298d2 100644
--- a/packages/Catch2/src/catch2/generators/catch_generators.hpp
+++ b/packages/Catch2/src/catch2/generators/catch_generators.hpp
@@ -11,10 +11,10 @@
 #include <catch2/interfaces/catch_interfaces_generatortracker.hpp>
 #include <catch2/internal/catch_source_line_info.hpp>
 #include <catch2/internal/catch_stringref.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <vector>
 #include <tuple>
-#include <utility>
 
 namespace Catch {
 
@@ -55,7 +55,7 @@ namespace Detail {
         GeneratorWrapper(IGenerator<T>* generator):
             m_generator(generator) {}
         GeneratorWrapper(GeneratorPtr<T> generator):
-            m_generator(std::move(generator)) {}
+            m_generator(CATCH_MOVE(generator)) {}
 
         T const& get() const {
             return m_generator->get();
@@ -74,7 +74,7 @@ namespace Detail {
             m_value(value)
         {}
         SingleValueGenerator(T&& value):
-            m_value(std::move(value))
+            m_value(CATCH_MOVE(value))
         {}
 
         T const& get() const override {
@@ -108,7 +108,7 @@ namespace Detail {
     GeneratorWrapper<DecayedT> value( T&& value ) {
         return GeneratorWrapper<DecayedT>(
             Catch::Detail::make_unique<SingleValueGenerator<DecayedT>>(
-                std::forward<T>( value ) ) );
+                CATCH_FORWARD( value ) ) );
     }
     template <typename T>
     GeneratorWrapper<T> values(std::initializer_list<T> values) {
@@ -121,35 +121,35 @@ namespace Detail {
         size_t m_current = 0;
 
         void add_generator( GeneratorWrapper<T>&& generator ) {
-            m_generators.emplace_back( std::move( generator ) );
+            m_generators.emplace_back( CATCH_MOVE( generator ) );
         }
         void add_generator( T const& val ) {
             m_generators.emplace_back( value( val ) );
         }
         void add_generator( T&& val ) {
-            m_generators.emplace_back( value( std::move( val ) ) );
+            m_generators.emplace_back( value( CATCH_MOVE( val ) ) );
         }
         template <typename U>
         std::enable_if_t<!std::is_same<std::decay_t<U>, T>::value>
         add_generator( U&& val ) {
-            add_generator( T( std::forward<U>( val ) ) );
+            add_generator( T( CATCH_FORWARD( val ) ) );
         }
 
         template <typename U> void add_generators( U&& valueOrGenerator ) {
-            add_generator( std::forward<U>( valueOrGenerator ) );
+            add_generator( CATCH_FORWARD( valueOrGenerator ) );
         }
 
         template <typename U, typename... Gs>
         void add_generators( U&& valueOrGenerator, Gs&&... moreGenerators ) {
-            add_generator( std::forward<U>( valueOrGenerator ) );
-            add_generators( std::forward<Gs>( moreGenerators )... );
+            add_generator( CATCH_FORWARD( valueOrGenerator ) );
+            add_generators( CATCH_FORWARD( moreGenerators )... );
         }
 
     public:
         template <typename... Gs>
         Generators(Gs &&... moreGenerators) {
             m_generators.reserve(sizeof...(Gs));
-            add_generators(std::forward<Gs>(moreGenerators)...);
+            add_generators(CATCH_FORWARD(moreGenerators)...);
         }
 
         T const& get() const override {
@@ -181,19 +181,19 @@ namespace Detail {
 
     template<typename T, typename... Gs>
     auto makeGenerators( GeneratorWrapper<T>&& generator, Gs &&... moreGenerators ) -> Generators<T> {
-        return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
+        return Generators<T>(CATCH_MOVE(generator), CATCH_FORWARD(moreGenerators)...);
     }
     template<typename T>
     auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
-        return Generators<T>(std::move(generator));
+        return Generators<T>(CATCH_MOVE(generator));
     }
     template<typename T, typename... Gs>
     auto makeGenerators( T&& val, Gs &&... moreGenerators ) -> Generators<std::decay_t<T>> {
-        return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
+        return makeGenerators( value( CATCH_FORWARD( val ) ), CATCH_FORWARD( moreGenerators )... );
     }
     template<typename T, typename U, typename... Gs>
     auto makeGenerators( as<T>, U&& val, Gs &&... moreGenerators ) -> Generators<T> {
-        return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
+        return makeGenerators( value( T( CATCH_FORWARD( val ) ) ), CATCH_FORWARD( moreGenerators )... );
     }
 
     auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
diff --git a/packages/Catch2/src/catch2/generators/catch_generators_adapters.hpp b/packages/Catch2/src/catch2/generators/catch_generators_adapters.hpp
index 393b070000ea8080c89ed4cf095974254c190584..c55a3962538b0335bdd6ce1df8570f8f312e499c 100644
--- a/packages/Catch2/src/catch2/generators/catch_generators_adapters.hpp
+++ b/packages/Catch2/src/catch2/generators/catch_generators_adapters.hpp
@@ -10,6 +10,9 @@
 
 #include <catch2/generators/catch_generators.hpp>
 #include <catch2/internal/catch_meta.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
+
+#include <cassert>
 
 namespace Catch {
 namespace Generators {
@@ -21,7 +24,7 @@ namespace Generators {
         size_t m_target;
     public:
         TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
-            m_generator(std::move(generator)),
+            m_generator(CATCH_MOVE(generator)),
             m_target(target)
         {
             assert(target != 0 && "Empty generators are not allowed");
@@ -47,7 +50,7 @@ namespace Generators {
 
     template <typename T>
     GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
-        return GeneratorWrapper<T>(Catch::Detail::make_unique<TakeGenerator<T>>(target, std::move(generator)));
+        return GeneratorWrapper<T>(Catch::Detail::make_unique<TakeGenerator<T>>(target, CATCH_MOVE(generator)));
     }
 
 
@@ -58,8 +61,8 @@ namespace Generators {
     public:
         template <typename P = Predicate>
         FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
-            m_generator(std::move(generator)),
-            m_predicate(std::forward<P>(pred))
+            m_generator(CATCH_MOVE(generator)),
+            m_predicate(CATCH_FORWARD(pred))
         {
             if (!m_predicate(m_generator.get())) {
                 // It might happen that there are no values that pass the
@@ -88,7 +91,7 @@ namespace Generators {
 
     template <typename T, typename Predicate>
     GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
-        return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator)));
+        return GeneratorWrapper<T>(Catch::Detail::make_unique<FilterGenerator<T, Predicate>>(CATCH_FORWARD(pred), CATCH_MOVE(generator)));
     }
 
     template <typename T>
@@ -103,7 +106,7 @@ namespace Generators {
         size_t m_repeat_index = 0;
     public:
         RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
-            m_generator(std::move(generator)),
+            m_generator(CATCH_MOVE(generator)),
             m_target_repeats(repeats)
         {
             assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
@@ -144,7 +147,7 @@ namespace Generators {
 
     template <typename T>
     GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
-        return GeneratorWrapper<T>(Catch::Detail::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
+        return GeneratorWrapper<T>(Catch::Detail::make_unique<RepeatGenerator<T>>(repeats, CATCH_MOVE(generator)));
     }
 
     template <typename T, typename U, typename Func>
@@ -157,8 +160,8 @@ namespace Generators {
     public:
         template <typename F2 = Func>
         MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
-            m_generator(std::move(generator)),
-            m_function(std::forward<F2>(function)),
+            m_generator(CATCH_MOVE(generator)),
+            m_function(CATCH_FORWARD(function)),
             m_cache(m_function(m_generator.get()))
         {}
 
@@ -177,14 +180,14 @@ namespace Generators {
     template <typename Func, typename U, typename T = FunctionReturnType<Func, U>>
     GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
         return GeneratorWrapper<T>(
-            Catch::Detail::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+            Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
         );
     }
 
     template <typename T, typename U, typename Func>
     GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
         return GeneratorWrapper<T>(
-            Catch::Detail::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
+            Catch::Detail::make_unique<MapGenerator<T, U, Func>>(CATCH_FORWARD(function), CATCH_MOVE(generator))
         );
     }
 
@@ -196,7 +199,7 @@ namespace Generators {
         bool m_used_up = false;
     public:
         ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
-            m_chunk_size(size), m_generator(std::move(generator))
+            m_chunk_size(size), m_generator(CATCH_MOVE(generator))
         {
             m_chunk.reserve(m_chunk_size);
             if (m_chunk_size != 0) {
@@ -227,7 +230,7 @@ namespace Generators {
     template <typename T>
     GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
         return GeneratorWrapper<std::vector<T>>(
-            Catch::Detail::make_unique<ChunkGenerator<T>>(size, std::move(generator))
+            Catch::Detail::make_unique<ChunkGenerator<T>>(size, CATCH_MOVE(generator))
         );
     }
 
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_all.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_all.hpp
index 6dc4ebf17bf745b8263b878d0016f8a410dc3c90..2960bf6763b11f06567a2ff35002fd4233ba0c05 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_all.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_all.hpp
@@ -31,7 +31,6 @@
 #include <catch2/interfaces/catch_interfaces_reporter.hpp>
 #include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
 #include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
-#include <catch2/interfaces/catch_interfaces_runner.hpp>
 #include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp>
 #include <catch2/interfaces/catch_interfaces_testcase.hpp>
 
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_capture.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_capture.hpp
index bfd0c0232ab12476f48870066ab1e2b213208601..2e7a631f152f28356893bd5524145cd6f2aa0b12 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_capture.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_capture.hpp
@@ -44,10 +44,10 @@ namespace Catch {
 
         virtual auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
 
-        virtual void benchmarkPreparing( std::string const& name ) = 0;
+        virtual void benchmarkPreparing( StringRef name ) = 0;
         virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
         virtual void benchmarkEnded( BenchmarkStats<> const& stats ) = 0;
-        virtual void benchmarkFailed( std::string const& error ) = 0;
+        virtual void benchmarkFailed( StringRef error ) = 0;
 
         virtual void pushScopedMessage( MessageInfo const& message ) = 0;
         virtual void popScopedMessage( MessageInfo const& message ) = 0;
@@ -63,7 +63,7 @@ namespace Catch {
         virtual void handleMessage
                 (   AssertionInfo const& info,
                     ResultWas::OfType resultType,
-                    StringRef const& message,
+                    StringRef message,
                     AssertionReaction& reaction ) = 0;
         virtual void handleUnexpectedExceptionNotThrown
                 (   AssertionInfo const& info,
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_combined_tu.cpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_combined_tu.cpp
index 46d7c38cf3a9f1a23d47daffcf73db2612d46e77..7acf0a12407cf6d1d18ba6a76ab769ce2320cd92 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_combined_tu.cpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_combined_tu.cpp
@@ -63,17 +63,6 @@ namespace Catch {
 }
 
 
-//////////////////////////////////////////////////
-// vvv formerly catch_interfaces_runner.cpp vvv //
-//////////////////////////////////////////////////
-
-#include <catch2/interfaces/catch_interfaces_runner.hpp>
-
-namespace Catch {
-    IRunner::~IRunner() = default;
-}
-
-
 ////////////////////////////////////////////////////
 // vvv formerly catch_interfaces_testcase.cpp vvv //
 ////////////////////////////////////////////////////
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_config.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_config.hpp
index f09b0c0fe95c163702906fae406c385893399ff9..60c4e9be8c47ffa9f0e1392968d221dfd59c0247 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_config.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_config.hpp
@@ -9,6 +9,7 @@
 #define CATCH_INTERFACES_CONFIG_HPP_INCLUDED
 
 #include <catch2/internal/catch_noncopyable.hpp>
+#include <catch2/internal/catch_stringref.hpp>
 
 #include <chrono>
 #include <iosfwd>
@@ -59,7 +60,7 @@ namespace Catch {
 
         virtual bool allowThrows() const = 0;
         virtual std::ostream& stream() const = 0;
-        virtual std::string name() const = 0;
+        virtual StringRef name() const = 0;
         virtual bool includeSuccessfulResults() const = 0;
         virtual bool shouldDebugBreak() const = 0;
         virtual bool warnAboutMissingAssertions() const = 0;
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp
index 2c18059a442fb864662d45ee52998a0aef61c383..e00f2c9db5ab376689480afebfabc131d1e753fa 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_enum_values_registry.hpp
@@ -26,7 +26,7 @@ namespace Catch {
     } // namespace Detail
 
     struct IMutableEnumValuesRegistry {
-        virtual ~IMutableEnumValuesRegistry();
+        virtual ~IMutableEnumValuesRegistry(); // = default;
 
         virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
 
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_exception.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_exception.hpp
index 84bb4974d104cd78da2ec4c8c902b0dc0237d1dc..c1834074577e0e1465a094c1e0aad6c703ba7bed 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_exception.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_exception.hpp
@@ -9,7 +9,6 @@
 #define CATCH_INTERFACES_EXCEPTION_HPP_INCLUDED
 
 #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
-#include <catch2/internal/catch_compiler_capabilities.hpp>
 #include <catch2/internal/catch_unique_ptr.hpp>
 
 #include <string>
@@ -22,12 +21,12 @@ namespace Catch {
     using ExceptionTranslators = std::vector<Detail::unique_ptr<IExceptionTranslator const>>;
 
     struct IExceptionTranslator {
-        virtual ~IExceptionTranslator();
+        virtual ~IExceptionTranslator(); // = default
         virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
     };
 
     struct IExceptionTranslatorRegistry {
-        virtual ~IExceptionTranslatorRegistry();
+        virtual ~IExceptionTranslatorRegistry(); // = default
 
         virtual std::string translateActiveException() const = 0;
     };
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp
index bea1f7d480727179c95e3ab6c90a604d82ba97be..aeefeedb5fb26c708e48c8229deff966150a8ade 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_registry_hub.hpp
@@ -31,7 +31,7 @@ namespace Catch {
     using IReporterFactoryPtr = Detail::unique_ptr<IReporterFactory>;
 
     struct IRegistryHub {
-        virtual ~IRegistryHub();
+        virtual ~IRegistryHub(); // = default
 
         virtual IReporterRegistry const& getReporterRegistry() const = 0;
         virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
@@ -43,11 +43,11 @@ namespace Catch {
     };
 
     struct IMutableRegistryHub {
-        virtual ~IMutableRegistryHub();
+        virtual ~IMutableRegistryHub(); // = default
         virtual void registerReporter( std::string const& name, IReporterFactoryPtr factory ) = 0;
         virtual void registerListener( IReporterFactoryPtr factory ) = 0;
         virtual void registerTest(Detail::unique_ptr<TestCaseInfo>&& testInfo, Detail::unique_ptr<ITestInvoker>&& invoker) = 0;
-        virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+        virtual void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) = 0;
         virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
         virtual void registerStartupException() noexcept = 0;
         virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp
index 194161302d6a9fec3cff227eaee56b813aea4fa0..89a86f59a867b2837d7ae45a111d556bd1dc01e7 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.cpp
@@ -29,20 +29,9 @@ namespace Catch {
     std::ostream& ReporterConfig::stream() const { return *m_stream; }
     IConfig const * ReporterConfig::fullConfig() const { return m_fullConfig; }
 
-
-    TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {}
-
-    GroupInfo::GroupInfo(  std::string const& _name,
-                           std::size_t _groupIndex,
-                           std::size_t _groupsCount )
-    :   name( _name ),
-        groupIndex( _groupIndex ),
-        groupsCounts( _groupsCount )
-    {}
-
-     AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
-                                     std::vector<MessageInfo> const& _infoMessages,
-                                     Totals const& _totals )
+    AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
+                                    std::vector<MessageInfo> const& _infoMessages,
+                                    Totals const& _totals )
     :   assertionResult( _assertionResult ),
         infoMessages( _infoMessages ),
         totals( _totals )
@@ -84,20 +73,6 @@ namespace Catch {
     {}
 
 
-    TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo,
-                                    Totals const& _totals,
-                                    bool _aborting )
-    :   groupInfo( _groupInfo ),
-        totals( _totals ),
-        aborting( _aborting )
-    {}
-
-    TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo )
-    :   groupInfo( _groupInfo ),
-        aborting( false )
-    {}
-
-
     TestRunStats::TestRunStats(   TestRunInfo const& _runInfo,
                     Totals const& _totals,
                     bool _aborting )
@@ -106,6 +81,6 @@ namespace Catch {
         aborting( _aborting )
     {}
 
-    void IStreamingReporter::fatalErrorEncountered( StringRef ) {}
+    IStreamingReporter::~IStreamingReporter() = default;
 
 } // end namespace Catch
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp
index 5a4c31a8df65760f8401e11d707d2c19eb55ebd7..681c8e4d85357cd7bc3f63a560ad4eb2881427a2 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter.hpp
@@ -14,7 +14,7 @@
 #include <catch2/internal/catch_message_info.hpp>
 #include <catch2/internal/catch_stringref.hpp>
 #include <catch2/internal/catch_unique_ptr.hpp>
-
+#include <catch2/internal/catch_move_and_forward.hpp>
 #include <catch2/benchmark/catch_estimate.hpp>
 #include <catch2/benchmark/catch_outlier_classification.hpp>
 
@@ -45,17 +45,8 @@ namespace Catch {
     };
 
     struct TestRunInfo {
-        TestRunInfo( std::string const& _name );
-        std::string name;
-    };
-    struct GroupInfo {
-        GroupInfo(  std::string const& _name,
-                    std::size_t _groupIndex,
-                    std::size_t _groupsCount );
-
-        std::string name;
-        std::size_t groupIndex;
-        std::size_t groupsCounts;
+        TestRunInfo(StringRef _name) : name(_name) {}
+        StringRef name;
     };
 
     struct AssertionStats {
@@ -99,17 +90,6 @@ namespace Catch {
         bool aborting;
     };
 
-    struct TestGroupStats {
-        TestGroupStats( GroupInfo const& _groupInfo,
-                        Totals const& _totals,
-                        bool _aborting );
-        TestGroupStats( GroupInfo const& _groupInfo );
-
-        GroupInfo groupInfo;
-        Totals totals;
-        bool aborting;
-    };
-
     struct TestRunStats {
         TestRunStats(   TestRunInfo const& _runInfo,
                         Totals const& _totals,
@@ -150,7 +130,7 @@ namespace Catch {
             }
             return {
                 info,
-                std::move(samples2),
+                CATCH_MOVE(samples2),
                 mean,
                 standardDeviation,
                 outliers,
@@ -182,7 +162,7 @@ namespace Catch {
     public:
         IStreamingReporter( IConfig const* config ): m_config( config ) {}
 
-        virtual ~IStreamingReporter() = default;
+        virtual ~IStreamingReporter(); // = default;
 
         // Implementing class must also provide the following static methods:
         // static std::string getDescription();
@@ -191,35 +171,36 @@ namespace Catch {
             return m_preferences;
         }
 
-        virtual void noMatchingTestCases( std::string const& spec ) = 0;
-
-        virtual void reportInvalidArguments(std::string const&) {}
+        virtual void noMatchingTestCases( StringRef unmatchedSpec ) = 0;
+        virtual void reportInvalidArguments( StringRef invalidArgument ) = 0;
 
         virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
 
+        //! Called _once_ for each TEST_CASE, no matter how many times it is entered
         virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+        //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
+        virtual void testCasePartialStarting( TestCaseInfo const& testInfo, uint64_t partNumber ) = 0;
         virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
 
-        virtual void benchmarkPreparing( std::string const& ) {}
-        virtual void benchmarkStarting( BenchmarkInfo const& ) {}
-        virtual void benchmarkEnded( BenchmarkStats<> const& ) {}
-        virtual void benchmarkFailed( std::string const& ) {}
+        virtual void benchmarkPreparing( StringRef benchmarkName ) = 0;
+        virtual void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) = 0;
+        virtual void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) = 0;
+        virtual void benchmarkFailed( StringRef benchmarkName ) = 0;
 
         virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
 
-        // The return value indicates if the messages buffer should be cleared:
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+        virtual void assertionEnded( AssertionStats const& assertionStats ) = 0;
 
         virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+        //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
+        virtual void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber ) = 0;
+        //! Called _once_ for each TEST_CASE, no matter how many times it is entered
         virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
         virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
 
         virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
 
-        // Default empty implementation provided
-        virtual void fatalErrorEncountered( StringRef name );
+        virtual void fatalErrorEncountered( StringRef error ) = 0;
 
         //! Writes out information about provided reporters using reporter-specific format
         virtual void listReporters(std::vector<ReporterDescription> const& descriptions) = 0;
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.hpp
index 9bcf94f0b9efd4683b448fae75d2bae5dcfa06ca..5a40d1bded67f983b054ce0e348f37cc50b98edc 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter_factory.hpp
@@ -8,9 +8,16 @@
 #ifndef CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
 #define CATCH_INTERFACES_REPORTER_FACTORY_HPP_INCLUDED
 
+#include <catch2/internal/catch_unique_ptr.hpp>
+
+#include <string>
+
 namespace Catch {
 
     struct ReporterConfig;
+    struct IStreamingReporter;
+    using IStreamingReporterPtr = Detail::unique_ptr<IStreamingReporter>;
+
 
     struct IReporterFactory {
         virtual ~IReporterFactory(); // = default
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp
index 05195f370ec85731c7f0781efe906f3470ad3124..54871ccedc236d162899531a9920565c3b52b514 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_reporter_registry.hpp
@@ -27,7 +27,7 @@ namespace Catch {
         using FactoryMap = std::map<std::string, IReporterFactoryPtr>;
         using Listeners = std::vector<IReporterFactoryPtr>;
 
-        virtual ~IReporterRegistry();
+        virtual ~IReporterRegistry(); // = default
         virtual IStreamingReporterPtr create( std::string const& name, IConfig const* config ) const = 0;
         virtual FactoryMap const& getFactories() const = 0;
         virtual Listeners const& getListeners() const = 0;
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_runner.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_runner.hpp
deleted file mode 100644
index 9072d72123f6c1eeb001e6b29ec7cef4fc047b0c..0000000000000000000000000000000000000000
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_runner.hpp
+++ /dev/null
@@ -1,19 +0,0 @@
-
-//              Copyright Catch2 Authors
-// Distributed under the Boost Software License, Version 1.0.
-//   (See accompanying file LICENSE_1_0.txt or copy at
-//        https://www.boost.org/LICENSE_1_0.txt)
-
-// SPDX-License-Identifier: BSL-1.0
-#ifndef CATCH_INTERFACES_RUNNER_HPP_INCLUDED
-#define CATCH_INTERFACES_RUNNER_HPP_INCLUDED
-
-namespace Catch {
-
-    struct IRunner {
-        virtual ~IRunner();
-        virtual bool aborting() const = 0;
-    };
-}
-
-#endif // CATCH_INTERFACES_RUNNER_HPP_INCLUDED
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp
index 152fdc2341436bcf48e58c47b00a1e2c37d05fb5..6b0973b40208204bc814619c43bed15e6d82c8a5 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_tag_alias_registry.hpp
@@ -15,7 +15,7 @@ namespace Catch {
     struct TagAlias;
 
     struct ITagAliasRegistry {
-        virtual ~ITagAliasRegistry();
+        virtual ~ITagAliasRegistry(); // = default
         // Nullptr if not present
         virtual TagAlias const* find( std::string const& alias ) const = 0;
         virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
diff --git a/packages/Catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp b/packages/Catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp
index aad5ea20e3a66f0dbe5e83d5b8533ece6d5c6740..2d0b6c284f59836bf0995916fcb0fcc2320a62d9 100644
--- a/packages/Catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp
+++ b/packages/Catch2/src/catch2/interfaces/catch_interfaces_testcase.hpp
@@ -17,14 +17,14 @@ namespace Catch {
 
     struct ITestInvoker {
         virtual void invoke () const = 0;
-        virtual ~ITestInvoker();
+        virtual ~ITestInvoker(); // = default
     };
 
     class TestCaseHandle;
     struct IConfig;
 
     struct ITestCaseRegistry {
-        virtual ~ITestCaseRegistry();
+        virtual ~ITestCaseRegistry(); // = default
         // TODO: this exists only for adding filenames to test cases -- let's expose this in a saner way later
         virtual std::vector<TestCaseInfo* > const& getAllInfos() const = 0;
         virtual std::vector<TestCaseHandle> const& getAllTests() const = 0;
diff --git a/packages/Catch2/src/catch2/internal/catch_assertion_handler.cpp b/packages/Catch2/src/catch2/internal/catch_assertion_handler.cpp
index 7774ca68f116c5187fabe07098fcb706e3e9ef29..828aa7cdff567238974b9d20e132377ea2bf0360 100644
--- a/packages/Catch2/src/catch2/internal/catch_assertion_handler.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_assertion_handler.cpp
@@ -18,7 +18,7 @@
 namespace Catch {
 
     AssertionHandler::AssertionHandler
-        (   StringRef const& macroName,
+        (   StringRef macroName,
             SourceLineInfo const& lineInfo,
             StringRef capturedExpression,
             ResultDisposition::Flags resultDisposition )
@@ -29,7 +29,7 @@ namespace Catch {
     void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
         m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
     }
-    void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) {
+    void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef message) {
         m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
     }
 
@@ -80,7 +80,7 @@ namespace Catch {
 
     // This is the overload that takes a string and infers the Equals matcher from it
     // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp
-    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString  ) {
+    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString  ) {
         handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString );
     }
 
diff --git a/packages/Catch2/src/catch2/internal/catch_assertion_handler.hpp b/packages/Catch2/src/catch2/internal/catch_assertion_handler.hpp
index 8e266ade62a8fbb906e506ddbab7895a343418a4..b7c7613e311d874dacb60860a1ccefbc4719468e 100644
--- a/packages/Catch2/src/catch2/internal/catch_assertion_handler.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_assertion_handler.hpp
@@ -32,7 +32,7 @@ namespace Catch {
 
     public:
         AssertionHandler
-            (   StringRef const& macroName,
+            (   StringRef macroName,
                 SourceLineInfo const& lineInfo,
                 StringRef capturedExpression,
                 ResultDisposition::Flags resultDisposition );
@@ -49,7 +49,7 @@ namespace Catch {
         }
         void handleExpr( ITransientExpression const& expr );
 
-        void handleMessage(ResultWas::OfType resultType, StringRef const& message);
+        void handleMessage(ResultWas::OfType resultType, StringRef message);
 
         void handleExceptionThrownAsExpected();
         void handleUnexpectedExceptionNotThrown();
@@ -64,7 +64,7 @@ namespace Catch {
         auto allowThrows() const -> bool;
     };
 
-    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString );
+    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef matcherString );
 
 } // namespace Catch
 
diff --git a/packages/Catch2/src/catch2/internal/catch_clara.hpp b/packages/Catch2/src/catch2/internal/catch_clara.hpp
index 2da6919a229d49e8beb52def386a7cb665a3ab06..477cb08d1e7a509e96da5d962cbe33fcbd2bbfab 100644
--- a/packages/Catch2/src/catch2/internal/catch_clara.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_clara.hpp
@@ -30,11 +30,11 @@
 #endif
 
 #include <catch2/internal/catch_noncopyable.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <cassert>
 #include <cctype>
 #include <memory>
-#include <ostream>
 #include <sstream>
 #include <string>
 #include <vector>
@@ -69,8 +69,7 @@ namespace Catch {
             template <typename ClassT, typename ReturnT, typename ArgT>
             struct UnaryLambdaTraits<ReturnT ( ClassT::* )( ArgT ) const> {
                 static const bool isValid = true;
-                using ArgType = typename std::remove_const<
-                    typename std::remove_reference<ArgT>::type>::type;
+                using ArgType = std::remove_const_t<std::remove_reference_t<ArgT>>;
                 using ReturnType = ReturnT;
             };
 
@@ -293,7 +292,7 @@ namespace Catch {
                 T temp;
                 auto result = convertInto( source, temp );
                 if ( result )
-                    target = std::move( temp );
+                    target = CATCH_MOVE( temp );
                 return result;
             }
 #endif // CLARA_CONFIG_OPTIONAL_TYPE
diff --git a/packages/Catch2/src/catch2/internal/catch_combined_tu.cpp b/packages/Catch2/src/catch2/internal/catch_combined_tu.cpp
index c3ed9e2bc7abead4b9a86462fa02714bb64b0665..b78d190498d519da2ed12db622fd75a89b4464a6 100644
--- a/packages/Catch2/src/catch2/internal/catch_combined_tu.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_combined_tu.cpp
@@ -194,7 +194,7 @@ Catch::LeakDetector::~LeakDetector() {
 
 namespace Catch {
 
-    MessageInfo::MessageInfo(   StringRef const& _macroName,
+    MessageInfo::MessageInfo(   StringRef _macroName,
                                 SourceLineInfo const& _lineInfo,
                                 ResultWas::OfType _type )
     :   macroName( _macroName ),
diff --git a/packages/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp b/packages/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp
index 3c24cf2b819898252e2f9401afeb53b6fe4a66cd..0684050b921cf3502ee531cfaa1554cb594cd5b4 100644
--- a/packages/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_compiler_capabilities.hpp
@@ -38,9 +38,9 @@
 
 #endif
 
-// We have to avoid both ICC and Clang, because they try to mask themselves
-// as gcc, and we want only GCC in this block
-#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__)
+// Only GCC compiler should be used in this block, so other compilers trying to
+// mask themselves as GCC should be ignored.
+#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__)
 #    define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" )
 #    define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION  _Pragma( "GCC diagnostic pop" )
 
@@ -71,8 +71,13 @@
 // REQUIRE(std::string("12") + "34" == "1234")
 // ```
 //
+// Similarly, NVHPC's implementation of `__builtin_constant_p` has a bug which
+// results in calls to the immediately evaluated lambda expressions to be
+// reported as unevaluated lambdas.
+// https://developer.nvidia.com/nvidia_bug/3321845.
+//
 // Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented.
-#  if !defined(__ibmxl__) && !defined(__CUDACC__)
+#  if !defined(__ibmxl__) && !defined(__CUDACC__) && !defined( __NVCOMPILER )
 #    define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */
 #  endif
 
@@ -222,7 +227,7 @@
   // Check if byte is available and usable
   #  if __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
   #    include <cstddef>
-  #    if __cpp_lib_byte > 0
+  #    if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0)
   #      define CATCH_INTERNAL_CONFIG_CPP17_BYTE
   #    endif
   #  endif // __has_include(<cstddef>) && defined(CATCH_CPP17_OR_GREATER)
diff --git a/packages/Catch2/src/catch2/internal/catch_container_nonmembers.hpp b/packages/Catch2/src/catch2/internal/catch_container_nonmembers.hpp
index f1dedeb6a4ca9ecea25e9f33b1d5a76ff0e8f4c4..d6f10eb5ed0a9e5d07050441f17403dbf9a60377 100644
--- a/packages/Catch2/src/catch2/internal/catch_container_nonmembers.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_container_nonmembers.hpp
@@ -10,6 +10,8 @@
 
 #include <catch2/internal/catch_compiler_capabilities.hpp>
 
+#include <cstddef>
+#include <initializer_list>
 
 // We want a simple polyfill over `std::empty`, `std::size` and so on
 // for C++14 or C++ libraries with incomplete support.
diff --git a/packages/Catch2/src/catch2/internal/catch_context.cpp b/packages/Catch2/src/catch2/internal/catch_context.cpp
index 86c56a6f5b577f27f8da7b2bf43091a01659eb73..790a1b23d62273f5ea2d8ccab8e76ae461b6371f 100644
--- a/packages/Catch2/src/catch2/internal/catch_context.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_context.cpp
@@ -17,9 +17,6 @@ namespace Catch {
         IResultCapture* getResultCapture() override {
             return m_resultCapture;
         }
-        IRunner* getRunner() override {
-            return m_runner;
-        }
 
         IConfig const* getConfig() const override {
             return m_config;
@@ -31,9 +28,6 @@ namespace Catch {
         void setResultCapture( IResultCapture* resultCapture ) override {
             m_resultCapture = resultCapture;
         }
-        void setRunner( IRunner* runner ) override {
-            m_runner = runner;
-        }
         void setConfig( IConfig const* config ) override {
             m_config = config;
         }
@@ -42,7 +36,6 @@ namespace Catch {
 
     private:
         IConfig const* m_config = nullptr;
-        IRunner* m_runner = nullptr;
         IResultCapture* m_resultCapture = nullptr;
     };
 
diff --git a/packages/Catch2/src/catch2/internal/catch_context.hpp b/packages/Catch2/src/catch2/internal/catch_context.hpp
index 6f660f3a039788deaf042e0580273d56d4804d82..d3552448222247500d5cc67542116ae32b5562ce 100644
--- a/packages/Catch2/src/catch2/internal/catch_context.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_context.hpp
@@ -11,23 +11,20 @@
 namespace Catch {
 
     struct IResultCapture;
-    struct IRunner;
     struct IConfig;
 
     struct IContext
     {
-        virtual ~IContext();
+        virtual ~IContext(); // = default
 
         virtual IResultCapture* getResultCapture() = 0;
-        virtual IRunner* getRunner() = 0;
         virtual IConfig const* getConfig() const = 0;
     };
 
     struct IMutableContext : IContext
     {
-        virtual ~IMutableContext();
+        virtual ~IMutableContext(); // = default
         virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
-        virtual void setRunner( IRunner* runner ) = 0;
         virtual void setConfig( IConfig const* config ) = 0;
 
     private:
diff --git a/packages/Catch2/src/catch2/internal/catch_debugger.cpp b/packages/Catch2/src/catch2/internal/catch_debugger.cpp
index e1764034b6a0bfc4c78492b5fefd0e7226a563fd..3c80173a69c830fc6ab1c719bc98aa5b59d5c091 100644
--- a/packages/Catch2/src/catch2/internal/catch_debugger.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_debugger.cpp
@@ -53,7 +53,7 @@
 
             size = sizeof(info);
             if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) {
-                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n\n" << std::flush;
                 return false;
             }
 
diff --git a/packages/Catch2/src/catch2/internal/catch_decomposer.hpp b/packages/Catch2/src/catch2/internal/catch_decomposer.hpp
index 9af5c19f706c9a59c2701396f08a21e7e2920c84..a747c34cd61a5b364749823d210f7d0b80e1870f 100644
--- a/packages/Catch2/src/catch2/internal/catch_decomposer.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_decomposer.hpp
@@ -183,60 +183,53 @@ namespace Catch {
     public:
         explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
 
-        template<typename RhsT>
-        auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { compareEqual( m_lhs, rhs ), m_lhs, "=="_sr, rhs };
+        template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0>
+        friend auto operator == ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> {
+            return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs };
         }
-        auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
-            return { m_lhs == rhs, m_lhs, "=="_sr, rhs };
+        template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0>
+        friend auto operator == ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> {
+            return { compareEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "=="_sr, rhs };
         }
 
-        template<typename RhsT>
-        auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { compareNotEqual( m_lhs, rhs ), m_lhs, "!="_sr, rhs };
+        template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0>
+        friend auto operator != ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> {
+            return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs };
         }
-        auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
-            return { m_lhs != rhs, m_lhs, "!="_sr, rhs };
+        template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0>
+        friend auto operator != ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> {
+            return { compareNotEqual( lhs.m_lhs, rhs ), lhs.m_lhs, "!="_sr, rhs };
         }
 
-        template<typename RhsT>
-        auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs > rhs), m_lhs, ">"_sr, rhs };
-        }
-        template<typename RhsT>
-        auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs < rhs), m_lhs, "<"_sr, rhs };
-        }
-        template<typename RhsT>
-        auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">="_sr, rhs };
-        }
-        template<typename RhsT>
-        auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<="_sr, rhs };
-        }
-        template <typename RhsT>
-        auto operator | (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs | rhs), m_lhs, "|"_sr, rhs };
-        }
-        template <typename RhsT>
-        auto operator & (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs & rhs), m_lhs, "&"_sr, rhs };
-        }
-        template <typename RhsT>
-        auto operator ^ (RhsT const& rhs) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs ^ rhs), m_lhs, "^"_sr, rhs };
+    #define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(op) \
+        template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0> \
+        friend auto operator op ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> { \
+            return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
+        } \
+        template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0> \
+        friend auto operator op ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> { \
+            return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
         }
 
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(<=)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(>=)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&)
+        CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(^)
+
+    #undef CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR
+
         template<typename RhsT>
-        auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+        friend auto operator && ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
             static_assert(always_false<RhsT>::value,
             "operator&& is not supported inside assertions, "
             "wrap the expression inside parentheses, or decompose it");
         }
 
         template<typename RhsT>
-        auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
+        friend auto operator || ( ExprLhs &&, RhsT && ) -> BinaryExpr<LhsT, RhsT const&> {
             static_assert(always_false<RhsT>::value,
             "operator|| is not supported inside assertions, "
             "wrap the expression inside parentheses, or decompose it");
@@ -247,21 +240,15 @@ namespace Catch {
         }
     };
 
-    void handleExpression( ITransientExpression const& expr );
-
-    template<typename T>
-    void handleExpression( ExprLhs<T> const& expr ) {
-        handleExpression( expr.makeUnaryExpr() );
-    }
-
     struct Decomposer {
-        template<typename T>
-        auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
-            return ExprLhs<T const&>{ lhs };
+        template<typename T, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<T>>::value, int> = 0>
+        friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {
+            return ExprLhs<const T&>{ lhs };
         }
 
-        auto operator <=( bool value ) -> ExprLhs<bool> {
-            return ExprLhs<bool>{ value };
+        template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>
+        friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {
+            return ExprLhs<T>{ value };
         }
     };
 
diff --git a/packages/Catch2/src/catch2/internal/catch_enum_values_registry.cpp b/packages/Catch2/src/catch2/internal/catch_enum_values_registry.cpp
index cfa206f99abadfe7e528744460f5100cc590a6ac..ef4baf9a994bc45f738fc49d7814a4fbd7b6b8cf 100644
--- a/packages/Catch2/src/catch2/internal/catch_enum_values_registry.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_enum_values_registry.cpp
@@ -13,7 +13,7 @@
 
 namespace Catch {
 
-    IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {}
+    IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() = default;
 
     namespace Detail {
 
@@ -21,7 +21,7 @@ namespace Catch {
             // Extracts the actual name part of an enum instance
             // In other words, it returns the Blue part of Bikeshed::Colour::Blue
             StringRef extractInstanceName(StringRef enumInstance) {
-                // Find last occurence of ":"
+                // Find last occurrence of ":"
                 size_t name_start = enumInstance.size();
                 while (name_start > 0 && enumInstance[name_start - 1] != ':') {
                     --name_start;
diff --git a/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.cpp b/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.cpp
index 084838894e5697b7738d8df0fd5cc7716d7d975d..acddea6ec99d796d699c01892bfcf34498f44891 100644
--- a/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.cpp
@@ -9,39 +9,45 @@
 #include <catch2/internal/catch_compiler_capabilities.hpp>
 #include <catch2/internal/catch_enforce.hpp>
 #include <catch2/internal/catch_test_failure_exception.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
 
     ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
     }
 
-    void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) {
-        m_translators.push_back( Detail::unique_ptr<const IExceptionTranslator>( translator ) );
+    void ExceptionTranslatorRegistry::registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator ) {
+        m_translators.push_back( CATCH_MOVE( translator ) );
     }
 
 #if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
     std::string ExceptionTranslatorRegistry::translateActiveException() const {
+        // Compiling a mixed mode project with MSVC means that CLR
+        // exceptions will be caught in (...) as well. However, these do
+        // do not fill-in std::current_exception and thus lead to crash
+        // when attempting rethrow.
+        // /EHa switch also causes structured exceptions to be caught
+        // here, but they fill-in current_exception properly, so
+        // at worst the output should be a little weird, instead of
+        // causing a crash.
+        if ( std::current_exception() == nullptr ) {
+            return "Non C++ exception. Possibly a CLR exception.";
+        }
+
+        // First we try user-registered translators. If none of them can
+        // handle the exception, it will be rethrown handled by our defaults.
         try {
-            // Compiling a mixed mode project with MSVC means that CLR
-            // exceptions will be caught in (...) as well. However, these
-            // do not fill-in std::current_exception and thus lead to crash
-            // when attempting rethrow.
-            // /EHa switch also causes structured exceptions to be caught
-            // here, but they fill-in current_exception properly, so
-            // at worst the output should be a little weird, instead of
-            // causing a crash.
-            if (std::current_exception() == nullptr) {
-                return "Non C++ exception. Possibly a CLR exception.";
-            }
             return tryTranslators();
         }
+        // To avoid having to handle TFE explicitly everywhere, we just
+        // rethrow it so that it goes back up the caller.
         catch( TestFailureException& ) {
             std::rethrow_exception(std::current_exception());
         }
-        catch( std::exception& ex ) {
+        catch( std::exception const& ex ) {
             return ex.what();
         }
-        catch( std::string& msg ) {
+        catch( std::string const& msg ) {
             return msg;
         }
         catch( const char* msg ) {
diff --git a/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.hpp b/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.hpp
index f4a28d91acfcabdfe21ed76c0dc235db0c49e0a8..465796d35b8aecf61908480f561285fa1fc78131 100644
--- a/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_exception_translator_registry.hpp
@@ -9,6 +9,8 @@
 #define CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
 
 #include <catch2/interfaces/catch_interfaces_exception.hpp>
+#include <catch2/internal/catch_unique_ptr.hpp>
+
 #include <vector>
 #include <string>
 
@@ -16,8 +18,8 @@ namespace Catch {
 
     class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
     public:
-        ~ExceptionTranslatorRegistry();
-        virtual void registerTranslator( const IExceptionTranslator* translator );
+        ~ExceptionTranslatorRegistry() override;
+        void registerTranslator( Detail::unique_ptr<IExceptionTranslator>&& translator );
         std::string translateActiveException() const override;
         std::string tryTranslators() const;
 
diff --git a/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.cpp b/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.cpp
index 1b1e5c7fd2bd1c4e61fd736ca757290bf6fbf943..9383257cecb51fc2e5762b94fc1f24df607292e8 100644
--- a/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.cpp
@@ -5,30 +5,73 @@
 //        https://www.boost.org/LICENSE_1_0.txt)
 
 // SPDX-License-Identifier: BSL-1.0
+
+/** \file
+ * This file provides platform specific implementations of FatalConditionHandler
+ *
+ * This means that there is a lot of conditional compilation, and platform
+ * specific code. Currently, Catch2 supports a dummy handler (if no
+ * handler is desired), and 2 platform specific handlers:
+ *  * Windows' SEH
+ *  * POSIX signals
+ *
+ * Consequently, various pieces of code below are compiled if either of
+ * the platform specific handlers is enabled, or if none of them are
+ * enabled. It is assumed that both cannot be enabled at the same time,
+ * and doing so should cause a compilation error.
+ *
+ * If another platform specific handler is added, the compile guards
+ * below will need to be updated taking these assumptions into account.
+ */
+
 #include <catch2/internal/catch_fatal_condition_handler.hpp>
 
 #include <catch2/internal/catch_context.hpp>
+#include <catch2/internal/catch_enforce.hpp>
 #include <catch2/interfaces/catch_interfaces_capture.hpp>
+#include <catch2/internal/catch_windows_h_proxy.hpp>
 
-#if defined(__GNUC__)
-#    pragma GCC diagnostic push
-#    pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#endif
+#include <algorithm>
+
+#if !defined( CATCH_CONFIG_WINDOWS_SEH ) && !defined( CATCH_CONFIG_POSIX_SIGNALS )
+
+namespace Catch {
+
+    // If neither SEH nor signal handling is required, the handler impls
+    // do not have to do anything, and can be empty.
+    void FatalConditionHandler::engage_platform() {}
+    void FatalConditionHandler::disengage_platform() {}
+    FatalConditionHandler::FatalConditionHandler() = default;
+    FatalConditionHandler::~FatalConditionHandler() = default;
+
+} // end namespace Catch
+
+#endif // !CATCH_CONFIG_WINDOWS_SEH && !CATCH_CONFIG_POSIX_SIGNALS
+
+#if defined( CATCH_CONFIG_WINDOWS_SEH ) && defined( CATCH_CONFIG_POSIX_SIGNALS )
+#error "Inconsistent configuration: Windows' SEH handling and POSIX signals cannot be enabled at the same time"
+#endif // CATCH_CONFIG_WINDOWS_SEH && CATCH_CONFIG_POSIX_SIGNALS
 
 #if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
 
 namespace {
-    // Report the error condition
+    //! Signals fatal error message to the run context
     void reportFatal( char const * const message ) {
         Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
     }
-}
 
-#endif // signals/SEH handling
+    //! Minimal size Catch2 needs for its own fatal error handling.
+    //! Picked empirically, so it might not be sufficient on all
+    //! platforms, and for all configurations.
+    constexpr std::size_t minStackSizeForErrors = 32 * 1024;
+} // end unnamed namespace
+
+#endif // CATCH_CONFIG_WINDOWS_SEH || CATCH_CONFIG_POSIX_SIGNALS
 
 #if defined( CATCH_CONFIG_WINDOWS_SEH )
 
 namespace Catch {
+
     struct SignalDefs { DWORD id; const char* name; };
 
     // There is no 1-1 mapping between signals and windows exceptions.
@@ -41,7 +84,7 @@ namespace Catch {
         { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
     };
 
-    LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+    static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
         for (auto const& def : signalDefs) {
             if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
                 reportFatal(def.name);
@@ -52,35 +95,52 @@ namespace Catch {
         return EXCEPTION_CONTINUE_SEARCH;
     }
 
+    // Since we do not support multiple instantiations, we put these
+    // into global variables and rely on cleaning them up in outlined
+    // constructors/destructors
+    static PVOID exceptionHandlerHandle = nullptr;
+
+
+    // For MSVC, we reserve part of the stack memory for handling
+    // memory overflow structured exception.
     FatalConditionHandler::FatalConditionHandler() {
-        isSet = true;
-        // 32k seems enough for Catch to handle stack overflow,
-        // but the value was found experimentally, so there is no strong guarantee
-        guaranteeSize = 32 * 1024;
-        exceptionHandlerHandle = nullptr;
+        ULONG guaranteeSize = static_cast<ULONG>(minStackSizeForErrors);
+        if (!SetThreadStackGuarantee(&guaranteeSize)) {
+            // We do not want to fully error out, because needing
+            // the stack reserve should be rare enough anyway.
+            Catch::cerr()
+                << "Failed to reserve piece of stack."
+                << " Stack overflows will not be reported successfully.";
+        }
+    }
+
+    // We do not attempt to unset the stack guarantee, because
+    // Windows does not support lowering the stack size guarantee.
+    FatalConditionHandler::~FatalConditionHandler() = default;
+
+
+    void FatalConditionHandler::engage_platform() {
         // Register as first handler in current chain
         exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
-        // Pass in guarantee size to be filled
-        SetThreadStackGuarantee(&guaranteeSize);
+        if (!exceptionHandlerHandle) {
+            CATCH_RUNTIME_ERROR("Could not register vectored exception handler");
+        }
     }
 
-    void FatalConditionHandler::reset() {
-        if (isSet) {
-            RemoveVectoredExceptionHandler(exceptionHandlerHandle);
-            SetThreadStackGuarantee(&guaranteeSize);
-            exceptionHandlerHandle = nullptr;
-            isSet = false;
+    void FatalConditionHandler::disengage_platform() {
+        if (!RemoveVectoredExceptionHandler(exceptionHandlerHandle)) {
+            CATCH_RUNTIME_ERROR("Could not unregister vectored exception handler");
         }
+        exceptionHandlerHandle = nullptr;
     }
 
-bool FatalConditionHandler::isSet = false;
-ULONG FatalConditionHandler::guaranteeSize = 0;
-PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
+} // end namespace Catch
 
+#endif // CATCH_CONFIG_WINDOWS_SEH
 
-} // namespace Catch
+#if defined( CATCH_CONFIG_POSIX_SIGNALS )
 
-#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
+#include <signal.h>
 
 namespace Catch {
 
@@ -89,10 +149,6 @@ namespace Catch {
         const char* name;
     };
 
-    // 32kb for the alternate stack seems to be sufficient. However, this value
-    // is experimentally determined, so that's not guaranteed.
-    static constexpr std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
-
     static SignalDefs signalDefs[] = {
         { SIGINT,  "SIGINT - Terminal interrupt signal" },
         { SIGILL,  "SIGILL - Illegal instruction signal" },
@@ -102,8 +158,32 @@ namespace Catch {
         { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
     };
 
+// Older GCCs trigger -Wmissing-field-initializers for T foo = {}
+// which is zero initialization, but not explicit. We want to avoid
+// that.
+#if defined(__GNUC__)
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wmissing-field-initializers"
+#endif
+
+    static char* altStackMem = nullptr;
+    static std::size_t altStackSize = 0;
+    static stack_t oldSigStack{};
+    static struct sigaction oldSigActions[sizeof(signalDefs) / sizeof(SignalDefs)]{};
+
+    static void restorePreviousSignalHandlers() {
+        // We set signal handlers back to the previous ones. Hopefully
+        // nobody overwrote them in the meantime, and doesn't expect
+        // their signal handlers to live past ours given that they
+        // installed them after ours..
+        for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+            sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
+        }
+        // Return the old stack
+        sigaltstack(&oldSigStack, nullptr);
+    }
 
-    void FatalConditionHandler::handleSignal( int sig ) {
+    static void handleSignal( int sig ) {
         char const * name = "<unknown signal>";
         for (auto const& def : signalDefs) {
             if (sig == def.id) {
@@ -111,16 +191,33 @@ namespace Catch {
                 break;
             }
         }
-        reset();
-        reportFatal(name);
+        // We need to restore previous signal handlers and let them do
+        // their thing, so that the users can have the debugger break
+        // when a signal is raised, and so on.
+        restorePreviousSignalHandlers();
+        reportFatal( name );
         raise( sig );
     }
 
     FatalConditionHandler::FatalConditionHandler() {
-        isSet = true;
+        assert(!altStackMem && "Cannot initialize POSIX signal handler when one already exists");
+        if (altStackSize == 0) {
+            altStackSize = std::max(static_cast<size_t>(SIGSTKSZ), minStackSizeForErrors);
+        }
+        altStackMem = new char[altStackSize]();
+    }
+
+    FatalConditionHandler::~FatalConditionHandler() {
+        delete[] altStackMem;
+        // We signal that another instance can be constructed by zeroing
+        // out the pointer.
+        altStackMem = nullptr;
+    }
+
+    void FatalConditionHandler::engage_platform() {
         stack_t sigStack;
         sigStack.ss_sp = altStackMem;
-        sigStack.ss_size = sigStackSize;
+        sigStack.ss_size = altStackSize;
         sigStack.ss_flags = 0;
         sigaltstack(&sigStack, &oldSigStack);
         struct sigaction sa = { };
@@ -132,28 +229,15 @@ namespace Catch {
         }
     }
 
-    void FatalConditionHandler::reset() {
-        if( isSet ) {
-            // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
-            for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
-                sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
-            }
-            // Return the old stack
-            sigaltstack(&oldSigStack, nullptr);
-            isSet = false;
-        }
-    }
-
-    bool FatalConditionHandler::isSet = false;
-    struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
-    stack_t FatalConditionHandler::oldSigStack = {};
-    char FatalConditionHandler::altStackMem[sigStackSize] = {};
+#if defined(__GNUC__)
+#    pragma GCC diagnostic pop
+#endif
 
 
-} // namespace Catch
+    void FatalConditionHandler::disengage_platform() {
+        restorePreviousSignalHandlers();
+    }
 
-#endif // signals/SEH handling
+} // end namespace Catch
 
-#if defined(__GNUC__)
-#    pragma GCC diagnostic pop
-#endif
+#endif // CATCH_CONFIG_POSIX_SIGNALS
diff --git a/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.hpp b/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.hpp
index 1b23c0b88fe70599fa1c581012ccd9488d7ad6e3..4a2818cb567b4c427bc224e945a8509db06fc0bd 100644
--- a/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_fatal_condition_handler.hpp
@@ -10,57 +10,60 @@
 
 #include <catch2/internal/catch_platform.hpp>
 #include <catch2/internal/catch_compiler_capabilities.hpp>
-#include <catch2/internal/catch_windows_h_proxy.hpp>
 
-
-#if defined( CATCH_CONFIG_WINDOWS_SEH )
+#include <cassert>
 
 namespace Catch {
 
-    struct FatalConditionHandler {
-
-        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
+    /**
+     * Wrapper for platform-specific fatal error (signals/SEH) handlers
+     *
+     * Tries to be cooperative with other handlers, and not step over
+     * other handlers. This means that unknown structured exceptions
+     * are passed on, previous signal handlers are called, and so on.
+     *
+     * Can only be instantiated once, and assumes that once a signal
+     * is caught, the binary will end up terminating. Thus, there
+     */
+    class FatalConditionHandler {
+        bool m_started = false;
+
+        // Install/disengage implementation for specific platform.
+        // Should be if-defed to work on current platform, can assume
+        // engage-disengage 1:1 pairing.
+        void engage_platform();
+        void disengage_platform();
+    public:
+        // Should also have platform-specific implementations as needed
         FatalConditionHandler();
-        static void reset();
-        ~FatalConditionHandler() { reset(); }
-
-    private:
-        static bool isSet;
-        static ULONG guaranteeSize;
-        static PVOID exceptionHandlerHandle;
+        ~FatalConditionHandler();
+
+        void engage() {
+            assert(!m_started && "Handler cannot be installed twice.");
+            m_started = true;
+            engage_platform();
+        }
+
+        void disengage() {
+            assert(m_started && "Handler cannot be uninstalled without being installed first");
+            m_started = false;
+            disengage_platform();
+        }
     };
 
-} // namespace Catch
-
-#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
-
-#include <signal.h>
-
-namespace Catch {
-
-    struct FatalConditionHandler {
-
-        static bool isSet;
-        static struct sigaction oldSigActions[];
-        static stack_t oldSigStack;
-        static char altStackMem[];
-
-        static void handleSignal( int sig );
-
-        FatalConditionHandler();
-        ~FatalConditionHandler() { reset(); }
-        static void reset();
+    //! Simple RAII guard for (dis)engaging the FatalConditionHandler
+    class FatalConditionHandlerGuard {
+        FatalConditionHandler* m_handler;
+    public:
+        FatalConditionHandlerGuard(FatalConditionHandler* handler):
+            m_handler(handler) {
+            m_handler->engage();
+        }
+        ~FatalConditionHandlerGuard() {
+            m_handler->disengage();
+        }
     };
 
-} // namespace Catch
-
-
-#else
-
-namespace Catch {
-    struct FatalConditionHandler {};
-}
-
-#endif
+} // end namespace Catch
 
 #endif // CATCH_FATAL_CONDITION_HANDLER_HPP_INCLUDED
diff --git a/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.cpp b/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..55a95a2846740c9bd87a479e0b65a6a1ec6adb7c
--- /dev/null
+++ b/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.cpp
@@ -0,0 +1,32 @@
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+#include <catch2/internal/catch_floating_point_helpers.hpp>
+
+#include <cstring>
+
+namespace Catch {
+    namespace Detail {
+
+        uint32_t convertToBits(float f) {
+            static_assert(sizeof(float) == sizeof(uint32_t), "Important ULP matcher assumption violated");
+            uint32_t i;
+            std::memcpy(&i, &f, sizeof(f));
+            return i;
+        }
+
+        uint64_t convertToBits(double d) {
+            static_assert(sizeof(double) == sizeof(uint64_t), "Important ULP matcher assumption violated");
+            uint64_t i;
+            std::memcpy(&i, &d, sizeof(d));
+            return i;
+        }
+
+    } // end namespace Detail
+} // end namespace Catch
+
diff --git a/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.hpp b/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3e73b3a72314a1e7bb8b8ad2b42ba24ed2cb6d27
--- /dev/null
+++ b/packages/Catch2/src/catch2/internal/catch_floating_point_helpers.hpp
@@ -0,0 +1,88 @@
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
+#define CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
+
+#include <catch2/internal/catch_polyfills.hpp>
+
+#include <cmath>
+#include <cstdint>
+#include <utility>
+#include <limits>
+
+namespace Catch {
+    namespace Detail {
+
+        uint32_t convertToBits(float f);
+        uint64_t convertToBits(double d);
+
+    } // end namespace Detail
+
+
+    /**
+     * Calculates the ULP distance between two floating point numbers
+     *
+     * The ULP distance of two floating point numbers is the count of
+     * valid floating point numbers representable between them.
+     *
+     * There are some exceptions between how this function counts the
+     * distance, and the interpretation of the standard as implemented.
+     * by e.g. `nextafter`. For this function it always holds that:
+     * * `(x == y) => ulpDistance(x, y) == 0` (so `ulpDistance(-0, 0) == 0`)
+     * * `ulpDistance(maxFinite, INF) == 1`
+     * * `ulpDistance(x, -x) == 2 * ulpDistance(x, 0)`
+     *
+     * \pre `!isnan( lhs )`
+     * \pre `!isnan( rhs )`
+     * \pre floating point numbers are represented in IEEE-754 format
+     */
+    template <typename FP>
+    uint64_t ulpDistance( FP lhs, FP rhs ) {
+        assert( std::numeric_limits<FP>::is_iec559 &&
+            "ulpDistance assumes IEEE-754 format for floating point types" );
+        assert( !Catch::isnan( lhs ) &&
+                "Distance between NaN and number is not meaningful" );
+        assert( !Catch::isnan( rhs ) &&
+                "Distance between NaN and number is not meaningful" );
+
+        // We want X == Y to imply 0 ULP distance even if X and Y aren't
+        // bit-equal (-0 and 0), or X - Y != 0 (same sign infinities).
+        if ( lhs == rhs ) { return 0; }
+
+        // We need a properly typed positive zero for type inference.
+        static constexpr FP positive_zero{};
+
+        // We want to ensure that +/- 0 is always represented as positive zero
+        if ( lhs == positive_zero ) { lhs = positive_zero; }
+        if ( rhs == positive_zero ) { rhs = positive_zero; }
+
+        // If arguments have different signs, we can handle them by summing
+        // how far are they from 0 each.
+        if ( std::signbit( lhs ) != std::signbit( rhs ) ) {
+            return ulpDistance( std::abs( lhs ), positive_zero ) +
+                   ulpDistance( std::abs( rhs ), positive_zero );
+        }
+
+        // When both lhs and rhs are of the same sign, we can just
+        // read the numbers bitwise as integers, and then subtract them
+        // (assuming IEEE).
+        uint64_t lc = Detail::convertToBits( lhs );
+        uint64_t rc = Detail::convertToBits( rhs );
+
+        // The ulp distance between two numbers is symmetric, so to avoid
+        // dealing with overflows we want the bigger converted number on the lhs
+        if ( lc < rc ) {
+            std::swap( lc, rc );
+        }
+
+        return lc - rc;
+    }
+
+} // end namespace Catch
+
+#endif // CATCH_FLOATING_POINT_HELPERS_HPP_INCLUDED
diff --git a/packages/Catch2/src/catch2/internal/catch_list.cpp b/packages/Catch2/src/catch2/internal/catch_list.cpp
index d7969096e372330580a23c482d158be9e690067a..20613c70798b6e3f0e08cd4bfd58e1374a13f205 100644
--- a/packages/Catch2/src/catch2/internal/catch_list.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_list.cpp
@@ -12,6 +12,7 @@
 #include <catch2/interfaces/catch_interfaces_reporter_registry.hpp>
 #include <catch2/interfaces/catch_interfaces_testcase.hpp>
 #include <catch2/interfaces/catch_interfaces_reporter_factory.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <catch2/internal/catch_context.hpp>
 #include <catch2/catch_config.hpp>
@@ -43,7 +44,7 @@ namespace Catch {
 
             std::vector<TagInfo> infos; infos.reserve(tagCounts.size());
             for (auto& tagc : tagCounts) {
-                infos.push_back(std::move(tagc.second));
+                infos.push_back(CATCH_MOVE(tagc.second));
             }
 
             reporter.listTags(infos);
diff --git a/packages/Catch2/src/catch2/internal/catch_main.cpp b/packages/Catch2/src/catch2/internal/catch_main.cpp
index 940ac3bc2d46ba9db03e39ce201364050c3ac434..ed89ab6eed5ca2564faac97d76eee23a7f51767f 100644
--- a/packages/Catch2/src/catch2/internal/catch_main.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_main.cpp
@@ -20,7 +20,7 @@ namespace Catch {
 
 #if defined(CATCH_CONFIG_WCHAR) && defined(CATCH_PLATFORM_WINDOWS) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
 // Standard C/C++ Win32 Unicode wmain entry point
-extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
+extern "C" int __cdecl wmain (int argc, wchar_t * argv[], wchar_t * []) {
 #else
 // Standard C/C++ main entry point
 int main (int argc, char * argv[]) {
diff --git a/packages/Catch2/src/catch2/internal/catch_message_info.hpp b/packages/Catch2/src/catch2/internal/catch_message_info.hpp
index 0c3076eae0069dcbe9d28f30fdcaa598996c17b9..ecd78cabf8b9a95780c5fa7a5cee214d34fd6f56 100644
--- a/packages/Catch2/src/catch2/internal/catch_message_info.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_message_info.hpp
@@ -17,7 +17,7 @@
 namespace Catch {
 
     struct MessageInfo {
-        MessageInfo(    StringRef const& _macroName,
+        MessageInfo(    StringRef _macroName,
                         SourceLineInfo const& _lineInfo,
                         ResultWas::OfType _type );
 
diff --git a/packages/Catch2/src/catch2/internal/catch_move_and_forward.hpp b/packages/Catch2/src/catch2/internal/catch_move_and_forward.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..70520308f1a61459c55ba4b8e1463ce1f217eb25
--- /dev/null
+++ b/packages/Catch2/src/catch2/internal/catch_move_and_forward.hpp
@@ -0,0 +1,19 @@
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
+#define CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
+
+#include <type_traits>
+
+//! TODO: replaces std::move for better performance
+#define CATCH_MOVE(...) static_cast<std::remove_reference_t<decltype(__VA_ARGS__)>&&>(__VA_ARGS__)
+
+#define CATCH_FORWARD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
+
+#endif // CATCH_MOVE_AND_FORWARD_HPP_INCLUDED
+        
\ No newline at end of file
diff --git a/packages/Catch2/src/catch2/internal/catch_platform.hpp b/packages/Catch2/src/catch2/internal/catch_platform.hpp
index 537abce5205c726018831802c8551b196c863b29..8ecf01ad296f1115d18574cfad5b5963a69c80bc 100644
--- a/packages/Catch2/src/catch2/internal/catch_platform.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_platform.hpp
@@ -8,13 +8,16 @@
 #ifndef CATCH_PLATFORM_HPP_INCLUDED
 #define CATCH_PLATFORM_HPP_INCLUDED
 
+// See e.g.:
+// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html
 #ifdef __APPLE__
-# include <TargetConditionals.h>
-# if TARGET_OS_OSX == 1
-#  define CATCH_PLATFORM_MAC
-# elif TARGET_OS_IPHONE == 1
-#  define CATCH_PLATFORM_IPHONE
-# endif
+#  include <TargetConditionals.h>
+#  if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \
+      (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1)
+#    define CATCH_PLATFORM_MAC
+#  elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1)
+#    define CATCH_PLATFORM_IPHONE
+#  endif
 
 #elif defined(linux) || defined(__linux) || defined(__linux__)
 #  define CATCH_PLATFORM_LINUX
diff --git a/packages/Catch2/src/catch2/internal/catch_reporter_registry.cpp b/packages/Catch2/src/catch2/internal/catch_reporter_registry.cpp
index 372a0e3776760a2a714b56a8f4310424013a1b24..ee92ad7de024de325109a43cfbe8534e4533a43d 100644
--- a/packages/Catch2/src/catch2/internal/catch_reporter_registry.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_reporter_registry.cpp
@@ -16,6 +16,7 @@
 #include <catch2/reporters/catch_reporter_tap.hpp>
 #include <catch2/reporters/catch_reporter_teamcity.hpp>
 #include <catch2/reporters/catch_reporter_xml.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
 
@@ -43,10 +44,10 @@ namespace Catch {
     }
 
     void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr factory ) {
-        m_factories.emplace(name, std::move(factory));
+        m_factories.emplace(name, CATCH_MOVE(factory));
     }
     void ReporterRegistry::registerListener( IReporterFactoryPtr factory ) {
-        m_listeners.push_back( std::move(factory) );
+        m_listeners.push_back( CATCH_MOVE(factory) );
     }
 
     IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
diff --git a/packages/Catch2/src/catch2/internal/catch_run_context.cpp b/packages/Catch2/src/catch2/internal/catch_run_context.cpp
index 8379a7aa02ff2a0e3ce97366c0d105a0e1b48f70..b8e74e5f35885e003737e11709cefb22780fb453 100644
--- a/packages/Catch2/src/catch2/internal/catch_run_context.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_run_context.cpp
@@ -50,7 +50,7 @@ namespace Catch {
                 // without it, the code above creates 5 nested generators.
                 if ( currentTracker.nameAndLocation() == nameAndLocation ) {
                     auto thisTracker =
-                        currentTracker.parent().findChild( nameAndLocation );
+                        currentTracker.parent()->findChild( nameAndLocation );
                     assert( thisTracker );
                     assert( thisTracker->isGeneratorTracker() );
                     tracker = static_cast<GeneratorTracker*>( thisTracker );
@@ -64,7 +64,7 @@ namespace Catch {
                         Catch::Detail::make_unique<GeneratorTracker>(
                             nameAndLocation, ctx, &currentTracker );
                     tracker = newTracker.get();
-                    currentTracker.addChild( std::move(newTracker) );
+                    currentTracker.addChild( CATCH_MOVE(newTracker) );
                 }
 
                 if( !tracker->isComplete() ) {
@@ -110,7 +110,7 @@ namespace Catch {
                     // This is safe: there is always at least one section
                     // tracker in a test case tracking tree
                     while ( !parent->isSectionTracker() ) {
-                        parent = &( parent->parent() );
+                        parent = parent->parent();
                     }
                     assert( parent &&
                             "Missing root (test case) level section" );
@@ -153,21 +153,20 @@ namespace Catch {
                 return m_generator;
             }
             void setGenerator( GeneratorBasePtr&& generator ) override {
-                m_generator = std::move( generator );
+                m_generator = CATCH_MOVE( generator );
             }
         };
-        GeneratorTracker::~GeneratorTracker() {}
+        GeneratorTracker::~GeneratorTracker() = default;
     }
 
     RunContext::RunContext(IConfig const* _config, IStreamingReporterPtr&& reporter)
     :   m_runInfo(_config->name()),
         m_context(getCurrentMutableContext()),
         m_config(_config),
-        m_reporter(std::move(reporter)),
+        m_reporter(CATCH_MOVE(reporter)),
         m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
         m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
     {
-        m_context.setRunner(this);
         m_context.setResultCapture(this);
         m_reporter->testRunStarting(m_runInfo);
     }
@@ -176,16 +175,8 @@ namespace Catch {
         m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
     }
 
-    void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) {
-        m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount));
-    }
-
-    void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) {
-        m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting()));
-    }
-
     Totals RunContext::runTest(TestCaseHandle const& testCase) {
-        Totals prevTotals = m_totals;
+        const Totals prevTotals = m_totals;
 
         std::string redirectedCout;
         std::string redirectedCerr;
@@ -200,10 +191,25 @@ namespace Catch {
         ITracker& rootTracker = m_trackerContext.startRun();
         assert(rootTracker.isSectionTracker());
         static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
+
+        uint64_t testRuns = 0;
         do {
             m_trackerContext.startCycle();
             m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
-            runCurrentTest(redirectedCout, redirectedCerr);
+
+            m_reporter->testCasePartialStarting(testInfo, testRuns);
+
+            const auto beforeRunTotals = m_totals;
+            std::string oneRunCout, oneRunCerr;
+            runCurrentTest(oneRunCout, oneRunCerr);
+            redirectedCout += oneRunCout;
+            redirectedCerr += oneRunCerr;
+
+            const auto singleRunTotals = m_totals.delta(beforeRunTotals);
+            auto statsForOneRun = TestCaseStats(testInfo, singleRunTotals, redirectedCout, oneRunCerr, aborting());
+
+            m_reporter->testCasePartialEnded(statsForOneRun, testRuns);
+            ++testRuns;
         } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
 
         Totals deltaTotals = m_totals.delta(prevTotals);
@@ -230,9 +236,11 @@ namespace Catch {
         if (result.getResultType() == ResultWas::Ok) {
             m_totals.assertions.passed++;
             m_lastAssertionPassed = true;
-        } else if (!result.isOk()) {
+        } else if (!result.succeeded()) {
             m_lastAssertionPassed = false;
-            if( m_activeTestCase->getTestCaseInfo().okToFail() )
+            if (result.isOk()) {
+            }
+            else if( m_activeTestCase->getTestCaseInfo().okToFail() )
                 m_totals.assertions.failedButOk++;
             else
                 m_totals.assertions.failed++;
@@ -241,9 +249,7 @@ namespace Catch {
             m_lastAssertionPassed = true;
         }
 
-        // We have no use for the return value (whether messages should be cleared), because messages were made scoped
-        // and should be let to clear themselves out.
-        static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+        m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals));
 
         if (result.getResultType() != ResultWas::Warning)
             m_messageScopes.clear();
@@ -315,7 +321,7 @@ namespace Catch {
         m_unfinishedSections.push_back(endInfo);
     }
 
-    void RunContext::benchmarkPreparing(std::string const& name) {
+    void RunContext::benchmarkPreparing( StringRef name ) {
         m_reporter->benchmarkPreparing(name);
     }
     void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
@@ -324,8 +330,8 @@ namespace Catch {
     void RunContext::benchmarkEnded( BenchmarkStats<> const& stats ) {
         m_reporter->benchmarkEnded( stats );
     }
-    void RunContext::benchmarkFailed(std::string const & error) {
-        m_reporter->benchmarkFailed(error);
+    void RunContext::benchmarkFailed( StringRef error ) {
+        m_reporter->benchmarkFailed( error );
     }
 
     void RunContext::pushScopedMessage(MessageInfo const & message) {
@@ -388,7 +394,6 @@ namespace Catch {
                                   std::string(),
                                   false));
         m_totals.testCases.failed++;
-        testGroupEnded(std::string(), m_totals, 1, 1);
         m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
     }
 
@@ -459,10 +464,10 @@ namespace Catch {
     }
 
     void RunContext::invokeActiveTestCase() {
-        // We need to register a handler for signals/structured exceptions
+        // We need to engage a handler for signals/structured exceptions
         // before running the tests themselves, or the binary can crash
         // without failed test being reported.
-        FatalConditionHandler _;
+        FatalConditionHandlerGuard _(&m_fatalConditionhandler);
         m_activeTestCase->invoke();
     }
 
@@ -518,7 +523,7 @@ namespace Catch {
     void RunContext::handleMessage(
             AssertionInfo const& info,
             ResultWas::OfType resultType,
-            StringRef const& message,
+            StringRef message,
             AssertionReaction& reaction
     ) {
         m_reporter->assertionStarting( info );
diff --git a/packages/Catch2/src/catch2/internal/catch_run_context.hpp b/packages/Catch2/src/catch2/internal/catch_run_context.hpp
index 24e0c99a3ebef042bea6c5f0f20f7c82b1ec605f..7c315e9acc830648f3bc2cd5152629c941e200dc 100644
--- a/packages/Catch2/src/catch2/internal/catch_run_context.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_run_context.hpp
@@ -8,9 +8,9 @@
 #ifndef CATCH_RUN_CONTEXT_HPP_INCLUDED
 #define CATCH_RUN_CONTEXT_HPP_INCLUDED
 
-#include <catch2/interfaces/catch_interfaces_runner.hpp>
 #include <catch2/interfaces/catch_interfaces_reporter.hpp>
 #include <catch2/internal/catch_test_registry.hpp>
+#include <catch2/internal/catch_fatal_condition_handler.hpp>
 #include <catch2/catch_test_case_info.hpp>
 #include <catch2/catch_message.hpp>
 #include <catch2/catch_totals.hpp>
@@ -18,6 +18,7 @@
 #include <catch2/catch_assertion_info.hpp>
 #include <catch2/catch_assertion_result.hpp>
 #include <catch2/internal/catch_option.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <string>
 
@@ -29,7 +30,7 @@ namespace Catch {
 
     ///////////////////////////////////////////////////////////////////////////
 
-    class RunContext : public IResultCapture, public IRunner {
+    class RunContext : public IResultCapture {
 
     public:
         RunContext( RunContext const& ) = delete;
@@ -39,9 +40,6 @@ namespace Catch {
 
         ~RunContext() override;
 
-        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount );
-        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount );
-
         Totals runTest(TestCaseHandle const& testCase);
 
     public: // IResultCapture
@@ -54,7 +52,7 @@ namespace Catch {
         void handleMessage
                 (   AssertionInfo const& info,
                     ResultWas::OfType resultType,
-                    StringRef const& message,
+                    StringRef message,
                     AssertionReaction& reaction ) override;
         void handleUnexpectedExceptionNotThrown
                 (   AssertionInfo const& info,
@@ -77,10 +75,10 @@ namespace Catch {
 
         auto acquireGeneratorTracker( StringRef generatorName, SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
 
-        void benchmarkPreparing( std::string const& name ) override;
+        void benchmarkPreparing( StringRef name ) override;
         void benchmarkStarting( BenchmarkInfo const& info ) override;
         void benchmarkEnded( BenchmarkStats<> const& stats ) override;
-        void benchmarkFailed( std::string const& error ) override;
+        void benchmarkFailed( StringRef error ) override;
 
         void pushScopedMessage( MessageInfo const& message ) override;
         void popScopedMessage( MessageInfo const& message ) override;
@@ -101,7 +99,7 @@ namespace Catch {
 
     public:
         // !TBD We need to do this another way!
-        bool aborting() const override;
+        bool aborting() const;
 
     private:
 
@@ -139,6 +137,7 @@ namespace Catch {
         std::vector<SectionEndInfo> m_unfinishedSections;
         std::vector<ITracker*> m_activeSections;
         TrackerContext m_trackerContext;
+        FatalConditionHandler m_fatalConditionhandler;
         bool m_lastAssertionPassed = false;
         bool m_shouldReportUnexpected = true;
         bool m_includeSuccessfulResults;
diff --git a/packages/Catch2/src/catch2/internal/catch_section.cpp b/packages/Catch2/src/catch2/internal/catch_section.cpp
index 6c13c7dd77094908796d9a11a197606a02e11ae0..8f37e27491545c00ef7cd5da9aaf1935ec5b765e 100644
--- a/packages/Catch2/src/catch2/internal/catch_section.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_section.cpp
@@ -6,15 +6,14 @@
 
 // SPDX-License-Identifier: BSL-1.0
 #include <catch2/internal/catch_section.hpp>
-#include <catch2/internal/catch_test_macro_impl.hpp>
+#include <catch2/internal/catch_run_context.hpp>
 #include <catch2/internal/catch_uncaught_exceptions.hpp>
-
-#include <utility>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
 
     Section::Section( SectionInfo&& info ):
-        m_info( std::move( info ) ),
+        m_info( CATCH_MOVE( info ) ),
         m_sectionIncluded(
             getResultCapture().sectionStarted( m_info, m_assertions ) ) {
         // Non-"included" sections will not use the timing information
diff --git a/packages/Catch2/src/catch2/internal/catch_section.hpp b/packages/Catch2/src/catch2/internal/catch_section.hpp
index 36d900bbeb094bb906da78b467afb755e3aaf751..2e72923c3a1a267752a5b4d6f77565d20a2d6863 100644
--- a/packages/Catch2/src/catch2/internal/catch_section.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_section.hpp
@@ -13,8 +13,7 @@
 #include <catch2/catch_section_info.hpp>
 #include <catch2/catch_timer.hpp>
 #include <catch2/catch_totals.hpp>
-
-#include <string>
+#include <catch2/internal/catch_unique_name.hpp>
 
 namespace Catch {
 
diff --git a/packages/Catch2/src/catch2/internal/catch_singletons.cpp b/packages/Catch2/src/catch2/internal/catch_singletons.cpp
index 36e0e7bdae1ea69951907a89d32974432e8e37a8..116fce4698630727876b03af314f71ae1c268c09 100644
--- a/packages/Catch2/src/catch2/internal/catch_singletons.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_singletons.cpp
@@ -20,7 +20,7 @@ namespace Catch {
         }
     }
 
-    ISingleton::~ISingleton() {}
+    ISingleton::~ISingleton() = default;
 
     void addSingleton(ISingleton* singleton ) {
         getSingletons()->push_back( singleton );
diff --git a/packages/Catch2/src/catch2/internal/catch_singletons.hpp b/packages/Catch2/src/catch2/internal/catch_singletons.hpp
index 0a7f7cccb4974bcb3c6777fdbacd44a0cefd174f..d06d224487eff160b5d30d8c7385381b3b255ef1 100644
--- a/packages/Catch2/src/catch2/internal/catch_singletons.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_singletons.hpp
@@ -11,7 +11,7 @@
 namespace Catch {
 
     struct ISingleton {
-        virtual ~ISingleton();
+        virtual ~ISingleton(); // = default
     };
 
 
diff --git a/packages/Catch2/src/catch2/internal/catch_source_line_info.hpp b/packages/Catch2/src/catch2/internal/catch_source_line_info.hpp
index 8aca4867396e7f22d288d1600eb43cfd3fd742cb..c6b98195e1e4a4bb46ec36da9c6856a832fd2cb6 100644
--- a/packages/Catch2/src/catch2/internal/catch_source_line_info.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_source_line_info.hpp
@@ -8,23 +8,9 @@
 #ifndef CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
 #define CATCH_SOURCE_LINE_INFO_HPP_INCLUDED
 
-#include <catch2/internal/catch_config_counter.hpp>
-
 #include <cstddef>
 #include <iosfwd>
 
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#ifdef CATCH_CONFIG_COUNTER
-#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
-#else
-#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
-#endif
-
-// We need a dummy global operator<< so we can bring it into Catch namespace later
-struct Catch_global_namespace_dummy {};
-std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
-
 namespace Catch {
 
     struct SourceLineInfo {
@@ -43,12 +29,6 @@ namespace Catch {
 
         friend std::ostream& operator << (std::ostream& os, SourceLineInfo const& info);
     };
-
-
-    // Bring in operator<< from global namespace into Catch namespace
-    // This is necessary because the overload of operator<< above makes
-    // lookup stop at namespace Catch
-    using ::operator<<;
 }
 
 #define CATCH_INTERNAL_LINEINFO \
diff --git a/packages/Catch2/src/catch2/internal/catch_stream.cpp b/packages/Catch2/src/catch2/internal/catch_stream.cpp
index 3014256de3d09ff195c7c38296dfd1bea4a20ef0..9890ae3a5df5a8b544eb235d98aa4cfe073039c7 100644
--- a/packages/Catch2/src/catch2/internal/catch_stream.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_stream.cpp
@@ -64,8 +64,10 @@ namespace Detail {
 
         struct OutputDebugWriter {
 
-            void operator()( std::string const&str ) {
-                writeToDebugConsole( str );
+            void operator()( std::string const& str ) {
+                if ( !str.empty() ) {
+                    writeToDebugConsole( str );
+                }
             }
         };
 
@@ -74,7 +76,7 @@ namespace Detail {
         class FileStream : public IStream {
             mutable std::ofstream m_ofs;
         public:
-            FileStream( StringRef filename ) {
+            FileStream( std::string const& filename ) {
                 m_ofs.open( filename.c_str() );
                 CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
             }
@@ -121,17 +123,17 @@ namespace Detail {
 
     ///////////////////////////////////////////////////////////////////////////
 
-    auto makeStream( StringRef const &filename ) -> IStream const* {
+    auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream const> {
         if( filename.empty() )
-            return new Detail::CoutStream();
+            return Detail::make_unique<Detail::CoutStream>();
         else if( filename[0] == '%' ) {
             if( filename == "%debug" )
-                return new Detail::DebugOutStream();
+                return Detail::make_unique<Detail::DebugOutStream>();
             else
                 CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
         }
         else
-            return new Detail::FileStream( filename );
+            return Detail::make_unique<Detail::FileStream>( filename );
     }
 
 
@@ -143,7 +145,7 @@ namespace Detail {
 
         auto add() -> std::size_t {
             if( m_unused.empty() ) {
-                m_streams.push_back( Detail::unique_ptr<std::ostringstream>( new std::ostringstream ) );
+                m_streams.push_back( Detail::make_unique<std::ostringstream>() );
                 return m_streams.size()-1;
             }
             else {
diff --git a/packages/Catch2/src/catch2/internal/catch_stream.hpp b/packages/Catch2/src/catch2/internal/catch_stream.hpp
index 5f24fc38df285f583aa1ee12fbc6e120cd3a5719..39a4c0c8cd7de0abbc3ac1493019341caaa2f3b7 100644
--- a/packages/Catch2/src/catch2/internal/catch_stream.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_stream.hpp
@@ -9,10 +9,12 @@
 #define CATCH_STREAM_HPP_INCLUDED
 
 #include <catch2/internal/catch_noncopyable.hpp>
+#include <catch2/internal/catch_unique_ptr.hpp>
 
 #include <iosfwd>
 #include <cstddef>
 #include <ostream>
+#include <string>
 
 namespace Catch {
 
@@ -20,14 +22,12 @@ namespace Catch {
     std::ostream& cerr();
     std::ostream& clog();
 
-    class StringRef;
-
     struct IStream {
-        virtual ~IStream();
+        virtual ~IStream(); // = default
         virtual std::ostream& stream() const = 0;
     };
 
-    auto makeStream( StringRef const &filename ) -> IStream const*;
+    auto makeStream( std::string const& filename ) -> Detail::unique_ptr<IStream const>;
 
     class ReusableStringStream : Detail::NonCopyable {
         std::size_t m_index;
diff --git a/packages/Catch2/src/catch2/internal/catch_string_manip.cpp b/packages/Catch2/src/catch2/internal/catch_string_manip.cpp
index df6b6c3485be8f4d6f5366a49e838945cafcd9e9..0444cd38ed6b958051b60953629d65c2bb0533c1 100644
--- a/packages/Catch2/src/catch2/internal/catch_string_manip.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_string_manip.cpp
@@ -94,11 +94,6 @@ namespace Catch {
         return subStrings;
     }
 
-    pluralise::pluralise( std::size_t count, std::string const& label )
-    :   m_count( count ),
-        m_label( label )
-    {}
-
     std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
         os << pluraliser.m_count << ' ' << pluraliser.m_label;
         if( pluraliser.m_count != 1 )
diff --git a/packages/Catch2/src/catch2/internal/catch_string_manip.hpp b/packages/Catch2/src/catch2/internal/catch_string_manip.hpp
index d4a2fbafe5c18818017408e19bfbc32c5db24f62..bd7d0ddf40fa4a0cc3f3af5e54303120faaad5f6 100644
--- a/packages/Catch2/src/catch2/internal/catch_string_manip.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_string_manip.hpp
@@ -32,13 +32,26 @@ namespace Catch {
     std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
     bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
 
+    /**
+     * Helper for streaming a "count [maybe-plural-of-label]" human-friendly string
+     *
+     * Usage example:
+     * ```cpp
+     * std::cout << "Found " << pluralise(count, "error") << '\n';
+     * ```
+     *
+     * **Important:** The provided string must outlive the instance
+     */
     struct pluralise {
-        pluralise( std::size_t count, std::string const& label );
+        pluralise(std::size_t count, StringRef label):
+            m_count(count),
+            m_label(label)
+        {}
 
         friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
 
         std::size_t m_count;
-        std::string m_label;
+        StringRef m_label;
     };
 }
 
diff --git a/packages/Catch2/src/catch2/internal/catch_stringref.cpp b/packages/Catch2/src/catch2/internal/catch_stringref.cpp
index a318857cadd6ab14f7220c0e5bc1991ddd8836ae..36a6d2466d83845ce44e8e57823216d607b832fe 100644
--- a/packages/Catch2/src/catch2/internal/catch_stringref.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_stringref.cpp
@@ -5,7 +5,6 @@
 //        https://www.boost.org/LICENSE_1_0.txt)
 
 // SPDX-License-Identifier: BSL-1.0
-#include <catch2/internal/catch_enforce.hpp>
 #include <catch2/internal/catch_stringref.hpp>
 
 #include <algorithm>
@@ -18,11 +17,6 @@ namespace Catch {
     : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
     {}
 
-    auto StringRef::c_str() const -> char const* {
-        CATCH_ENFORCE(isNullTerminated(), "Called StringRef::c_str() on a non-null-terminated instance");
-        return m_start;
-    }
-
     auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
         return m_size == other.m_size
             && (std::memcmp( m_start, other.m_start, m_size ) == 0);
diff --git a/packages/Catch2/src/catch2/internal/catch_stringref.hpp b/packages/Catch2/src/catch2/internal/catch_stringref.hpp
index 33b35ef6c682e65daffc78d1d65559db506c7aea..17f5d07dab0312218743f42cf4d814421294ab89 100644
--- a/packages/Catch2/src/catch2/internal/catch_stringref.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_stringref.hpp
@@ -69,10 +69,6 @@ namespace Catch {
             return m_size;
         }
 
-        // Returns the current start pointer. If the StringRef is not
-        // null-terminated, throws std::domain_exception
-        auto c_str() const -> char const*;
-
     public: // substrings and searches
         // Returns a substring of [start, start + length).
         // If start + length > size(), then the substring is [start, start + size()).
@@ -91,10 +87,6 @@ namespace Catch {
             return m_start;
         }
 
-        constexpr auto isNullTerminated() const noexcept -> bool {
-            return m_start[m_size] == '\0';
-        }
-
     public: // iterators
         constexpr const_iterator begin() const { return m_start; }
         constexpr const_iterator end() const { return m_start + m_size; }
@@ -106,12 +98,12 @@ namespace Catch {
     };
 
 
-    constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
+    constexpr auto operator ""_sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
         return StringRef( rawChars, size );
     }
 } // namespace Catch
 
-constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
+constexpr auto operator ""_catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
     return Catch::StringRef( rawChars, size );
 }
 
diff --git a/packages/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp b/packages/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp
index 5df3c02ba8988d2b6850bd6a41987869d86b8977..8c3e339f6761894e0d272c1f18512f534ab78457 100644
--- a/packages/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_tag_alias_registry.cpp
@@ -46,7 +46,7 @@ namespace Catch {
                       << "\tRedefined at: " << lineInfo );
     }
 
-    ITagAliasRegistry::~ITagAliasRegistry() {}
+    ITagAliasRegistry::~ITagAliasRegistry() = default;
 
     ITagAliasRegistry const& ITagAliasRegistry::get() {
         return getRegistryHub().getTagAliasRegistry();
diff --git a/packages/Catch2/src/catch2/internal/catch_template_test_registry.hpp b/packages/Catch2/src/catch2/internal/catch_template_test_registry.hpp
index 3617109b3eb18fcb0c31abea5c68eaa895cf7851..1099f9db890726804aba573aac6bf99a6eb5a895 100644
--- a/packages/Catch2/src/catch2/internal/catch_template_test_registry.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_template_test_registry.hpp
@@ -12,6 +12,8 @@
 #include <catch2/internal/catch_compiler_capabilities.hpp>
 #include <catch2/internal/catch_preprocessor.hpp>
 #include <catch2/internal/catch_meta.hpp>
+#include <catch2/internal/catch_unique_name.hpp>
+
 
 // GCC 5 and older do not properly handle disabling unused-variable warning
 // with a _Pragma. This means that we have to leak the suppression to the
@@ -33,34 +35,34 @@
 
     #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ )
     #else
         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
     #endif
 
     #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ )
     #else
         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ ) )
     #endif
 
     #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
     #else
         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
     #endif
 
     #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
     #else
         #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
     #endif
 #endif
 
@@ -98,18 +100,18 @@
 
 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ )
 #else
     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
 #endif
 
 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ )
 #else
     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ ) )
 #endif
 
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
@@ -148,18 +150,18 @@
 
 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T,__VA_ARGS__)
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename T,__VA_ARGS__)
 #else
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T, __VA_ARGS__ ) )
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, typename T, __VA_ARGS__ ) )
 #endif
 
 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__)
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__)
 #else
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, Signature, __VA_ARGS__ ) )
 #endif
 
     #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2(TestName, TestFunc, Name, Tags, TmplList)\
@@ -191,7 +193,7 @@
         static void TestFunc()
 
     #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE(Name, Tags, TmplList) \
-        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, TmplList )
+        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), Name, Tags, TmplList )
 
 
     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
@@ -226,18 +228,18 @@
 
 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
 #else
     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
 #endif
 
 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
+        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
 #else
     #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_CLASS_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
 #endif
 
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
@@ -279,18 +281,18 @@
 
 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
 #else
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
 #endif
 
 #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
+        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
 #else
     #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
+        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
 #endif
 
     #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, TmplList) \
@@ -325,7 +327,7 @@
         void TestName<TestType>::test()
 
 #define INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD(ClassName, Name, Tags, TmplList) \
-        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, TmplList )
+        INTERNAL_CATCH_TEMPLATE_LIST_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEMPLATE_TEST_ ), ClassName, Name, Tags, TmplList )
 
 
 #endif // CATCH_TEMPLATE_TEST_REGISTRY_HPP_INCLUDED
diff --git a/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.cpp b/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.cpp
index 7f7f101d18ad7079d1bc73b54c039eda33f20cdb..dd1744a7072f699e263ddef58f85d0dac32e8a49 100644
--- a/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.cpp
@@ -12,9 +12,9 @@
 #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
 #include <catch2/internal/catch_random_number_generator.hpp>
 #include <catch2/internal/catch_run_context.hpp>
-#include <catch2/internal/catch_string_manip.hpp>
 #include <catch2/catch_test_case_info.hpp>
 #include <catch2/catch_test_spec.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <algorithm>
 #include <set>
@@ -22,24 +22,27 @@
 namespace Catch {
 
 namespace {
-    struct HashTest {
-        explicit HashTest(SimplePcg32& rng_inst) {
-            basis = rng_inst();
-            basis <<= 32;
-            basis |= rng_inst();
-        }
+    struct TestHasher {
+        using hash_t = uint64_t;
+
+        explicit TestHasher( hash_t hashSuffix ):
+            m_hashSuffix( hashSuffix ) {}
 
-        uint64_t basis;
+        uint64_t m_hashSuffix;
 
-        uint64_t operator()(TestCaseInfo const& t) const {
-            // Modified FNV-1a hash
-            static constexpr uint64_t prime = 1099511628211;
-            uint64_t hash = basis;
+        uint32_t operator()( TestCaseInfo const& t ) const {
+            // FNV-1a hash with multiplication fold.
+            const hash_t prime = 1099511628211u;
+            hash_t hash = 14695981039346656037u;
             for (const char c : t.name) {
                 hash ^= c;
                 hash *= prime;
             }
-            return hash;
+            hash ^= m_hashSuffix;
+            hash *= prime;
+            const uint32_t low{ static_cast<uint32_t>(hash) };
+            const uint32_t high{ static_cast<uint32_t>(hash >> 32) };
+            return low * high;
         }
     };
 } // end anonymous namespace
@@ -51,20 +54,36 @@ namespace {
 
         case TestRunOrder::LexicographicallySorted: {
             std::vector<TestCaseHandle> sorted = unsortedTestCases;
-            std::sort(sorted.begin(), sorted.end());
+            std::sort(
+                sorted.begin(),
+                sorted.end(),
+                []( TestCaseHandle const& lhs, TestCaseHandle const& rhs ) {
+                    return lhs.getTestCaseInfo() < rhs.getTestCaseInfo();
+                }
+            );
             return sorted;
         }
         case TestRunOrder::Randomized: {
             seedRng(config);
-            HashTest h(rng());
-            std::vector<std::pair<uint64_t, TestCaseHandle>> indexed_tests;
+            using TestWithHash = std::pair<TestHasher::hash_t, TestCaseHandle>;
+
+            TestHasher h{ config.rngSeed() };
+            std::vector<TestWithHash> indexed_tests;
             indexed_tests.reserve(unsortedTestCases.size());
 
             for (auto const& handle : unsortedTestCases) {
                 indexed_tests.emplace_back(h(handle.getTestCaseInfo()), handle);
             }
 
-            std::sort(indexed_tests.begin(), indexed_tests.end());
+            std::sort( indexed_tests.begin(),
+                       indexed_tests.end(),
+                       []( TestWithHash const& lhs, TestWithHash const& rhs ) {
+                           if ( lhs.first == rhs.first ) {
+                               return lhs.second.getTestCaseInfo() <
+                                      rhs.second.getTestCaseInfo();
+                           }
+                           return lhs.first < rhs.first;
+                       } );
 
             std::vector<TestCaseHandle> randomized;
             randomized.reserve(indexed_tests.size());
@@ -88,14 +107,22 @@ namespace {
         return testSpec.matches( testCase.getTestCaseInfo() ) && isThrowSafe( testCase, config );
     }
 
-    void enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& functions ) {
-        std::set<TestCaseHandle> seenFunctions;
-        for( auto const& function : functions ) {
-            auto prev = seenFunctions.insert( function );
-            CATCH_ENFORCE( prev.second,
-                    "error: TEST_CASE( \"" << function.getTestCaseInfo().name << "\" ) already defined.\n"
-                    << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
-                    << "\tRedefined at " << function.getTestCaseInfo().lineInfo );
+    void
+    enforceNoDuplicateTestCases( std::vector<TestCaseHandle> const& tests ) {
+        auto testInfoCmp = []( TestCaseInfo const* lhs,
+                               TestCaseInfo const* rhs ) {
+            return *lhs < *rhs;
+        };
+        std::set<TestCaseInfo const*, decltype(testInfoCmp)> seenTests(testInfoCmp);
+        for ( auto const& test : tests ) {
+            const auto infoPtr = &test.getTestCaseInfo();
+            const auto prev = seenTests.insert( infoPtr );
+            CATCH_ENFORCE(
+                prev.second,
+                "error: test case \"" << infoPtr->name << "\", with tags \""
+                    << infoPtr->tagsAsString() << "\" already defined.\n"
+                    << "\tFirst seen at " << ( *prev.first )->lineInfo << "\n"
+                    << "\tRedefined at " << infoPtr->lineInfo );
         }
     }
 
@@ -117,8 +144,8 @@ namespace {
     void TestRegistry::registerTest(Detail::unique_ptr<TestCaseInfo> testInfo, Detail::unique_ptr<ITestInvoker> testInvoker) {
         m_handles.emplace_back(testInfo.get(), testInvoker.get());
         m_viewed_test_infos.push_back(testInfo.get());
-        m_owned_test_infos.push_back(std::move(testInfo));
-        m_invokers.push_back(std::move(testInvoker));
+        m_owned_test_infos.push_back(CATCH_MOVE(testInfo));
+        m_invokers.push_back(CATCH_MOVE(testInvoker));
     }
 
     std::vector<TestCaseInfo*> const& TestRegistry::getAllInfos() const {
@@ -146,17 +173,4 @@ namespace {
         m_testAsFunction();
     }
 
-    std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
-        std::string className(classOrQualifiedMethodName);
-        if( startsWith( className, '&' ) )
-        {
-            std::size_t lastColons = className.rfind( "::" );
-            std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
-            if( penultimateColons == std::string::npos )
-                penultimateColons = 1;
-            className = className.substr( penultimateColons, lastColons-penultimateColons );
-        }
-        return className;
-    }
-
 } // end namespace Catch
diff --git a/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp b/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp
index f7fc37ef3a32a252d35e2fd03655a5fdfc24a6da..60a8f9bdc5af5b9af129b63d11686282265552d6 100644
--- a/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_test_case_registry_impl.hpp
@@ -63,9 +63,6 @@ namespace Catch {
         void invoke() const override;
     };
 
-
-    std::string extractClassName( StringRef const& classOrQualifiedMethodName );
-
     ///////////////////////////////////////////////////////////////////////////
 
 
diff --git a/packages/Catch2/src/catch2/internal/catch_test_case_tracker.cpp b/packages/Catch2/src/catch2/internal/catch_test_case_tracker.cpp
index 2e8c3169f61055c384b9e7d2ce71bc840931be4c..ce76dcba89287acaf63e6cc712c00f11c7439baf 100644
--- a/packages/Catch2/src/catch2/internal/catch_test_case_tracker.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_test_case_tracker.cpp
@@ -9,6 +9,7 @@
 
 #include <catch2/internal/catch_enforce.hpp>
 #include <catch2/internal/catch_string_manip.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <algorithm>
 #include <cassert>
@@ -29,8 +30,12 @@ namespace TestCaseTracking {
 
     ITracker::~ITracker() = default;
 
+    void ITracker::markAsNeedingAnotherRun() {
+        m_runState = NeedsAnotherRun;
+    }
+
     void ITracker::addChild( ITrackerPtr&& child ) {
-        m_children.push_back( std::move(child) );
+        m_children.push_back( CATCH_MOVE(child) );
     }
 
     ITracker* ITracker::findChild( NameAndLocation const& nameAndLocation ) {
@@ -45,7 +50,27 @@ namespace TestCaseTracking {
         return ( it != m_children.end() ) ? it->get() : nullptr;
     }
 
+    bool ITracker::isSectionTracker() const { return false; }
+    bool ITracker::isGeneratorTracker() const { return false; }
+
+    bool ITracker::isSuccessfullyCompleted() const {
+        return m_runState == CompletedSuccessfully;
+    }
 
+    bool ITracker::isOpen() const {
+        return m_runState != NotStarted && !isComplete();
+    }
+
+    bool ITracker::hasStarted() const { return m_runState != NotStarted; }
+
+    void ITracker::openChild() {
+        if (m_runState != ExecutingChildren) {
+            m_runState = ExecutingChildren;
+            if (m_parent) {
+                m_parent->openChild();
+            }
+        }
+    }
 
     ITracker& TrackerContext::startRun() {
         using namespace std::string_literals;
@@ -84,36 +109,13 @@ namespace TestCaseTracking {
 
 
     TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent ):
-        ITracker(nameAndLocation),
-        m_ctx( ctx ),
-        m_parent( parent )
+        ITracker(nameAndLocation, parent),
+        m_ctx( ctx )
     {}
 
     bool TrackerBase::isComplete() const {
         return m_runState == CompletedSuccessfully || m_runState == Failed;
     }
-    bool TrackerBase::isSuccessfullyCompleted() const {
-        return m_runState == CompletedSuccessfully;
-    }
-    bool TrackerBase::isOpen() const {
-        return m_runState != NotStarted && !isComplete();
-    }
-
-    ITracker& TrackerBase::parent() {
-        assert( m_parent ); // Should always be non-null except for root
-        return *m_parent;
-    }
-
-    void TrackerBase::openChild() {
-        if( m_runState != ExecutingChildren ) {
-            m_runState = ExecutingChildren;
-            if( m_parent )
-                m_parent->openChild();
-        }
-    }
-
-    bool TrackerBase::isSectionTracker() const { return false; }
-    bool TrackerBase::isGeneratorTracker() const { return false; }
 
     void TrackerBase::open() {
         m_runState = Executing;
@@ -158,9 +160,6 @@ namespace TestCaseTracking {
         moveToParent();
         m_ctx.completeCycle();
     }
-    void TrackerBase::markAsNeedingAnotherRun() {
-        m_runState = NeedsAnotherRun;
-    }
 
     void TrackerBase::moveToParent() {
         assert( m_parent );
@@ -176,7 +175,7 @@ namespace TestCaseTracking {
     {
         if( parent ) {
             while( !parent->isSectionTracker() )
-                parent = &parent->parent();
+                parent = parent->parent();
 
             SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
             addNextFilters( parentSection.m_filters );
@@ -187,7 +186,7 @@ namespace TestCaseTracking {
         bool complete = true;
 
         if (m_filters.empty()
-            || m_filters[0] == ""
+            || m_filters[0].empty()
             || std::find(m_filters.begin(), m_filters.end(), m_trimmed_name) != m_filters.end()) {
             complete = TrackerBase::isComplete();
         }
@@ -209,7 +208,7 @@ namespace TestCaseTracking {
             auto newSection = Catch::Detail::make_unique<SectionTracker>(
                 nameAndLocation, ctx, &currentTracker );
             section = newSection.get();
-            currentTracker.addChild( std::move( newSection ) );
+            currentTracker.addChild( CATCH_MOVE( newSection ) );
         }
         if( !ctx.completedCycle() )
             section->tryOpen();
@@ -224,30 +223,26 @@ namespace TestCaseTracking {
     void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
         if( !filters.empty() ) {
             m_filters.reserve( m_filters.size() + filters.size() + 2 );
-            m_filters.emplace_back(""); // Root - should never be consulted
-            m_filters.emplace_back(""); // Test Case - not a section filter
+            m_filters.emplace_back(StringRef{}); // Root - should never be consulted
+            m_filters.emplace_back(StringRef{}); // Test Case - not a section filter
             m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
         }
     }
-    void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
+    void SectionTracker::addNextFilters( std::vector<StringRef> const& filters ) {
         if( filters.size() > 1 )
             m_filters.insert( m_filters.end(), filters.begin()+1, filters.end() );
     }
 
-    std::vector<std::string> const& SectionTracker::getFilters() const {
+    std::vector<StringRef> const& SectionTracker::getFilters() const {
         return m_filters;
     }
 
-    std::string const& SectionTracker::trimmedName() const {
+    StringRef SectionTracker::trimmedName() const {
         return m_trimmed_name;
     }
 
 } // namespace TestCaseTracking
 
-using TestCaseTracking::ITracker;
-using TestCaseTracking::TrackerContext;
-using TestCaseTracking::SectionTracker;
-
 } // namespace Catch
 
 #if defined(__clang__)
diff --git a/packages/Catch2/src/catch2/internal/catch_test_case_tracker.hpp b/packages/Catch2/src/catch2/internal/catch_test_case_tracker.hpp
index c3ed1ae0950bb88402d7b44c36e97d03b0625625..0355f195cd006f83fbf712b4c935c87dea2e958e 100644
--- a/packages/Catch2/src/catch2/internal/catch_test_case_tracker.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_test_case_tracker.hpp
@@ -10,6 +10,7 @@
 
 #include <catch2/internal/catch_source_line_info.hpp>
 #include <catch2/internal/catch_unique_ptr.hpp>
+#include <catch2/internal/catch_stringref.hpp>
 
 #include <string>
 #include <vector>
@@ -32,17 +33,29 @@ namespace TestCaseTracking {
 
     using ITrackerPtr = Catch::Detail::unique_ptr<ITracker>;
 
-    class  ITracker {
+    class ITracker {
         NameAndLocation m_nameAndLocation;
 
         using Children = std::vector<ITrackerPtr>;
 
     protected:
+        enum CycleState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            NeedsAnotherRun,
+            CompletedSuccessfully,
+            Failed
+        };
+
+        ITracker* m_parent = nullptr;
         Children m_children;
+        CycleState m_runState = NotStarted;
 
     public:
-        ITracker(NameAndLocation const& nameAndLoc) :
-            m_nameAndLocation(nameAndLoc)
+        ITracker( NameAndLocation const& nameAndLoc, ITracker* parent ):
+            m_nameAndLocation( nameAndLoc ),
+            m_parent( parent )
         {}
 
 
@@ -50,22 +63,28 @@ namespace TestCaseTracking {
         NameAndLocation const& nameAndLocation() const {
             return m_nameAndLocation;
         }
+        ITracker* parent() const {
+            return m_parent;
+        }
 
-        virtual ~ITracker();
+        virtual ~ITracker(); // = default
 
 
         // dynamic queries
-        virtual bool isComplete() const = 0; // Successfully completed or failed
-        virtual bool isSuccessfullyCompleted() const = 0;
-        virtual bool isOpen() const = 0; // Started but not complete
-        virtual bool hasStarted() const = 0;
 
-        virtual ITracker& parent() = 0;
+        //! Returns true if tracker run to completion (successfully or not)
+        virtual bool isComplete() const = 0;
+        //! Returns true if tracker run to completion succesfully
+        bool isSuccessfullyCompleted() const;
+        //! Returns true if tracker has started but hasn't been completed
+        bool isOpen() const;
+        //! Returns true iff tracker has started
+        bool hasStarted() const;
 
         // actions
         virtual void close() = 0; // Successfully complete
         virtual void fail() = 0;
-        virtual void markAsNeedingAnotherRun() = 0;
+        void markAsNeedingAnotherRun();
 
         //! Register a nested ITracker
         void addChild( ITrackerPtr&& child );
@@ -81,11 +100,23 @@ namespace TestCaseTracking {
         }
 
 
-        virtual void openChild() = 0;
+        //! Marks tracker as executing a child, doing se recursively up the tree
+        void openChild();
 
-        // Debug/ checking
-        virtual bool isSectionTracker() const = 0;
-        virtual bool isGeneratorTracker() const = 0;
+        /**
+         * Returns true if the instance is a section tracker
+         *
+         * Subclasses should override to true if they are, replaces RTTI
+         * for internal debug checks.
+         */
+        virtual bool isSectionTracker() const;
+        /**
+         * Returns true if the instance is a generator tracker
+         *
+         * Subclasses should override to true if they are, replaces RTTI
+         * for internal debug checks.
+         */
+        virtual bool isGeneratorTracker() const;
     };
 
     class TrackerContext {
@@ -115,41 +146,18 @@ namespace TestCaseTracking {
 
     class TrackerBase : public ITracker {
     protected:
-        enum CycleState {
-            NotStarted,
-            Executing,
-            ExecutingChildren,
-            NeedsAnotherRun,
-            CompletedSuccessfully,
-            Failed
-        };
 
         TrackerContext& m_ctx;
-        ITracker* m_parent;
-        CycleState m_runState = NotStarted;
 
     public:
         TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
 
         bool isComplete() const override;
-        bool isSuccessfullyCompleted() const override;
-        bool isOpen() const override;
-        bool hasStarted() const override {
-            return m_runState != NotStarted;
-        }
-
-        ITracker& parent() override;
-
-        void openChild() override;
-
-        bool isSectionTracker() const override;
-        bool isGeneratorTracker() const override;
 
         void open();
 
         void close() override;
         void fail() override;
-        void markAsNeedingAnotherRun() override;
 
     private:
         void moveToParent();
@@ -157,7 +165,7 @@ namespace TestCaseTracking {
     };
 
     class SectionTracker : public TrackerBase {
-        std::vector<std::string> m_filters;
+        std::vector<StringRef> m_filters;
         std::string m_trimmed_name;
     public:
         SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
@@ -171,11 +179,11 @@ namespace TestCaseTracking {
         void tryOpen();
 
         void addInitialFilters( std::vector<std::string> const& filters );
-        void addNextFilters( std::vector<std::string> const& filters );
+        void addNextFilters( std::vector<StringRef> const& filters );
         //! Returns filters active in this tracker
-        std::vector<std::string> const& getFilters() const;
+        std::vector<StringRef> const& getFilters() const;
         //! Returns whitespace-trimmed name of the tracked section
-        std::string const& trimmedName() const;
+        StringRef trimmedName() const;
     };
 
 } // namespace TestCaseTracking
diff --git a/packages/Catch2/src/catch2/internal/catch_test_macro_impl.hpp b/packages/Catch2/src/catch2/internal/catch_test_macro_impl.hpp
index 9ceb03ab096985c960ca170673cf3a82f01e8d23..357e114dd25a4ddf1bd4bd852cea4fbce6340f59 100644
--- a/packages/Catch2/src/catch2/internal/catch_test_macro_impl.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_test_macro_impl.hpp
@@ -11,6 +11,7 @@
 #include <catch2/internal/catch_assertion_handler.hpp>
 #include <catch2/interfaces/catch_interfaces_capture.hpp>
 #include <catch2/internal/catch_stringref.hpp>
+#include <catch2/internal/catch_source_line_info.hpp>
 
 // We need this suppression to leak, because it took until GCC 9
 // for the front end to handle local suppression via _Pragma properly
diff --git a/packages/Catch2/src/catch2/internal/catch_test_registry.cpp b/packages/Catch2/src/catch2/internal/catch_test_registry.cpp
index c4f26181b0497d0be4a57f5cc36ead093ccd11bc..d33ba27abeb77ae15b59fb6b3f7bb264f2c7fffb 100644
--- a/packages/Catch2/src/catch2/internal/catch_test_registry.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_test_registry.cpp
@@ -10,14 +10,32 @@
 #include <catch2/catch_test_case_info.hpp>
 #include <catch2/internal/catch_test_case_registry_impl.hpp>
 #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
+#include <catch2/internal/catch_string_manip.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
 
+    namespace {
+        std::string extractClassName( StringRef classOrQualifiedMethodName ) {
+            std::string className( classOrQualifiedMethodName );
+            if ( startsWith( className, '&' ) ) {
+                std::size_t lastColons = className.rfind( "::" );
+                std::size_t penultimateColons =
+                    className.rfind( "::", lastColons - 1 );
+                if ( penultimateColons == std::string::npos )
+                    penultimateColons = 1;
+                className = className.substr( penultimateColons,
+                                              lastColons - penultimateColons );
+            }
+            return className;
+        }
+    } // namespace
+
     Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() ) {
-        return Detail::unique_ptr<ITestInvoker>( new TestInvokerAsFunction( testAsFunction ));
+        return Detail::make_unique<TestInvokerAsFunction>( testAsFunction );
     }
 
-    AutoReg::AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept {
+    AutoReg::AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept {
         CATCH_TRY {
             getMutableRegistryHub()
                     .registerTest(
@@ -25,7 +43,7 @@ namespace Catch {
                             extractClassName( classOrMethod ),
                             nameAndTags,
                             lineInfo),
-                        std::move(invoker)
+                        CATCH_MOVE(invoker)
                     );
         } CATCH_CATCH_ALL {
             // Do not throw when constructing global objects, instead register the exception to be processed later
diff --git a/packages/Catch2/src/catch2/internal/catch_test_registry.hpp b/packages/Catch2/src/catch2/internal/catch_test_registry.hpp
index 0f5ec48837bc407d4110a868e36e9f02badda5be..9a03e6dbfa4043508ada321f043e139b9c636c9c 100644
--- a/packages/Catch2/src/catch2/internal/catch_test_registry.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_test_registry.hpp
@@ -13,6 +13,7 @@
 #include <catch2/interfaces/catch_interfaces_testcase.hpp>
 #include <catch2/internal/catch_stringref.hpp>
 #include <catch2/internal/catch_unique_ptr.hpp>
+#include <catch2/internal/catch_unique_name.hpp>
 
 // GCC 5 and older do not properly handle disabling unused-variable warning
 // with a _Pragma. This means that we have to leak the suppression to the
@@ -41,19 +42,19 @@ Detail::unique_ptr<ITestInvoker> makeTestInvoker( void(*testAsFunction)() );
 
 template<typename C>
 Detail::unique_ptr<ITestInvoker> makeTestInvoker( void (C::*testAsMethod)() ) {
-    return Detail::unique_ptr<ITestInvoker>( new TestInvokerAsMethod<C>(testAsMethod) );
+    return Detail::make_unique<TestInvokerAsMethod<C>>( testAsMethod );
 }
 
 struct NameAndTags {
-    NameAndTags(StringRef const& name_ = StringRef(),
-                StringRef const& tags_ = StringRef()) noexcept:
-        name(name_), tags(tags_) {}
+    constexpr NameAndTags( StringRef name_ = StringRef(),
+                           StringRef tags_ = StringRef() ) noexcept:
+        name( name_ ), tags( tags_ ) {}
     StringRef name;
     StringRef tags;
 };
 
 struct AutoReg : Detail::NonCopyable {
-    AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
+    AutoReg( Detail::unique_ptr<ITestInvoker> invoker, SourceLineInfo const& lineInfo, StringRef classOrMethod, NameAndTags const& nameAndTags ) noexcept;
 };
 
 } // end namespace Catch
@@ -79,7 +80,7 @@ struct AutoReg : Detail::NonCopyable {
         CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
         static void TestName()
     #define INTERNAL_CATCH_TESTCASE( ... ) \
-        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), __VA_ARGS__ )
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
@@ -101,7 +102,7 @@ struct AutoReg : Detail::NonCopyable {
         CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION \
         void TestName::test()
     #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
-        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( CATCH2_INTERNAL_TEST_ ), ClassName, __VA_ARGS__ )
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
diff --git a/packages/Catch2/src/catch2/internal/catch_test_spec_parser.cpp b/packages/Catch2/src/catch2/internal/catch_test_spec_parser.cpp
index e9d77e74fc98a9a1aba0ee3d0599a3d4613ce8c9..86b25bfa607036fe928081fc2fb7b57611c35c5d 100644
--- a/packages/Catch2/src/catch2/internal/catch_test_spec_parser.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_test_spec_parser.cpp
@@ -9,6 +9,7 @@
 
 #include <catch2/internal/catch_string_manip.hpp>
 #include <catch2/interfaces/catch_interfaces_tag_alias_registry.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 
 namespace Catch {
@@ -35,7 +36,7 @@ namespace Catch {
     }
     TestSpec TestSpecParser::testSpec() {
         addFilter();
-        return std::move(m_testSpec);
+        return CATCH_MOVE(m_testSpec);
     }
     bool TestSpecParser::visitChar( char c ) {
         if( (m_mode != EscapedName) && (c == '\\') ) {
@@ -151,7 +152,7 @@ namespace Catch {
 
     void TestSpecParser::addFilter() {
         if( !m_currentFilter.m_required.empty() || !m_currentFilter.m_forbidden.empty() ) {
-            m_testSpec.m_filters.push_back( std::move(m_currentFilter) );
+            m_testSpec.m_filters.push_back( CATCH_MOVE(m_currentFilter) );
             m_currentFilter = TestSpec::Filter();
         }
     }
diff --git a/packages/Catch2/src/catch2/internal/catch_unique_name.hpp b/packages/Catch2/src/catch2/internal/catch_unique_name.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..31ab91268416035c74ec0a1b66ff1a2625ac6f23
--- /dev/null
+++ b/packages/Catch2/src/catch2/internal/catch_unique_name.hpp
@@ -0,0 +1,20 @@
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+#ifndef CATCH_UNIQUE_NAME_HPP_INCLUDED
+#define CATCH_UNIQUE_NAME_HPP_INCLUDED
+
+#include <catch2/internal/catch_config_counter.hpp>
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#endif // CATCH_UNIQUE_NAME_HPP_INCLUDED
diff --git a/packages/Catch2/src/catch2/internal/catch_unique_ptr.hpp b/packages/Catch2/src/catch2/internal/catch_unique_ptr.hpp
index 758b22da63045a7572064ee3f2de85fa961ba031..d3bc8417de3a9435bf257ba9b1ad7da3630b1c06 100644
--- a/packages/Catch2/src/catch2/internal/catch_unique_ptr.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_unique_ptr.hpp
@@ -64,7 +64,11 @@ namespace Detail {
             assert(m_ptr);
             return *m_ptr;
         }
-        T* operator->() const noexcept {
+        T* operator->() noexcept {
+            assert(m_ptr);
+            return m_ptr;
+        }
+        T const* operator->() const noexcept {
             assert(m_ptr);
             return m_ptr;
         }
diff --git a/packages/Catch2/src/catch2/internal/catch_xmlwriter.cpp b/packages/Catch2/src/catch2/internal/catch_xmlwriter.cpp
index f69256cf4ef90a9d059f67dd9fdd4d4751196a8e..b8635f7f2bd2760e3662931e6ce7e2badcfbf80e 100644
--- a/packages/Catch2/src/catch2/internal/catch_xmlwriter.cpp
+++ b/packages/Catch2/src/catch2/internal/catch_xmlwriter.cpp
@@ -5,9 +5,11 @@
 //        https://www.boost.org/LICENSE_1_0.txt)
 
 // SPDX-License-Identifier: BSL-1.0
-#include <catch2/internal/catch_xmlwriter.hpp>
-
+// Note: swapping these two includes around causes MSVC to error out
+//       while in /permissive- mode. No, I don't know why.
+//       Tested on VS 2019, 18.{3, 4}.x
 #include <catch2/internal/catch_enforce.hpp>
+#include <catch2/internal/catch_xmlwriter.hpp>
 
 #include <iomanip>
 #include <type_traits>
@@ -75,7 +77,7 @@ namespace {
     }
 
 
-    XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
+    XmlEncode::XmlEncode( StringRef str, ForWhat forWhat )
     :   m_str( str ),
         m_forWhat( forWhat )
     {}
@@ -209,11 +211,20 @@ namespace {
         }
     }
 
-    XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, XmlFormatting fmt ) {
+    XmlWriter::ScopedElement&
+    XmlWriter::ScopedElement::writeText( StringRef text, XmlFormatting fmt ) {
         m_writer->writeText( text, fmt );
         return *this;
     }
 
+    XmlWriter::ScopedElement&
+    XmlWriter::ScopedElement::writeAttribute( StringRef name,
+                                              StringRef attribute ) {
+        m_writer->writeAttribute( name, attribute );
+        return *this;
+    }
+
+
     XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
     {
         writeDeclaration();
@@ -265,18 +276,25 @@ namespace {
         return *this;
     }
 
-    XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
+    XmlWriter& XmlWriter::writeAttribute( StringRef name,
+                                          StringRef attribute ) {
         if( !name.empty() && !attribute.empty() )
             m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
         return *this;
     }
 
-    XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
-        m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+    XmlWriter& XmlWriter::writeAttribute( StringRef name, bool attribute ) {
+        writeAttribute(name, (attribute ? "true"_sr : "false"_sr));
+        return *this;
+    }
+
+    XmlWriter& XmlWriter::writeAttribute( StringRef name,
+                                          char const* attribute ) {
+        writeAttribute( name, StringRef( attribute ) );
         return *this;
     }
 
-    XmlWriter& XmlWriter::writeText( std::string const& text, XmlFormatting fmt) {
+    XmlWriter& XmlWriter::writeText( StringRef text, XmlFormatting fmt ) {
         if( !text.empty() ){
             bool tagWasOpen = m_tagIsOpen;
             ensureTagClosed();
@@ -289,7 +307,7 @@ namespace {
         return *this;
     }
 
-    XmlWriter& XmlWriter::writeComment( std::string const& text, XmlFormatting fmt) {
+    XmlWriter& XmlWriter::writeComment( StringRef text, XmlFormatting fmt ) {
         ensureTagClosed();
         if (shouldIndent(fmt)) {
             m_os << m_indent;
@@ -299,8 +317,8 @@ namespace {
         return *this;
     }
 
-    void XmlWriter::writeStylesheetRef( std::string const& url ) {
-        m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+    void XmlWriter::writeStylesheetRef( StringRef url ) {
+        m_os << R"(<?xml-stylesheet type="text/xsl" href=")" << url << R"("?>)" << '\n';
     }
 
     XmlWriter& XmlWriter::writeBlankLine() {
@@ -322,12 +340,12 @@ namespace {
     }
 
     void XmlWriter::writeDeclaration() {
-        m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+        m_os << R"(<?xml version="1.0" encoding="UTF-8"?>)" << '\n';
     }
 
     void XmlWriter::newlineIfNecessary() {
         if( m_needsNewline ) {
-            m_os << std::endl;
+            m_os << '\n' << std::flush;
             m_needsNewline = false;
         }
     }
diff --git a/packages/Catch2/src/catch2/internal/catch_xmlwriter.hpp b/packages/Catch2/src/catch2/internal/catch_xmlwriter.hpp
index c2e7473e1fa8c59877fc09bc731a08c5dc8f9072..3ba8c08ecb60a93dbbd1c383cd308581e7b9bbeb 100644
--- a/packages/Catch2/src/catch2/internal/catch_xmlwriter.hpp
+++ b/packages/Catch2/src/catch2/internal/catch_xmlwriter.hpp
@@ -9,13 +9,7 @@
 #define CATCH_XMLWRITER_HPP_INCLUDED
 
 #include <catch2/internal/catch_stream.hpp>
-
-// FixMe: Without this include (and something inside it), MSVC goes crazy
-//        and reports that calls to XmlEncode's op << are ambiguous between
-//        the declaration and definition.
-//        It also has to be in the header.
-#include <catch2/internal/catch_source_line_info.hpp>
-
+#include <catch2/internal/catch_stringref.hpp>
 
 #include <vector>
 
@@ -29,18 +23,24 @@ namespace Catch {
     XmlFormatting operator | (XmlFormatting lhs, XmlFormatting rhs);
     XmlFormatting operator & (XmlFormatting lhs, XmlFormatting rhs);
 
+    /**
+     * Helper for XML-encoding text (escaping angle brackets, quotes, etc)
+     *
+     * Note: doesn't take ownership of passed strings, and thus the
+     *       encoded string must outlive the encoding instance.
+     */
     class XmlEncode {
     public:
         enum ForWhat { ForTextNodes, ForAttributes };
 
-        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes );
+        XmlEncode( StringRef str, ForWhat forWhat = ForTextNodes );
 
         void encodeTo( std::ostream& os ) const;
 
         friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
 
     private:
-        std::string m_str;
+        StringRef m_str;
         ForWhat m_forWhat;
     };
 
@@ -56,10 +56,22 @@ namespace Catch {
 
             ~ScopedElement();
 
-            ScopedElement& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent );
-
-            template<typename T>
-            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+            ScopedElement&
+            writeText( StringRef text,
+                       XmlFormatting fmt = XmlFormatting::Newline |
+                                           XmlFormatting::Indent );
+
+            ScopedElement& writeAttribute( StringRef name,
+                                           StringRef attribute );
+            template <typename T,
+                      // Without this SFINAE, this overload is a better match
+                      // for `std::string`, `char const*`, `char const[N]` args.
+                      // While it would still work, it would cause code bloat
+                      // and multiple iteration over the strings
+                      typename = typename std::enable_if_t<
+                          !std::is_convertible<T, StringRef>::value>>
+            ScopedElement& writeAttribute( StringRef name,
+                                           T const& attribute ) {
                 m_writer->writeAttribute( name, attribute );
                 return *this;
             }
@@ -81,22 +93,39 @@ namespace Catch {
 
         XmlWriter& endElement(XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
 
-        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
-
-        XmlWriter& writeAttribute( std::string const& name, bool attribute );
-
-        template<typename T>
-        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+        //! The attribute content is XML-encoded
+        XmlWriter& writeAttribute( StringRef name, StringRef attribute );
+
+        //! Writes the attribute as "true/false"
+        XmlWriter& writeAttribute( StringRef name, bool attribute );
+
+        //! The attribute content is XML-encoded
+        XmlWriter& writeAttribute( StringRef name, char const* attribute );
+
+        //! The attribute value must provide op<<(ostream&, T). The resulting
+        //! serialization is XML-encoded
+        template <typename T,
+                  // Without this SFINAE, this overload is a better match
+                  // for `std::string`, `char const*`, `char const[N]` args.
+                  // While it would still work, it would cause code bloat
+                  // and multiple iteration over the strings
+                  typename = typename std::enable_if_t<
+                      !std::is_convertible<T, StringRef>::value>>
+        XmlWriter& writeAttribute( StringRef name, T const& attribute ) {
             ReusableStringStream rss;
             rss << attribute;
             return writeAttribute( name, rss.str() );
         }
 
-        XmlWriter& writeText( std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+        XmlWriter& writeText( StringRef text,
+                              XmlFormatting fmt = XmlFormatting::Newline |
+                                                  XmlFormatting::Indent );
 
-        XmlWriter& writeComment(std::string const& text, XmlFormatting fmt = XmlFormatting::Newline | XmlFormatting::Indent);
+        XmlWriter& writeComment( StringRef text,
+                                 XmlFormatting fmt = XmlFormatting::Newline |
+                                                     XmlFormatting::Indent );
 
-        void writeStylesheetRef( std::string const& url );
+        void writeStylesheetRef( StringRef url );
 
         XmlWriter& writeBlankLine();
 
diff --git a/packages/Catch2/src/catch2/matchers/catch_matchers.hpp b/packages/Catch2/src/catch2/matchers/catch_matchers.hpp
index fe8f48c4b71ca45d2e362375cdddc3115db3d20b..86f63bee5e41e9678523c1eaeb9f307488e23223 100644
--- a/packages/Catch2/src/catch2/matchers/catch_matchers.hpp
+++ b/packages/Catch2/src/catch2/matchers/catch_matchers.hpp
@@ -9,6 +9,7 @@
 #define CATCH_MATCHERS_HPP_INCLUDED
 
 #include <catch2/matchers/internal/catch_matchers_impl.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <string>
 #include <vector>
@@ -87,11 +88,11 @@ namespace Matchers {
 
             friend MatchAllOf operator&& (MatchAllOf&& lhs, MatcherBase<ArgT> const& rhs) {
                 lhs.m_matchers.push_back(&rhs);
-                return std::move(lhs);
+                return CATCH_MOVE(lhs);
             }
             friend MatchAllOf operator&& (MatcherBase<ArgT> const& lhs, MatchAllOf&& rhs) {
                 rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
-                return std::move(rhs);
+                return CATCH_MOVE(rhs);
             }
 
         private:
@@ -140,11 +141,11 @@ namespace Matchers {
 
             friend MatchAnyOf operator|| (MatchAnyOf&& lhs, MatcherBase<ArgT> const& rhs) {
                 lhs.m_matchers.push_back(&rhs);
-                return std::move(lhs);
+                return CATCH_MOVE(lhs);
             }
             friend MatchAnyOf operator|| (MatcherBase<ArgT> const& lhs, MatchAnyOf&& rhs) {
                 rhs.m_matchers.insert(rhs.m_matchers.begin(), &lhs);
-                return std::move(rhs);
+                return CATCH_MOVE(rhs);
             }
 
         private:
diff --git a/packages/Catch2/src/catch2/matchers/catch_matchers_container_properties.hpp b/packages/Catch2/src/catch2/matchers/catch_matchers_container_properties.hpp
index 81273995538dc576176eb16321cc02041ba2d3e4..082f834f281a694f240f68305d1e7c83e9728d66 100644
--- a/packages/Catch2/src/catch2/matchers/catch_matchers_container_properties.hpp
+++ b/packages/Catch2/src/catch2/matchers/catch_matchers_container_properties.hpp
@@ -10,6 +10,7 @@
 
 #include <catch2/matchers/catch_matchers_templated.hpp>
 #include <catch2/internal/catch_container_nonmembers.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
     namespace Matchers {
@@ -55,7 +56,7 @@ namespace Catch {
             Matcher m_matcher;
         public:
             explicit SizeMatchesMatcher(Matcher m):
-                m_matcher(std::move(m))
+                m_matcher(CATCH_MOVE(m))
             {}
 
             template <typename RangeLike>
@@ -81,7 +82,7 @@ namespace Catch {
         template <typename Matcher>
         std::enable_if_t<Detail::is_matcher<Matcher>::value,
         SizeMatchesMatcher<Matcher>> SizeIs(Matcher&& m) {
-            return SizeMatchesMatcher<Matcher>{std::forward<Matcher>(m)};
+            return SizeMatchesMatcher<Matcher>{CATCH_FORWARD(m)};
         }
 
     } // end namespace Matchers
diff --git a/packages/Catch2/src/catch2/matchers/catch_matchers_contains.hpp b/packages/Catch2/src/catch2/matchers/catch_matchers_contains.hpp
index be1b9379ff53b3afaa4063fb7948a06e15599da2..239aa80b96f71b855e69177b5950790793a2ce9e 100644
--- a/packages/Catch2/src/catch2/matchers/catch_matchers_contains.hpp
+++ b/packages/Catch2/src/catch2/matchers/catch_matchers_contains.hpp
@@ -9,10 +9,10 @@
 #define CATCH_MATCHERS_CONTAINS_HPP_INCLUDED
 
 #include <catch2/matchers/catch_matchers_templated.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <algorithm>
 #include <functional>
-#include <utility>
 
 namespace Catch {
     namespace Matchers {
@@ -24,8 +24,8 @@ namespace Catch {
         public:
             template <typename T2, typename Equality2>
             ContainsElementMatcher(T2&& target, Equality2&& predicate):
-                m_desired(std::forward<T2>(target)),
-                m_eq(std::forward<Equality2>(predicate))
+                m_desired(CATCH_FORWARD(target)),
+                m_eq(CATCH_FORWARD(predicate))
             {}
 
             std::string describe() const override {
@@ -52,12 +52,11 @@ namespace Catch {
             // constructor (and also avoid some perfect forwarding failure
             // cases)
             ContainsMatcherMatcher(Matcher matcher):
-                m_matcher(std::move(matcher))
+                m_matcher(CATCH_MOVE(matcher))
             {}
 
             template <typename RangeLike>
             bool match(RangeLike&& rng) const {
-                using std::begin; using std::endl;
                 for (auto&& elem : rng) {
                     if (m_matcher.match(elem)) {
                         return true;
@@ -79,14 +78,14 @@ namespace Catch {
         template <typename T>
         std::enable_if_t<!Detail::is_matcher<T>::value,
         ContainsElementMatcher<T, std::equal_to<>>> Contains(T&& elem) {
-            return { std::forward<T>(elem), std::equal_to<>{} };
+            return { CATCH_FORWARD(elem), std::equal_to<>{} };
         }
 
         //! Creates a matcher that checks whether a range contains element matching a matcher
         template <typename Matcher>
         std::enable_if_t<Detail::is_matcher<Matcher>::value,
         ContainsMatcherMatcher<Matcher>> Contains(Matcher&& matcher) {
-            return { std::forward<Matcher>(matcher) };
+            return { CATCH_FORWARD(matcher) };
         }
 
         /**
@@ -96,7 +95,7 @@ namespace Catch {
          */
         template <typename T, typename Equality>
         ContainsElementMatcher<T, Equality> Contains(T&& elem, Equality&& eq) {
-            return { std::forward<T>(elem), std::forward<Equality>(eq) };
+            return { CATCH_FORWARD(elem), CATCH_FORWARD(eq) };
         }
 
     }
diff --git a/packages/Catch2/src/catch2/matchers/catch_matchers_floating_point.cpp b/packages/Catch2/src/catch2/matchers/catch_matchers_floating_point.cpp
index fc5d916525251bdcb6c5d0e800e975813f928da2..ad0fd378c5aff1aa43a4e0b312d812ed03cf39d5 100644
--- a/packages/Catch2/src/catch2/matchers/catch_matchers_floating_point.cpp
+++ b/packages/Catch2/src/catch2/matchers/catch_matchers_floating_point.cpp
@@ -10,12 +10,12 @@
 #include <catch2/internal/catch_polyfills.hpp>
 #include <catch2/internal/catch_to_string.hpp>
 #include <catch2/catch_tostring.hpp>
+#include <catch2/internal/catch_floating_point_helpers.hpp>
 
 #include <algorithm>
 #include <cmath>
 #include <cstdlib>
 #include <cstdint>
-#include <cstring>
 #include <sstream>
 #include <iomanip>
 #include <limits>
@@ -24,20 +24,6 @@
 namespace Catch {
 namespace {
 
-    int32_t convert(float f) {
-        static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
-        int32_t i;
-        std::memcpy(&i, &f, sizeof(f));
-        return i;
-    }
-
-    int64_t convert(double d) {
-        static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
-        int64_t i;
-        std::memcpy(&i, &d, sizeof(d));
-        return i;
-    }
-
     template <typename FP>
     bool almostEqualUlps(FP lhs, FP rhs, uint64_t maxUlpDiff) {
         // Comparison with NaN should always be false.
@@ -46,16 +32,10 @@ namespace {
             return false;
         }
 
-        auto lc = convert(lhs);
-        auto rc = convert(rhs);
-
-        if ((lc < 0) != (rc < 0)) {
-            // Potentially we can have +0 and -0
-            return lhs == rhs;
-        }
+        // This should also handle positive and negative zeros, infinities
+        const auto ulpDist = ulpDistance(lhs, rhs);
 
-        auto ulpDiff = std::abs(lc - rc);
-        return static_cast<uint64_t>(ulpDiff) <= maxUlpDiff;
+        return ulpDist <= maxUlpDiff;
     }
 
 #if defined(CATCH_CONFIG_GLOBAL_NEXTAFTER)
@@ -130,6 +110,9 @@ namespace Detail {
         CATCH_ENFORCE(m_type == Detail::FloatingPointKind::Double
                    || m_ulps < (std::numeric_limits<uint32_t>::max)(),
             "Provided ULP is impossibly large for a float comparison.");
+        CATCH_ENFORCE( std::numeric_limits<double>::is_iec559,
+                       "WithinUlp matcher only supports platforms with "
+                       "IEEE-754 compatible floating point representation" );
     }
 
 #if defined(__clang__)
diff --git a/packages/Catch2/src/catch2/matchers/catch_matchers_predicate.hpp b/packages/Catch2/src/catch2/matchers/catch_matchers_predicate.hpp
index 99a01a4edb594a7d0d45dafc6d0d9570f52e2ca2..5f5cea590137927288b535c432a6ebe8685980f1 100644
--- a/packages/Catch2/src/catch2/matchers/catch_matchers_predicate.hpp
+++ b/packages/Catch2/src/catch2/matchers/catch_matchers_predicate.hpp
@@ -10,9 +10,9 @@
 
 #include <catch2/matchers/catch_matchers.hpp>
 #include <catch2/internal/catch_meta.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <string>
-#include <utility>
 
 namespace Catch {
 namespace Matchers {
@@ -28,7 +28,7 @@ class PredicateMatcher final : public MatcherBase<T> {
 public:
 
     PredicateMatcher(Predicate&& elem, std::string const& descr)
-        :m_predicate(std::forward<Predicate>(elem)),
+        :m_predicate(CATCH_FORWARD(elem)),
         m_description(Detail::finalizeDescription(descr))
     {}
 
@@ -50,7 +50,7 @@ public:
     PredicateMatcher<T, Pred> Predicate(Pred&& predicate, std::string const& description = "") {
         static_assert(is_callable<Pred(T)>::value, "Predicate not callable with argument T");
         static_assert(std::is_same<bool, FunctionReturnType<Pred, T>>::value, "Predicate does not return bool");
-        return PredicateMatcher<T, Pred>(std::forward<Pred>(predicate), description);
+        return PredicateMatcher<T, Pred>(CATCH_FORWARD(predicate), description);
     }
 
 } // namespace Matchers
diff --git a/packages/Catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp b/packages/Catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp
index 09a8c1280e53daef26a6cfc439a234eb8f37a55b..e0e1a62d702794e5c4dcaf35e2bb4b7a6175bac1 100644
--- a/packages/Catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp
+++ b/packages/Catch2/src/catch2/matchers/catch_matchers_quantifiers.hpp
@@ -9,8 +9,7 @@
 #define CATCH_MATCHERS_QUANTIFIERS_HPP_INCLUDED
 
 #include <catch2/matchers/catch_matchers_templated.hpp>
-
-#include <utility>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
     namespace Matchers {
@@ -20,7 +19,7 @@ namespace Catch {
             Matcher m_matcher;
         public:
             AllMatchMatcher(Matcher matcher):
-                m_matcher(std::move(matcher))
+                m_matcher(CATCH_MOVE(matcher))
             {}
 
             std::string describe() const override {
@@ -44,7 +43,7 @@ namespace Catch {
             Matcher m_matcher;
         public:
             NoneMatchMatcher(Matcher matcher):
-                m_matcher(std::move(matcher))
+                m_matcher(CATCH_MOVE(matcher))
             {}
 
             std::string describe() const override {
@@ -68,7 +67,7 @@ namespace Catch {
             Matcher m_matcher;
         public:
             AnyMatchMatcher(Matcher matcher):
-                m_matcher(std::move(matcher))
+                m_matcher(CATCH_MOVE(matcher))
             {}
 
             std::string describe() const override {
@@ -89,19 +88,19 @@ namespace Catch {
         // Creates a matcher that checks whether a range contains element matching a matcher
         template <typename Matcher>
         AllMatchMatcher<Matcher> AllMatch(Matcher&& matcher) {
-            return { std::forward<Matcher>(matcher) };
+            return { CATCH_FORWARD(matcher) };
         }
 
         // Creates a matcher that checks whether no element in a range matches a matcher.
         template <typename Matcher>
         NoneMatchMatcher<Matcher> NoneMatch(Matcher&& matcher) {
-            return { std::forward<Matcher>(matcher) };
+            return { CATCH_FORWARD(matcher) };
         }
 
         // Creates a matcher that checks whether any element in a range matches a matcher.
         template <typename Matcher>
         AnyMatchMatcher<Matcher> AnyMatch(Matcher&& matcher) {
-            return { std::forward<Matcher>(matcher) };
+            return { CATCH_FORWARD(matcher) };
         }
     }
 }
diff --git a/packages/Catch2/src/catch2/matchers/catch_matchers_string.cpp b/packages/Catch2/src/catch2/matchers/catch_matchers_string.cpp
index a81baddd599c1ce3f642290333edb5a585809a4b..ee082d2c06e12b86509a3c3ee7e00902b86a1976 100644
--- a/packages/Catch2/src/catch2/matchers/catch_matchers_string.cpp
+++ b/packages/Catch2/src/catch2/matchers/catch_matchers_string.cpp
@@ -8,6 +8,7 @@
 #include <catch2/matchers/catch_matchers_string.hpp>
 #include <catch2/internal/catch_string_manip.hpp>
 #include <catch2/catch_tostring.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <regex>
 
@@ -76,7 +77,7 @@ namespace Matchers {
 
 
 
-    RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {}
+    RegexMatcher::RegexMatcher(std::string regex, CaseSensitive caseSensitivity): m_regex(CATCH_MOVE(regex)), m_caseSensitivity(caseSensitivity) {}
 
     bool RegexMatcher::match(std::string const& matchee) const {
         auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
diff --git a/packages/Catch2/src/catch2/matchers/catch_matchers_templated.hpp b/packages/Catch2/src/catch2/matchers/catch_matchers_templated.hpp
index 544a2a5bedddedc54f04fb578a4972f6fdd10871..200ca74bf9e919462163580b945cbfb45ebcbd44 100644
--- a/packages/Catch2/src/catch2/matchers/catch_matchers_templated.hpp
+++ b/packages/Catch2/src/catch2/matchers/catch_matchers_templated.hpp
@@ -10,12 +10,12 @@
 
 #include <catch2/matchers/catch_matchers.hpp>
 #include <catch2/internal/catch_stringref.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <array>
 #include <algorithm>
 #include <string>
 #include <type_traits>
-#include <utility>
 
 namespace Catch {
 namespace Matchers {
@@ -55,11 +55,11 @@ namespace Matchers {
             return arr;
         }
 
-        #ifdef CATCH_CPP17_OR_GREATER
+#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510
 
         using std::conjunction;
 
-        #else // CATCH_CPP17_OR_GREATER
+#else // __cpp_lib_logical_traits
 
         template<typename... Cond>
         struct conjunction : std::true_type {};
@@ -67,7 +67,7 @@ namespace Matchers {
         template<typename Cond, typename... Rest>
         struct conjunction<Cond, Rest...> : std::integral_constant<bool, Cond::value && conjunction<Rest...>::value> {};
 
-        #endif // CATCH_CPP17_OR_GREATER
+#endif // __cpp_lib_logical_traits
 
         template<typename T>
         using is_generic_matcher = std::is_base_of<
@@ -145,7 +145,7 @@ namespace Matchers {
             MatchAllOfGeneric<MatcherTs..., MatchersRHS...> operator && (
                     MatchAllOfGeneric<MatcherTs...>&& lhs,
                     MatchAllOfGeneric<MatchersRHS...>&& rhs) {
-                return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
+                return MatchAllOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
             }
 
             //! Avoids type nesting for `GenericAllOf && some matcher` case
@@ -154,7 +154,7 @@ namespace Matchers {
             MatchAllOfGeneric<MatcherTs..., MatcherRHS>> operator && (
                     MatchAllOfGeneric<MatcherTs...>&& lhs,
                     MatcherRHS const& rhs) {
-                return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(&rhs))};
+                return MatchAllOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(&rhs))};
             }
 
             //! Avoids type nesting for `some matcher && GenericAllOf` case
@@ -163,7 +163,7 @@ namespace Matchers {
             MatchAllOfGeneric<MatcherLHS, MatcherTs...>> operator && (
                     MatcherLHS const& lhs,
                     MatchAllOfGeneric<MatcherTs...>&& rhs) {
-                return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
+                return MatchAllOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
             }
         };
 
@@ -194,7 +194,7 @@ namespace Matchers {
             friend MatchAnyOfGeneric<MatcherTs..., MatchersRHS...> operator || (
                     MatchAnyOfGeneric<MatcherTs...>&& lhs,
                     MatchAnyOfGeneric<MatchersRHS...>&& rhs) {
-                return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(std::move(lhs.m_matchers), std::move(rhs.m_matchers))};
+                return MatchAnyOfGeneric<MatcherTs..., MatchersRHS...>{array_cat(CATCH_MOVE(lhs.m_matchers), CATCH_MOVE(rhs.m_matchers))};
             }
 
             //! Avoids type nesting for `GenericAnyOf || some matcher` case
@@ -203,7 +203,7 @@ namespace Matchers {
             MatchAnyOfGeneric<MatcherTs..., MatcherRHS>> operator || (
                     MatchAnyOfGeneric<MatcherTs...>&& lhs,
                     MatcherRHS const& rhs) {
-                return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(std::move(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
+                return MatchAnyOfGeneric<MatcherTs..., MatcherRHS>{array_cat(CATCH_MOVE(lhs.m_matchers), static_cast<void const*>(std::addressof(rhs)))};
             }
 
             //! Avoids type nesting for `some matcher || GenericAnyOf` case
@@ -212,7 +212,7 @@ namespace Matchers {
             MatchAnyOfGeneric<MatcherLHS, MatcherTs...>> operator || (
                 MatcherLHS const& lhs,
                 MatchAnyOfGeneric<MatcherTs...>&& rhs) {
-                return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), std::move(rhs.m_matchers))};
+                return MatchAnyOfGeneric<MatcherLHS, MatcherTs...>{array_cat(static_cast<void const*>(std::addressof(lhs)), CATCH_MOVE(rhs.m_matchers))};
             }
         };
 
diff --git a/packages/Catch2/src/catch2/matchers/internal/catch_matchers_combined_tu.cpp b/packages/Catch2/src/catch2/matchers/internal/catch_matchers_combined_tu.cpp
index 7104e4c8412f84256c51480e9f211f921c5722c7..4fab1b52f84cf5c04273b79ce9600bf2eae5977e 100644
--- a/packages/Catch2/src/catch2/matchers/internal/catch_matchers_combined_tu.cpp
+++ b/packages/Catch2/src/catch2/matchers/internal/catch_matchers_combined_tu.cpp
@@ -23,15 +23,16 @@
 #include <catch2/matchers/internal/catch_matchers_impl.hpp>
 #include <catch2/matchers/catch_matchers.hpp>
 #include <catch2/interfaces/catch_interfaces_registry_hub.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
 
     // This is the general overload that takes a any string matcher
     // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
     // the Equals matcher (so the header does not mention matchers)
-    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  ) {
+    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString  ) {
         std::string exceptionMessage = Catch::translateActiveException();
-        MatchExpr<std::string, StringMatcher const&> expr( std::move(exceptionMessage), matcher, matcherString );
+        MatchExpr<std::string, StringMatcher const&> expr( CATCH_MOVE(exceptionMessage), matcher, matcherString );
         handler.handleExpr( expr );
     }
 
diff --git a/packages/Catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp b/packages/Catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp
index 5a5448da560ede59b8fec946c9bdda58c3bbe53f..2acbec1f3014796c335279ddb52840e320d81fe6 100644
--- a/packages/Catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp
+++ b/packages/Catch2/src/catch2/matchers/internal/catch_matchers_impl.hpp
@@ -10,6 +10,7 @@
 
 #include <catch2/internal/catch_test_macro_impl.hpp>
 #include <catch2/internal/catch_stringref.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 namespace Catch {
 
@@ -19,20 +20,17 @@ namespace Catch {
         MatcherT const& m_matcher;
         StringRef m_matcherString;
     public:
-        MatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString )
+        MatchExpr( ArgT && arg, MatcherT const& matcher, StringRef matcherString )
         :   ITransientExpression{ true, matcher.match( arg ) }, // not forwarding arg here on purpose
-            m_arg( std::forward<ArgT>(arg) ),
+            m_arg( CATCH_FORWARD(arg) ),
             m_matcher( matcher ),
             m_matcherString( matcherString )
         {}
 
-        void streamReconstructedExpression( std::ostream &os ) const override {
-            auto matcherAsString = m_matcher.toString();
-            os << Catch::Detail::stringify( m_arg ) << ' ';
-            if( matcherAsString == Detail::unprintableString )
-                os << m_matcherString;
-            else
-                os << matcherAsString;
+        void streamReconstructedExpression( std::ostream& os ) const override {
+            os << Catch::Detail::stringify( m_arg )
+               << ' '
+               << m_matcher.toString();
         }
     };
 
@@ -43,11 +41,11 @@ namespace Catch {
 
     using StringMatcher = Matchers::MatcherBase<std::string>;
 
-    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  );
+    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef matcherString  );
 
     template<typename ArgT, typename MatcherT>
-    auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef const& matcherString  ) -> MatchExpr<ArgT, MatcherT> {
-        return MatchExpr<ArgT, MatcherT>( std::forward<ArgT>(arg), matcher, matcherString );
+    auto makeMatchExpr( ArgT && arg, MatcherT const& matcher, StringRef matcherString  ) -> MatchExpr<ArgT, MatcherT> {
+        return MatchExpr<ArgT, MatcherT>( CATCH_FORWARD(arg), matcher, matcherString );
     }
 
 } // namespace Catch
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_automake.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_automake.hpp
index 209729c11b22f2502df4b98e54cf5540300cc95d..84fad79027e1ebec9a22dea4810f7aa27a275f6e 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_automake.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_automake.hpp
@@ -12,7 +12,7 @@
 
 namespace Catch {
 
-    struct AutomakeReporter : StreamingReporterBase {
+    struct AutomakeReporter final : StreamingReporterBase {
         AutomakeReporter( ReporterConfig const& _config )
           :   StreamingReporterBase( _config )
         {}
@@ -24,12 +24,7 @@ namespace Catch {
             return "Reports test results in the format of Automake .trs files"s;
         }
 
-        void assertionStarting( AssertionInfo const& ) override {}
-
-        bool assertionEnded( AssertionStats const& /*_assertionStats*/ ) override { return true; }
-
         void testCaseEnded(TestCaseStats const& _testCaseStats) override;
-
         void skipTest(TestCaseInfo const& testInfo) override;
 
     };
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_combined_tu.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_combined_tu.cpp
index 25a52e9dacd2327767b3ed3b00c06a02e2694e4e..2ca76c9351c3c4024e3d0e418ef235e75f31ea14 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_combined_tu.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_combined_tu.cpp
@@ -69,11 +69,13 @@ namespace Catch {
         // Save previous errno, to prevent sprintf from overwriting it
         ErrnoGuard guard;
 #ifdef _MSC_VER
-        sprintf_s( buffer, "%.3f", duration );
+        size_t printedLength = static_cast<size_t>(
+            sprintf_s( buffer, "%.3f", duration ) );
 #else
-        std::snprintf( buffer, maxDoubleSize, "%.3f", duration );
+        size_t printedLength = static_cast<size_t>(
+            std::snprintf( buffer, maxDoubleSize, "%.3f", duration ) );
 #endif
-        return std::string( buffer );
+        return std::string( buffer, printedLength );
     }
 
     bool shouldShowDuration( IConfig const& config, double duration ) {
@@ -169,7 +171,7 @@ namespace Catch {
                                .width( CATCH_CONFIG_CONSOLE_WIDTH - 10 );
             out << str << wrapper << '\n';
         }
-        out << pluralise( tags.size(), "tag" ) << '\n' << std::endl;
+        out << pluralise(tags.size(), "tag"_sr) << "\n\n" << std::flush;
     }
 
     void defaultListTests(std::ostream& out, std::vector<TestCaseHandle> const& tests, bool isFiltered, Verbosity verbosity) {
@@ -196,7 +198,7 @@ namespace Catch {
 
             out << TextFlow::Column(testCaseInfo.name).initialIndent(2).indent(4) << '\n';
             if (verbosity >= Verbosity::High) {
-                out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << std::endl;
+                out << TextFlow::Column(Catch::Detail::stringify(testCaseInfo.lineInfo)).indent(4) << '\n';
             }
             if (!testCaseInfo.tags.empty() &&
                 verbosity > Verbosity::Quiet) {
@@ -205,10 +207,11 @@ namespace Catch {
         }
 
         if (isFiltered) {
-            out << pluralise(tests.size(), "matching test case") << '\n' << std::endl;
+            out << pluralise(tests.size(), "matching test case"_sr);
         } else {
-            out << pluralise(tests.size(), "test case") << '\n' << std::endl;
+            out << pluralise(tests.size(), "test case"_sr);
         }
+        out << "\n\n" << std::flush;
     }
 
 } // namespace Catch
@@ -217,23 +220,30 @@ namespace Catch {
 #include <catch2/reporters/catch_reporter_event_listener.hpp>
 
 namespace Catch {
+
+    void EventListenerBase::fatalErrorEncountered( StringRef ) {}
+
+    void EventListenerBase::benchmarkPreparing( StringRef ) {}
+    void EventListenerBase::benchmarkStarting( BenchmarkInfo const& ) {}
+    void EventListenerBase::benchmarkEnded( BenchmarkStats<> const& ) {}
+    void EventListenerBase::benchmarkFailed( StringRef ) {}
+
     void EventListenerBase::assertionStarting( AssertionInfo const& ) {}
 
-    bool EventListenerBase::assertionEnded( AssertionStats const& ) {
-        return false;
-    }
+    void EventListenerBase::assertionEnded( AssertionStats const& ) {}
     void EventListenerBase::listReporters(
         std::vector<ReporterDescription> const& ) {}
     void EventListenerBase::listTests( std::vector<TestCaseHandle> const& ) {}
     void EventListenerBase::listTags( std::vector<TagInfo> const& ) {}
-    void EventListenerBase::noMatchingTestCases( std::string const& ) {}
+    void EventListenerBase::noMatchingTestCases( StringRef ) {}
+    void EventListenerBase::reportInvalidArguments( StringRef ) {}
     void EventListenerBase::testRunStarting( TestRunInfo const& ) {}
-    void EventListenerBase::testGroupStarting( GroupInfo const& ) {}
     void EventListenerBase::testCaseStarting( TestCaseInfo const& ) {}
+    void EventListenerBase::testCasePartialStarting(TestCaseInfo const&, uint64_t) {}
     void EventListenerBase::sectionStarting( SectionInfo const& ) {}
     void EventListenerBase::sectionEnded( SectionStats const& ) {}
+    void EventListenerBase::testCasePartialEnded(TestCaseStats const&, uint64_t) {}
     void EventListenerBase::testCaseEnded( TestCaseStats const& ) {}
-    void EventListenerBase::testGroupEnded( TestGroupStats const& ) {}
     void EventListenerBase::testRunEnded( TestRunStats const& ) {}
     void EventListenerBase::skipTest( TestCaseInfo const& ) {}
 } // namespace Catch
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_compact.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_compact.cpp
index d59cb5a5c72ea4881c22a7ce18d609b9765fee6b..1bd997bdc70f19bfe7ca48af9031b981b620a95a 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_compact.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_compact.cpp
@@ -19,9 +19,9 @@
 namespace {
 
     // Colour::LightGrey
-    Catch::Colour::Code dimColour() { return Catch::Colour::FileName; }
+    constexpr Catch::Colour::Code dimColour() { return Catch::Colour::FileName; }
 
-    Catch::StringRef bothOrAll( std::size_t count ) {
+    constexpr Catch::StringRef bothOrAll( std::size_t count ) {
         switch (count) {
         case 1:
             return Catch::StringRef{};
@@ -62,25 +62,25 @@ void printTotals(std::ostream& out, const Totals& totals) {
             bothOrAll(totals.assertions.failed) : StringRef{};
         out <<
             "Failed " << bothOrAll(totals.testCases.failed)
-            << pluralise(totals.testCases.failed, "test case") << ", "
+            << pluralise(totals.testCases.failed, "test case"_sr) << ", "
             "failed " << qualify_assertions_failed <<
-            pluralise(totals.assertions.failed, "assertion") << '.';
+            pluralise(totals.assertions.failed, "assertion"_sr) << '.';
     } else if (totals.assertions.total() == 0) {
         out <<
             "Passed " << bothOrAll(totals.testCases.total())
-            << pluralise(totals.testCases.total(), "test case")
+            << pluralise(totals.testCases.total(), "test case"_sr)
             << " (no assertions).";
     } else if (totals.assertions.failed) {
         Colour colour(Colour::ResultError);
         out <<
-            "Failed " << pluralise(totals.testCases.failed, "test case") << ", "
-            "failed " << pluralise(totals.assertions.failed, "assertion") << '.';
+            "Failed " << pluralise(totals.testCases.failed, "test case"_sr) << ", "
+            "failed " << pluralise(totals.assertions.failed, "assertion"_sr) << '.';
     } else {
         Colour colour(Colour::ResultSuccess);
         out <<
             "Passed " << bothOrAll(totals.testCases.passed)
-            << pluralise(totals.testCases.passed, "test case") <<
-            " with " << pluralise(totals.assertions.passed, "assertion") << '.';
+            << pluralise(totals.testCases.passed, "test case"_sr) <<
+            " with " << pluralise(totals.assertions.passed, "assertion"_sr) << '.';
     }
 }
 
@@ -227,7 +227,7 @@ private:
 
         {
             Colour colourGuard(colour);
-            stream << " with " << pluralise(N, "message") << ':';
+            stream << " with " << pluralise(N, "message"_sr) << ':';
         }
 
         while (itMessage != itEnd) {
@@ -258,13 +258,11 @@ private:
             return "Reports test results on a single line, suitable for IDEs";
         }
 
-        void CompactReporter::noMatchingTestCases( std::string const& spec ) {
-            stream << "No test cases matched '" << spec << '\'' << std::endl;
+        void CompactReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+            stream << "No test cases matched '" << unmatchedSpec << "'\n";
         }
 
-        void CompactReporter::assertionStarting( AssertionInfo const& ) {}
-
-        bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
+        void CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
             AssertionResult const& result = _assertionStats.assertionResult;
 
             bool printInfoMessages = true;
@@ -272,27 +270,26 @@ private:
             // Drop out if result was successful and we're not printing those
             if( !m_config->includeSuccessfulResults() && result.isOk() ) {
                 if( result.getResultType() != ResultWas::Warning )
-                    return false;
+                    return;
                 printInfoMessages = false;
             }
 
             AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
             printer.print();
 
-            stream << std::endl;
-            return true;
+            stream << '\n' << std::flush;
         }
 
         void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
             double dur = _sectionStats.durationInSeconds;
             if ( shouldShowDuration( *m_config, dur ) ) {
-                stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+                stream << getFormattedDuration( dur ) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush;
             }
         }
 
         void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
             printTotals( stream, _testRunStats.totals );
-            stream << '\n' << std::endl;
+            stream << "\n\n" << std::flush;
             StreamingReporterBase::testRunEnded( _testRunStats );
         }
 
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_compact.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_compact.hpp
index 15f9531759d603f4ce30cdcad0ac539928f383fc..b39bd18ae264e890416cad430a9852614efc1eb9 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_compact.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_compact.hpp
@@ -14,7 +14,7 @@
 
 namespace Catch {
 
-    struct CompactReporter : StreamingReporterBase {
+    struct CompactReporter final : StreamingReporterBase {
 
         using StreamingReporterBase::StreamingReporterBase;
 
@@ -22,11 +22,9 @@ namespace Catch {
 
         static std::string getDescription();
 
-        void noMatchingTestCases(std::string const& spec) override;
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
 
-        void assertionStarting(AssertionInfo const&) override;
-
-        bool assertionEnded(AssertionStats const& _assertionStats) override;
+        void assertionEnded(AssertionStats const& _assertionStats) override;
 
         void sectionEnded(SectionStats const& _sectionStats) override;
 
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_console.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_console.cpp
index e89702142251584c1b00b2a2b382aa9351e392d9..d7c2dd5953cd51dc145131d668015caf9650c733 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_console.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_console.cpp
@@ -18,8 +18,8 @@
 #include <catch2/catch_test_case_info.hpp>
 #include <catch2/internal/catch_console_width.hpp>
 #include <catch2/reporters/catch_reporter_helpers.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
-#include <cfloat>
 #include <cstdio>
 
 #if defined(_MSC_VER)
@@ -56,7 +56,7 @@ public:
         switch (result.getResultType()) {
         case ResultWas::Ok:
             colour = Colour::Success;
-            passOrFail = "PASSED";
+            passOrFail = "PASSED"_sr;
             //if( result.hasMessage() )
             if (_stats.infoMessages.size() == 1)
                 messageLabel = "with message";
@@ -66,10 +66,10 @@ public:
         case ResultWas::ExpressionFailed:
             if (result.isOk()) {
                 colour = Colour::Success;
-                passOrFail = "FAILED - but was ok";
+                passOrFail = "FAILED - but was ok"_sr;
             } else {
                 colour = Colour::Error;
-                passOrFail = "FAILED";
+                passOrFail = "FAILED"_sr;
             }
             if (_stats.infoMessages.size() == 1)
                 messageLabel = "with message";
@@ -78,7 +78,7 @@ public:
             break;
         case ResultWas::ThrewException:
             colour = Colour::Error;
-            passOrFail = "FAILED";
+            passOrFail = "FAILED"_sr;
             messageLabel = "due to unexpected exception with ";
             if (_stats.infoMessages.size() == 1)
                 messageLabel += "message";
@@ -87,12 +87,12 @@ public:
             break;
         case ResultWas::FatalErrorCondition:
             colour = Colour::Error;
-            passOrFail = "FAILED";
+            passOrFail = "FAILED"_sr;
             messageLabel = "due to a fatal error condition";
             break;
         case ResultWas::DidntThrowException:
             colour = Colour::Error;
-            passOrFail = "FAILED";
+            passOrFail = "FAILED"_sr;
             messageLabel = "because no exception was thrown where one was expected";
             break;
         case ResultWas::Info:
@@ -102,7 +102,7 @@ public:
             messageLabel = "warning";
             break;
         case ResultWas::ExplicitFailure:
-            passOrFail = "FAILED";
+            passOrFail = "FAILED"_sr;
             colour = Colour::Error;
             if (_stats.infoMessages.size() == 1)
                 messageLabel = "explicitly with message";
@@ -113,7 +113,7 @@ public:
         case ResultWas::Unknown:
         case ResultWas::FailureBit:
         case ResultWas::Exception:
-            passOrFail = "** internal error **";
+            passOrFail = "** internal error **"_sr;
             colour = Colour::Error;
             break;
         }
@@ -171,7 +171,7 @@ private:
     AssertionStats const& stats;
     AssertionResult const& result;
     Colour::Code colour;
-    std::string passOrFail;
+    StringRef passOrFail;
     std::string messageLabel;
     std::string message;
     std::vector<MessageInfo> messages;
@@ -285,7 +285,7 @@ class TablePrinter {
 public:
     TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
     :   m_os( os ),
-        m_columnInfos( std::move( columnInfos ) ) {}
+        m_columnInfos( CATCH_MOVE( columnInfos ) ) {}
 
     auto columnInfos() const -> std::vector<ColumnInfo> const& {
         return m_columnInfos;
@@ -310,7 +310,7 @@ public:
     void close() {
         if (m_isOpen) {
             *this << RowBreak();
-            m_os << std::endl;
+            m_os << '\n' << std::flush;
             m_isOpen = false;
         }
     }
@@ -354,7 +354,7 @@ public:
 
 ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
     : StreamingReporterBase(config),
-    m_tablePrinter(new TablePrinter(config.stream(),
+    m_tablePrinter(Detail::make_unique<TablePrinter>(config.stream(),
         [&config]() -> std::vector<ColumnInfo> {
         if (config.fullConfig()->benchmarkNoAnalysis())
         {
@@ -381,31 +381,30 @@ std::string ConsoleReporter::getDescription() {
     return "Reports test results as plain lines of text";
 }
 
-void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
-    stream << "No test cases matched '" << spec << '\'' << std::endl;
+void ConsoleReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+    stream << "No test cases matched '" << unmatchedSpec << "'\n";
 }
 
-void ConsoleReporter::reportInvalidArguments(std::string const&arg){
-    stream << "Invalid Filter: " << arg << std::endl;
+void ConsoleReporter::reportInvalidArguments( StringRef arg ) {
+    stream << "Invalid Filter: " << arg << '\n';
 }
 
 void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
 
-bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
+void ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
     AssertionResult const& result = _assertionStats.assertionResult;
 
     bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
 
     // Drop out if result was successful but we're not printing them.
     if (!includeResults && result.getResultType() != ResultWas::Warning)
-        return false;
+        return;
 
     lazyPrint();
 
     ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults);
     printer.print();
-    stream << std::endl;
-    return true;
+    stream << '\n' << std::flush;
 }
 
 void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
@@ -422,11 +421,11 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
             stream << "\nNo assertions in section";
         else
             stream << "\nNo assertions in test case";
-        stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+        stream << " '" << _sectionStats.sectionInfo.name << "'\n\n" << std::flush;
     }
     double dur = _sectionStats.durationInSeconds;
     if (shouldShowDuration(*m_config, dur)) {
-        stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+        stream << getFormattedDuration(dur) << " s: " << _sectionStats.sectionInfo.name << '\n' << std::flush;
     }
     if (m_headerPrinted) {
         m_headerPrinted = false;
@@ -434,10 +433,12 @@ void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
     StreamingReporterBase::sectionEnded(_sectionStats);
 }
 
-void ConsoleReporter::benchmarkPreparing(std::string const& name) {
+void ConsoleReporter::benchmarkPreparing( StringRef name ) {
 	lazyPrintWithoutClosingBenchmarkTable();
 
-	auto nameCol = TextFlow::Column(name).width(static_cast<std::size_t>(m_tablePrinter->columnInfos()[0].width - 2));
+	auto nameCol = TextFlow::Column( static_cast<std::string>( name ) )
+                       .width( static_cast<std::size_t>(
+                           m_tablePrinter->columnInfos()[0].width - 2 ) );
 
 	bool firstLine = true;
 	for (auto line : nameCol) {
@@ -473,7 +474,7 @@ void ConsoleReporter::benchmarkEnded(BenchmarkStats<> const& stats) {
     }
 }
 
-void ConsoleReporter::benchmarkFailed(std::string const& error) {
+void ConsoleReporter::benchmarkFailed( StringRef error ) {
 	Colour colour(Colour::Red);
     (*m_tablePrinter)
         << "Benchmark failed (" << error << ')'
@@ -485,19 +486,10 @@ void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
     StreamingReporterBase::testCaseEnded(_testCaseStats);
     m_headerPrinted = false;
 }
-void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) {
-    if (currentGroupInfo.used) {
-        printSummaryDivider();
-        stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
-        printTotals(_testGroupStats.totals);
-        stream << '\n' << std::endl;
-    }
-    StreamingReporterBase::testGroupEnded(_testGroupStats);
-}
 void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
     printTotalsDivider(_testRunStats.totals);
     printTotals(_testRunStats.totals);
-    stream << std::endl;
+    stream << '\n' << std::flush;
     StreamingReporterBase::testRunEnded(_testRunStats);
 }
 void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
@@ -513,11 +505,9 @@ void ConsoleReporter::lazyPrint() {
 
 void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
 
-    if (!currentTestRunInfo.used)
+    if ( !currentTestRunInfo.used ) {
         lazyPrintRunInfo();
-    if (!currentGroupInfo.used)
-        lazyPrintGroupInfo();
-
+    }
     if (!m_headerPrinted) {
         printTestCaseAndSectionHeader();
         m_headerPrinted = true;
@@ -535,12 +525,6 @@ void ConsoleReporter::lazyPrintRunInfo() {
 
     currentTestRunInfo.used = true;
 }
-void ConsoleReporter::lazyPrintGroupInfo() {
-    if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) {
-        printClosedHeader("Group: " + currentGroupInfo->name);
-        currentGroupInfo.used = true;
-    }
-}
 void ConsoleReporter::printTestCaseAndSectionHeader() {
     assert(!m_sectionStack.empty());
     printOpenHeader(currentTestCaseInfo->name);
@@ -561,7 +545,7 @@ void ConsoleReporter::printTestCaseAndSectionHeader() {
     stream << lineOfChars('-') << '\n';
     Colour colourGuard(Colour::FileName);
     stream << lineInfo << '\n';
-    stream << lineOfChars('.') << '\n' << std::endl;
+    stream << lineOfChars('.') << "\n\n" << std::flush;
 }
 
 void ConsoleReporter::printClosedHeader(std::string const& _name) {
@@ -590,7 +574,7 @@ void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t
 struct SummaryColumn {
 
     SummaryColumn( std::string _label, Colour::Code _colour )
-    :   label( std::move( _label ) ),
+    :   label( CATCH_MOVE( _label ) ),
         colour( _colour ) {}
     SummaryColumn addRow( std::size_t count ) {
         ReusableStringStream rss;
@@ -618,8 +602,8 @@ void ConsoleReporter::printTotals( Totals const& totals ) {
     } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
         stream << Colour(Colour::ResultSuccess) << "All tests passed";
         stream << " ("
-            << pluralise(totals.assertions.passed, "assertion") << " in "
-            << pluralise(totals.testCases.passed, "test case") << ')'
+            << pluralise(totals.assertions.passed, "assertion"_sr) << " in "
+            << pluralise(totals.testCases.passed, "test case"_sr) << ')'
             << '\n';
     } else {
 
@@ -637,11 +621,11 @@ void ConsoleReporter::printTotals( Totals const& totals ) {
                           .addRow(totals.testCases.failedButOk)
                           .addRow(totals.assertions.failedButOk));
 
-        printSummaryRow("test cases", columns, 0);
-        printSummaryRow("assertions", columns, 1);
+        printSummaryRow("test cases"_sr, columns, 0);
+        printSummaryRow("assertions"_sr, columns, 1);
     }
 }
-void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) {
+void ConsoleReporter::printSummaryRow(StringRef label, std::vector<SummaryColumn> const& cols, std::size_t row) {
     for (auto col : cols) {
         std::string value = col.rows[row];
         if (col.label.empty()) {
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_console.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_console.hpp
index e446d8cc385f12de5517550155121ecb8cbc0c4e..ec7430935205769d18d36d91c82394c209d59bfa 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_console.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_console.hpp
@@ -11,44 +11,34 @@
 #include <catch2/reporters/catch_reporter_streaming_base.hpp>
 #include <catch2/internal/catch_unique_ptr.hpp>
 
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
-                              // Note that 4062 (not all labels are handled
-                              // and default is missing) is enabled
-#endif
-
-
 namespace Catch {
     // Fwd decls
     struct SummaryColumn;
     class TablePrinter;
 
-    struct ConsoleReporter : StreamingReporterBase {
+    struct ConsoleReporter final : StreamingReporterBase {
         Detail::unique_ptr<TablePrinter> m_tablePrinter;
 
         ConsoleReporter(ReporterConfig const& config);
         ~ConsoleReporter() override;
         static std::string getDescription();
 
-        void noMatchingTestCases(std::string const& spec) override;
-
-        void reportInvalidArguments(std::string const&arg) override;
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
+        void reportInvalidArguments( StringRef arg ) override;
 
         void assertionStarting(AssertionInfo const&) override;
 
-        bool assertionEnded(AssertionStats const& _assertionStats) override;
+        void assertionEnded(AssertionStats const& _assertionStats) override;
 
         void sectionStarting(SectionInfo const& _sectionInfo) override;
         void sectionEnded(SectionStats const& _sectionStats) override;
 
-        void benchmarkPreparing(std::string const& name) override;
+        void benchmarkPreparing( StringRef name ) override;
         void benchmarkStarting(BenchmarkInfo const& info) override;
         void benchmarkEnded(BenchmarkStats<> const& stats) override;
-        void benchmarkFailed(std::string const& error) override;
+        void benchmarkFailed( StringRef error ) override;
 
         void testCaseEnded(TestCaseStats const& _testCaseStats) override;
-        void testGroupEnded(TestGroupStats const& _testGroupStats) override;
         void testRunEnded(TestRunStats const& _testRunStats) override;
         void testRunStarting(TestRunInfo const& _testRunInfo) override;
     private:
@@ -57,7 +47,6 @@ namespace Catch {
 
         void lazyPrintWithoutClosingBenchmarkTable();
         void lazyPrintRunInfo();
-        void lazyPrintGroupInfo();
         void printTestCaseAndSectionHeader();
 
         void printClosedHeader(std::string const& _name);
@@ -69,7 +58,7 @@ namespace Catch {
 
 
         void printTotals(Totals const& totals);
-        void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row);
+        void printSummaryRow(StringRef label, std::vector<SummaryColumn> const& cols, std::size_t row);
 
         void printTotalsDivider(Totals const& totals);
         void printSummaryDivider();
@@ -81,8 +70,4 @@ namespace Catch {
 
 } // end namespace Catch
 
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
 #endif // CATCH_REPORTER_CONSOLE_HPP_INCLUDED
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp
index a83a9ad0abd55b89373766327b77a96732aa593a..64afac18c93d8303919eba2208b76acfb72dac9c 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.cpp
@@ -54,7 +54,7 @@ namespace Catch {
                 auto newNode =
                     Detail::make_unique<SectionNode>( incompleteStats );
                 node = newNode.get();
-                parentNode.childSections.push_back( std::move( newNode ) );
+                parentNode.childSections.push_back( CATCH_MOVE( newNode ) );
             } else {
                 node = it->get();
             }
@@ -64,7 +64,7 @@ namespace Catch {
         m_sectionStack.push_back( node );
     }
 
-    bool CumulativeReporterBase::assertionEnded(
+    void CumulativeReporterBase::assertionEnded(
         AssertionStats const& assertionStats ) {
         assert( !m_sectionStack.empty() );
         // AssertionResult holds a pointer to a temporary DecomposedExpression,
@@ -76,7 +76,6 @@ namespace Catch {
             assertionStats.assertionResult.getExpandedExpression() );
         SectionNode& sectionNode = *m_sectionStack.back();
         sectionNode.assertions.push_back( assertionStats );
-        return true;
     }
 
     void CumulativeReporterBase::sectionEnded( SectionStats const& sectionStats ) {
@@ -90,24 +89,19 @@ namespace Catch {
         TestCaseStats const& testCaseStats ) {
         auto node = Detail::make_unique<TestCaseNode>( testCaseStats );
         assert( m_sectionStack.size() == 0 );
-        node->children.push_back( std::move(m_rootSection) );
-        m_testCases.push_back( std::move(node) );
+        node->children.push_back( CATCH_MOVE(m_rootSection) );
+        m_testCases.push_back( CATCH_MOVE(node) );
 
         assert( m_deepestSection );
         m_deepestSection->stdOut = testCaseStats.stdOut;
         m_deepestSection->stdErr = testCaseStats.stdErr;
     }
 
-    void CumulativeReporterBase::testGroupEnded(
-        TestGroupStats const& testGroupStats ) {
-        auto node = Detail::make_unique<TestGroupNode>( testGroupStats );
-        node->children.swap( m_testCases );
-        m_testGroups.push_back( std::move(node) );
-    }
 
     void CumulativeReporterBase::testRunEnded( TestRunStats const& testRunStats ) {
-        m_testRuns.emplace_back( testRunStats );
-        m_testRuns.back().children.swap( m_testGroups );
+        assert(!m_testRun && "CumulativeReporterBase assumes there can only be one test run");
+        m_testRun = Detail::make_unique<TestRunNode>( testRunStats );
+        m_testRun->children.swap( m_testCases );
         testRunEndedCumulative();
     }
 
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp
index 2820e9f6223022850513bc199254dc7110523a1a..a6acc54f75b9c3750c9b9c66ed5d05608260f8cf 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_cumulative_base.hpp
@@ -42,27 +42,35 @@ namespace Catch {
 
 
         using TestCaseNode = Node<TestCaseStats, SectionNode>;
-        using TestGroupNode = Node<TestGroupStats, TestCaseNode>;
-        using TestRunNode = Node<TestRunStats, TestGroupNode>;
+        using TestRunNode = Node<TestRunStats, TestCaseNode>;
 
         CumulativeReporterBase( ReporterConfig const& _config ):
             IStreamingReporter( _config.fullConfig() ),
             stream( _config.stream() ) {}
         ~CumulativeReporterBase() override;
 
+        void benchmarkPreparing( StringRef ) override {}
+        void benchmarkStarting( BenchmarkInfo const& ) override {}
+        void benchmarkEnded( BenchmarkStats<> const& ) override {}
+        void benchmarkFailed( StringRef ) override {}
+
+        void noMatchingTestCases( StringRef ) override {}
+        void reportInvalidArguments( StringRef ) override {}
+        void fatalErrorEncountered( StringRef /*error*/ ) override {}
+
+
         void testRunStarting( TestRunInfo const& ) override {}
-        void testGroupStarting( GroupInfo const& ) override {}
 
         void testCaseStarting( TestCaseInfo const& ) override {}
-
+        void testCasePartialStarting( TestCaseInfo const&, uint64_t ) override {}
         void sectionStarting( SectionInfo const& sectionInfo ) override;
 
         void assertionStarting( AssertionInfo const& ) override {}
 
-        bool assertionEnded( AssertionStats const& assertionStats ) override;
+        void assertionEnded( AssertionStats const& assertionStats ) override;
         void sectionEnded( SectionStats const& sectionStats ) override;
+        void testCasePartialEnded( TestCaseStats const&, uint64_t ) override {}
         void testCaseEnded( TestCaseStats const& testCaseStats ) override;
-        void testGroupEnded( TestGroupStats const& testGroupStats ) override;
         void testRunEnded( TestRunStats const& testRunStats ) override;
         //! Customization point: called after last test finishes (testRunEnded has been handled)
         virtual void testRunEndedCumulative() = 0;
@@ -73,14 +81,14 @@ namespace Catch {
         void listTests( std::vector<TestCaseHandle> const& tests ) override;
         void listTags( std::vector<TagInfo> const& tags ) override;
 
-
+    protected:
         std::ostream& stream;
         // Note: We rely on pointer identity being stable, which is why
-        //       which is why we store around pointers rather than values.
+        //       we store pointers to the nodes rather than the values.
         std::vector<Detail::unique_ptr<TestCaseNode>> m_testCases;
-        std::vector<Detail::unique_ptr<TestGroupNode>> m_testGroups;
-
-        std::vector<TestRunNode> m_testRuns;
+        // We need lazy construction here. We should probably refactor it
+        // later, after the events are redone.
+        Detail::unique_ptr<TestRunNode> m_testRun;
 
         Detail::unique_ptr<SectionNode> m_rootSection;
         SectionNode* m_deepestSection = nullptr;
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_event_listener.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_event_listener.hpp
index ee65eebdfae30d4863315872d88e01488f59c715..d2cccb6be0cc8f58bcdc1db9cb2f52825ddec587 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_event_listener.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_event_listener.hpp
@@ -15,7 +15,7 @@ namespace Catch {
     /**
      * Base class identifying listeners.
      *
-     * Provides default implementation for all IStreamingReporter member
+     * Provides empty default implementation for all IStreamingReporter member
      * functions, so that listeners implementations can pick which
      * member functions it actually cares about.
      */
@@ -24,24 +24,35 @@ namespace Catch {
         EventListenerBase( ReporterConfig const& config ):
             IStreamingReporter( config.fullConfig() ) {}
 
+        void reportInvalidArguments( StringRef unmatchedSpec ) override;
+        void fatalErrorEncountered( StringRef error ) override;
+
+        void benchmarkPreparing( StringRef name ) override;
+        void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
+        void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
+        void benchmarkFailed( StringRef error ) override;
+
         void assertionStarting( AssertionInfo const& assertionInfo ) override;
-        bool assertionEnded( AssertionStats const& assertionStats ) override;
+        void assertionEnded( AssertionStats const& assertionStats ) override;
 
         void listReporters(
             std::vector<ReporterDescription> const& descriptions ) override;
         void listTests( std::vector<TestCaseHandle> const& tests ) override;
         void listTags( std::vector<TagInfo> const& tagInfos ) override;
 
-        void noMatchingTestCases( std::string const& spec ) override;
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
         void testRunStarting( TestRunInfo const& testRunInfo ) override;
-        void testGroupStarting( GroupInfo const& groupInfo ) override;
         void testCaseStarting( TestCaseInfo const& testInfo ) override;
+        void testCasePartialStarting( TestCaseInfo const& testInfo,
+                                      uint64_t partNumber ) override;
         void sectionStarting( SectionInfo const& sectionInfo ) override;
         void sectionEnded( SectionStats const& sectionStats ) override;
+        void testCasePartialEnded( TestCaseStats const& testCaseStats,
+                                   uint64_t partNumber ) override;
         void testCaseEnded( TestCaseStats const& testCaseStats ) override;
-        void testGroupEnded( TestGroupStats const& testGroupStats ) override;
         void testRunEnded( TestRunStats const& testRunStats ) override;
         void skipTest( TestCaseInfo const& testInfo ) override;
+
     };
 
 } // end namespace Catch
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_junit.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_junit.cpp
index cff5d7d7caf939dcd1f60155267dd85b9e785648..f7420b7ada063b1b48a8d9f68eeb65015fd86a09 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_junit.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_junit.cpp
@@ -26,7 +26,7 @@ namespace Catch {
             std::time(&rawtime);
 
             std::tm timeInfo = {};
-#ifdef _MSC_VER
+#if defined (_MSC_VER) || defined (__MINGW32__)
             gmtime_s(&timeInfo, &rawtime);
 #else
             gmtime_r(&rawtime, &timeInfo);
@@ -38,7 +38,7 @@ namespace Catch {
 
             std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
 
-            return std::string(timeStamp);
+            return std::string(timeStamp, timeStampSize - 1);
         }
 
         std::string fileNameTag(std::vector<Tag> const& tags) {
@@ -64,35 +64,27 @@ namespace Catch {
             m_preferences.shouldReportAllAssertions = true;
         }
 
-    JunitReporter::~JunitReporter() {}
-
     std::string JunitReporter::getDescription() {
         return "Reports test results in an XML format that looks like Ant's junitreport target";
     }
 
-    void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {}
-
     void JunitReporter::testRunStarting( TestRunInfo const& runInfo )  {
         CumulativeReporterBase::testRunStarting( runInfo );
         xml.startElement( "testsuites" );
-    }
-
-    void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) {
         suiteTimer.start();
         stdOutForSuite.clear();
         stdErrForSuite.clear();
         unexpectedExceptions = 0;
-        CumulativeReporterBase::testGroupStarting( groupInfo );
     }
 
     void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) {
         m_okToFail = testCaseInfo.okToFail();
     }
 
-    bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
+    void JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
         if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
             unexpectedExceptions++;
-        return CumulativeReporterBase::assertionEnded( assertionStats );
+        CumulativeReporterBase::assertionEnded( assertionStats );
     }
 
     void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
@@ -101,48 +93,44 @@ namespace Catch {
         CumulativeReporterBase::testCaseEnded( testCaseStats );
     }
 
-    void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
-        double suiteTime = suiteTimer.getElapsedSeconds();
-        CumulativeReporterBase::testGroupEnded( testGroupStats );
-        writeGroup( *m_testGroups.back(), suiteTime );
-    }
-
     void JunitReporter::testRunEndedCumulative() {
+        const auto suiteTime = suiteTimer.getElapsedSeconds();
+        writeRun( *m_testRun, suiteTime );
         xml.endElement();
     }
 
-    void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+    void JunitReporter::writeRun( TestRunNode const& testRunNode, double suiteTime ) {
         XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
 
-        TestGroupStats const& stats = groupNode.value;
-        xml.writeAttribute( "name", stats.groupInfo.name );
-        xml.writeAttribute( "errors", unexpectedExceptions );
-        xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
-        xml.writeAttribute( "tests", stats.totals.assertions.total() );
-        xml.writeAttribute( "hostname", "tbd" ); // !TBD
+        TestRunStats const& stats = testRunNode.value;
+        xml.writeAttribute( "name"_sr, stats.runInfo.name );
+        xml.writeAttribute( "errors"_sr, unexpectedExceptions );
+        xml.writeAttribute( "failures"_sr, stats.totals.assertions.failed-unexpectedExceptions );
+        xml.writeAttribute( "tests"_sr, stats.totals.assertions.total() );
+        xml.writeAttribute( "hostname"_sr, "tbd"_sr ); // !TBD
         if( m_config->showDurations() == ShowDurations::Never )
-            xml.writeAttribute( "time", "" );
+            xml.writeAttribute( "time"_sr, ""_sr );
         else
-            xml.writeAttribute( "time", suiteTime );
-        xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+            xml.writeAttribute( "time"_sr, suiteTime );
+        xml.writeAttribute( "timestamp"_sr, getCurrentTimestamp() );
 
         // Write properties if there are any
         if (m_config->hasTestFilters() || m_config->rngSeed() != 0) {
             auto properties = xml.scopedElement("properties");
             if (m_config->hasTestFilters()) {
                 xml.scopedElement("property")
-                    .writeAttribute("name", "filters")
-                    .writeAttribute("value", serializeFilters(m_config->getTestsOrTags()));
+                    .writeAttribute("name"_sr, "filters"_sr)
+                    .writeAttribute("value"_sr, serializeFilters(m_config->getTestsOrTags()));
             }
             if (m_config->rngSeed() != 0) {
                 xml.scopedElement("property")
-                    .writeAttribute("name", "random-seed")
-                    .writeAttribute("value", m_config->rngSeed());
+                    .writeAttribute("name"_sr, "random-seed"_sr)
+                    .writeAttribute("value"_sr, m_config->rngSeed());
             }
         }
 
         // Write test cases
-        for( auto const& child : groupNode.children )
+        for( auto const& child : testRunNode.children )
             writeTestCase( *child );
 
         xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), XmlFormatting::Newline );
@@ -183,19 +171,19 @@ namespace Catch {
             !sectionNode.stdErr.empty() ) {
             XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
             if( className.empty() ) {
-                xml.writeAttribute( "classname", name );
-                xml.writeAttribute( "name", "root" );
+                xml.writeAttribute( "classname"_sr, name );
+                xml.writeAttribute( "name"_sr, "root"_sr );
             }
             else {
-                xml.writeAttribute( "classname", className );
-                xml.writeAttribute( "name", name );
+                xml.writeAttribute( "classname"_sr, className );
+                xml.writeAttribute( "name"_sr, name );
             }
-            xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
+            xml.writeAttribute( "time"_sr, ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
             // This is not ideal, but it should be enough to mimic gtest's
             // junit output.
             // Ideally the JUnit reporter would also handle `skipTest`
             // events and write those out appropriately.
-            xml.writeAttribute( "status", "run" );
+            xml.writeAttribute( "status"_sr, "run"_sr );
 
             writeAssertions( sectionNode );
 
@@ -244,8 +232,8 @@ namespace Catch {
 
             XmlWriter::ScopedElement e = xml.scopedElement( elementName );
 
-            xml.writeAttribute( "message", result.getExpression() );
-            xml.writeAttribute( "type", result.getTestMacroName() );
+            xml.writeAttribute( "message"_sr, result.getExpression() );
+            xml.writeAttribute( "type"_sr, result.getTestMacroName() );
 
             ReusableStringStream rss;
             if (stats.totals.assertions.total() > 0) {
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_junit.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_junit.hpp
index c76ddddefd1f6bd91fe3939299db47c8f06cc4e6..6665663cee03082d05cf3b7a4d8b29c9f85c5916 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_junit.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_junit.hpp
@@ -15,36 +15,31 @@
 
 namespace Catch {
 
-    class JunitReporter : public CumulativeReporterBase {
+    class JunitReporter final : public CumulativeReporterBase {
     public:
         JunitReporter(ReporterConfig const& _config);
 
-        ~JunitReporter() override;
+        ~JunitReporter() override = default;
 
         static std::string getDescription();
 
-        void noMatchingTestCases(std::string const& /*spec*/) override;
-
         void testRunStarting(TestRunInfo const& runInfo) override;
 
-        void testGroupStarting(GroupInfo const& groupInfo) override;
-
         void testCaseStarting(TestCaseInfo const& testCaseInfo) override;
-        bool assertionEnded(AssertionStats const& assertionStats) override;
+        void assertionEnded(AssertionStats const& assertionStats) override;
 
         void testCaseEnded(TestCaseStats const& testCaseStats) override;
 
-        void testGroupEnded(TestGroupStats const& testGroupStats) override;
-
         void testRunEndedCumulative() override;
 
-        void writeGroup(TestGroupNode const& groupNode, double suiteTime);
+    private:
+        void writeRun(TestRunNode const& testRunNode, double suiteTime);
 
         void writeTestCase(TestCaseNode const& testCaseNode);
 
-        void writeSection(std::string const& className,
-                          std::string const& rootName,
-                          SectionNode const& sectionNode);
+        void writeSection( std::string const& className,
+                           std::string const& rootName,
+                           SectionNode const& sectionNode );
 
         void writeAssertions(SectionNode const& sectionNode);
         void writeAssertion(AssertionStats const& stats);
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_listening.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_listening.cpp
index d189388b1c140f99a42d2632ee3c3fe23016dbdd..f233b0a2ec3f97d67e4d6e11d3c82688b35f0964 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_listening.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_listening.cpp
@@ -6,128 +6,138 @@
 
 // SPDX-License-Identifier: BSL-1.0
 #include <catch2/reporters/catch_reporter_listening.hpp>
+#include <catch2/internal/catch_move_and_forward.hpp>
 
 #include <cassert>
 
 namespace Catch {
 
     void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) {
-        m_listeners.push_back( std::move( listener ) );
+        m_listeners.push_back( CATCH_MOVE( listener ) );
     }
 
     void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) {
         assert(!m_reporter && "Listening reporter can wrap only 1 real reporter");
-        m_reporter = std::move( reporter );
+        m_reporter = CATCH_MOVE( reporter );
         m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut;
     }
 
-    void ListeningReporter::noMatchingTestCases( std::string const& spec ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->noMatchingTestCases( spec );
+    void ListeningReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+        for ( auto& listener : m_listeners ) {
+            listener->noMatchingTestCases( unmatchedSpec );
         }
-        m_reporter->noMatchingTestCases( spec );
+        m_reporter->noMatchingTestCases( unmatchedSpec );
     }
 
-    void ListeningReporter::reportInvalidArguments(std::string const&arg){
-        for ( auto const& listener : m_listeners ) {
+    void ListeningReporter::fatalErrorEncountered( StringRef error ) {
+        for ( auto& listener : m_listeners ) {
+            listener->fatalErrorEncountered( error );
+        }
+        m_reporter->fatalErrorEncountered( error );
+    }
+
+    void ListeningReporter::reportInvalidArguments( StringRef arg ) {
+        for ( auto& listener : m_listeners ) {
             listener->reportInvalidArguments( arg );
         }
         m_reporter->reportInvalidArguments( arg );
     }
 
-    void ListeningReporter::benchmarkPreparing( std::string const& name ) {
-        for (auto const& listener : m_listeners) {
+    void ListeningReporter::benchmarkPreparing( StringRef name ) {
+        for (auto& listener : m_listeners) {
             listener->benchmarkPreparing(name);
         }
         m_reporter->benchmarkPreparing(name);
     }
     void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
-        for ( auto const& listener : m_listeners ) {
+        for ( auto& listener : m_listeners ) {
             listener->benchmarkStarting( benchmarkInfo );
         }
         m_reporter->benchmarkStarting( benchmarkInfo );
     }
     void ListeningReporter::benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) {
-        for ( auto const& listener : m_listeners ) {
+        for ( auto& listener : m_listeners ) {
             listener->benchmarkEnded( benchmarkStats );
         }
         m_reporter->benchmarkEnded( benchmarkStats );
     }
 
-    void ListeningReporter::benchmarkFailed( std::string const& error ) {
-        for (auto const& listener : m_listeners) {
+    void ListeningReporter::benchmarkFailed( StringRef error ) {
+        for (auto& listener : m_listeners) {
             listener->benchmarkFailed(error);
         }
         m_reporter->benchmarkFailed(error);
     }
 
     void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
-        for ( auto const& listener : m_listeners ) {
+        for ( auto& listener : m_listeners ) {
             listener->testRunStarting( testRunInfo );
         }
         m_reporter->testRunStarting( testRunInfo );
     }
 
-    void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->testGroupStarting( groupInfo );
-        }
-        m_reporter->testGroupStarting( groupInfo );
-    }
-
-
     void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
-        for ( auto const& listener : m_listeners ) {
+        for ( auto& listener : m_listeners ) {
             listener->testCaseStarting( testInfo );
         }
         m_reporter->testCaseStarting( testInfo );
     }
 
+    void
+    ListeningReporter::testCasePartialStarting( TestCaseInfo const& testInfo,
+                                                uint64_t partNumber ) {
+        for ( auto& listener : m_listeners ) {
+            listener->testCasePartialStarting( testInfo, partNumber );
+        }
+        m_reporter->testCasePartialStarting( testInfo, partNumber );
+    }
+
     void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) {
-        for ( auto const& listener : m_listeners ) {
+        for ( auto& listener : m_listeners ) {
             listener->sectionStarting( sectionInfo );
         }
         m_reporter->sectionStarting( sectionInfo );
     }
 
     void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) {
-        for ( auto const& listener : m_listeners ) {
+        for ( auto& listener : m_listeners ) {
             listener->assertionStarting( assertionInfo );
         }
         m_reporter->assertionStarting( assertionInfo );
     }
 
     // The return value indicates if the messages buffer should be cleared:
-    bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) {
-        for( auto const& listener : m_listeners ) {
-            static_cast<void>( listener->assertionEnded( assertionStats ) );
+    void ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) {
+        for( auto& listener : m_listeners ) {
+            listener->assertionEnded( assertionStats );
         }
-        return m_reporter->assertionEnded( assertionStats );
+        m_reporter->assertionEnded( assertionStats );
     }
 
     void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) {
-        for ( auto const& listener : m_listeners ) {
+        for ( auto& listener : m_listeners ) {
             listener->sectionEnded( sectionStats );
         }
         m_reporter->sectionEnded( sectionStats );
     }
 
-    void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->testCaseEnded( testCaseStats );
+    void ListeningReporter::testCasePartialEnded( TestCaseStats const& testInfo,
+                                                  uint64_t partNumber ) {
+        for ( auto& listener : m_listeners ) {
+            listener->testCasePartialEnded( testInfo, partNumber );
         }
-        m_reporter->testCaseEnded( testCaseStats );
+        m_reporter->testCasePartialEnded( testInfo, partNumber );
     }
 
-    void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->testGroupEnded( testGroupStats );
+    void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        for ( auto& listener : m_listeners ) {
+            listener->testCaseEnded( testCaseStats );
         }
-        m_reporter->testGroupEnded( testGroupStats );
+        m_reporter->testCaseEnded( testCaseStats );
     }
 
     void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) {
-        for ( auto const& listener : m_listeners ) {
+        for ( auto& listener : m_listeners ) {
             listener->testRunEnded( testRunStats );
         }
         m_reporter->testRunEnded( testRunStats );
@@ -135,28 +145,28 @@ namespace Catch {
 
 
     void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) {
-        for ( auto const& listener : m_listeners ) {
+        for ( auto& listener : m_listeners ) {
             listener->skipTest( testInfo );
         }
         m_reporter->skipTest( testInfo );
     }
 
     void ListeningReporter::listReporters(std::vector<ReporterDescription> const& descriptions) {
-        for (auto const& listener : m_listeners) {
+        for (auto& listener : m_listeners) {
             listener->listReporters(descriptions);
         }
         m_reporter->listReporters(descriptions);
     }
 
     void ListeningReporter::listTests(std::vector<TestCaseHandle> const& tests) {
-        for (auto const& listener : m_listeners) {
+        for (auto& listener : m_listeners) {
             listener->listTests(tests);
         }
         m_reporter->listTests(tests);
     }
 
     void ListeningReporter::listTags(std::vector<TagInfo> const& tags) {
-        for (auto const& listener : m_listeners) {
+        for (auto& listener : m_listeners) {
             listener->listTags(tags);
         }
         m_reporter->listTags(tags);
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_listening.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_listening.hpp
index 88234cdc334bd2007f6cf1cbe9e60d71948c771e..caa5c1a84540ce176cd8bd313b28a102546a2545 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_listening.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_listening.hpp
@@ -30,26 +30,25 @@ namespace Catch {
 
     public: // IStreamingReporter
 
-        void noMatchingTestCases( std::string const& spec ) override;
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
+        void fatalErrorEncountered( StringRef error ) override;
+        void reportInvalidArguments( StringRef arg ) override;
 
-        void reportInvalidArguments(std::string const&arg) override;
-
-        void benchmarkPreparing(std::string const& name) override;
+        void benchmarkPreparing( StringRef name ) override;
         void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
         void benchmarkEnded( BenchmarkStats<> const& benchmarkStats ) override;
-        void benchmarkFailed(std::string const&) override;
+        void benchmarkFailed( StringRef error ) override;
 
         void testRunStarting( TestRunInfo const& testRunInfo ) override;
-        void testGroupStarting( GroupInfo const& groupInfo ) override;
         void testCaseStarting( TestCaseInfo const& testInfo ) override;
+        void testCasePartialStarting(TestCaseInfo const& testInfo, uint64_t partNumber) override;
         void sectionStarting( SectionInfo const& sectionInfo ) override;
         void assertionStarting( AssertionInfo const& assertionInfo ) override;
 
-        // The return value indicates if the messages buffer should be cleared:
-        bool assertionEnded( AssertionStats const& assertionStats ) override;
+        void assertionEnded( AssertionStats const& assertionStats ) override;
         void sectionEnded( SectionStats const& sectionStats ) override;
+        void testCasePartialEnded(TestCaseStats const& testInfo, uint64_t partNumber) override;
         void testCaseEnded( TestCaseStats const& testCaseStats ) override;
-        void testGroupEnded( TestGroupStats const& testGroupStats ) override;
         void testRunEnded( TestRunStats const& testRunStats ) override;
 
         void skipTest( TestCaseInfo const& testInfo ) override;
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp
index 18ab3134eea90368184dcd2bc1c2193a2fae59c7..e09b51090491372899d7aa4b0fef3da448d19e52 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.cpp
@@ -14,33 +14,28 @@
 
 namespace Catch {
 
-    SonarQubeReporter::~SonarQubeReporter() {}
-
     void SonarQubeReporter::testRunStarting(TestRunInfo const& testRunInfo) {
         CumulativeReporterBase::testRunStarting(testRunInfo);
         xml.startElement("testExecutions");
-        xml.writeAttribute("version", '1');
-    }
-
-    void SonarQubeReporter::testGroupEnded(TestGroupStats const& testGroupStats) {
-        CumulativeReporterBase::testGroupEnded(testGroupStats);
-        writeGroup(*m_testGroups.back());
+        xml.writeAttribute("version"_sr, '1');
     }
 
-    void SonarQubeReporter::writeGroup(TestGroupNode const& groupNode) {
+    void SonarQubeReporter::writeRun( TestRunNode const& runNode ) {
         std::map<std::string, std::vector<TestCaseNode const*>> testsPerFile;
-        for ( auto const& child : groupNode.children ) {
+
+        for ( auto const& child : runNode.children ) {
             testsPerFile[child->value.testInfo->lineInfo.file].push_back(
                 child.get() );
         }
 
-        for (auto const& kv : testsPerFile)
-            writeTestFile(kv.first, kv.second);
+        for ( auto const& kv : testsPerFile ) {
+            writeTestFile( kv.first, kv.second );
+        }
     }
 
     void SonarQubeReporter::writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes) {
         XmlWriter::ScopedElement e = xml.scopedElement("file");
-        xml.writeAttribute("path", filename);
+        xml.writeAttribute("path"_sr, filename);
 
         for (auto const& child : testCaseNodes)
             writeTestCase(*child);
@@ -61,8 +56,8 @@ namespace Catch {
 
         if (!sectionNode.assertions.empty() || !sectionNode.stdOut.empty() || !sectionNode.stdErr.empty()) {
             XmlWriter::ScopedElement e = xml.scopedElement("testCase");
-            xml.writeAttribute("name", name);
-            xml.writeAttribute("duration", static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
+            xml.writeAttribute("name"_sr, name);
+            xml.writeAttribute("duration"_sr, static_cast<long>(sectionNode.stats.durationInSeconds * 1000));
 
             writeAssertions(sectionNode, okToFail);
         }
@@ -113,26 +108,26 @@ namespace Catch {
             XmlWriter::ScopedElement e = xml.scopedElement(elementName);
 
             ReusableStringStream messageRss;
-            messageRss << result.getTestMacroName() << "(" << result.getExpression() << ")";
-            xml.writeAttribute("message", messageRss.str());
+            messageRss << result.getTestMacroName() << '(' << result.getExpression() << ')';
+            xml.writeAttribute("message"_sr, messageRss.str());
 
             ReusableStringStream textRss;
             if (stats.totals.assertions.total() > 0) {
                 textRss << "FAILED:\n";
                 if (result.hasExpression()) {
-                    textRss << "\t" << result.getExpressionInMacro() << "\n";
+                    textRss << '\t' << result.getExpressionInMacro() << '\n';
                 }
                 if (result.hasExpandedExpression()) {
-                    textRss << "with expansion:\n\t" << result.getExpandedExpression() << "\n";
+                    textRss << "with expansion:\n\t" << result.getExpandedExpression() << '\n';
                 }
             }
 
             if (!result.getMessage().empty())
-                textRss << result.getMessage() << "\n";
+                textRss << result.getMessage() << '\n';
 
             for (auto const& msg : stats.infoMessages)
                 if (msg.type == ResultWas::Info)
-                    textRss << msg.message << "\n";
+                    textRss << msg.message << '\n';
 
             textRss << "at " << result.getSourceInfo();
             xml.writeText(textRss.str(), XmlFormatting::Newline);
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp
index d46cb422d797876201d922dab03402cf63b8dc82..720c0ea0fb4f7b63c899cfa4b2c2ad11a9989686 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_sonarqube.hpp
@@ -14,7 +14,7 @@
 
 namespace Catch {
 
-    struct SonarQubeReporter : CumulativeReporterBase {
+    struct SonarQubeReporter final : CumulativeReporterBase {
 
         SonarQubeReporter(ReporterConfig const& config)
         : CumulativeReporterBase(config)
@@ -23,24 +23,21 @@ namespace Catch {
             m_preferences.shouldReportAllAssertions = true;
         }
 
-        ~SonarQubeReporter() override;
+        ~SonarQubeReporter() override = default;
 
         static std::string getDescription() {
             using namespace std::string_literals;
             return "Reports test results in the Generic Test Data SonarQube XML format"s;
         }
 
-        void noMatchingTestCases(std::string const& /*spec*/) override {}
-
-        void testRunStarting(TestRunInfo const& testRunInfo) override;
-
-        void testGroupEnded(TestGroupStats const& testGroupStats) override;
+        void testRunStarting( TestRunInfo const& testRunInfo ) override;
 
         void testRunEndedCumulative() override {
+            writeRun( *m_testRun );
             xml.endElement();
         }
 
-        void writeGroup(TestGroupNode const& groupNode);
+        void writeRun( TestRunNode const& groupNode );
 
         void writeTestFile(std::string const& filename, std::vector<TestCaseNode const*> const& testCaseNodes);
 
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_streaming_base.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_streaming_base.cpp
index d0b8ae9491d23778bde4a26f283ce2f70356aea5..1c2787ca01c31c23e5fdbde7739da2ac88b2784c 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_streaming_base.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_streaming_base.cpp
@@ -17,18 +17,8 @@ namespace Catch {
         currentTestRunInfo = _testRunInfo;
     }
 
-    void
-    StreamingReporterBase::testGroupStarting( GroupInfo const& _groupInfo ) {
-        currentGroupInfo = _groupInfo;
-    }
-
-    void StreamingReporterBase::testGroupEnded( TestGroupStats const& ) {
-        currentGroupInfo.reset();
-    }
-
     void StreamingReporterBase::testRunEnded( TestRunStats const& ) {
         currentTestCaseInfo = nullptr;
-        currentGroupInfo.reset();
         currentTestRunInfo.reset();
     }
 
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp
index b0f1dd4969700c8e06045a0462915392fdabb2d0..e7b6e5b3a61e761a35ac0ec7bea5fd9ee6ebec06 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_streaming_base.hpp
@@ -42,28 +42,35 @@ namespace Catch {
 
         ~StreamingReporterBase() override;
 
-        void noMatchingTestCases(std::string const&) override {}
+        void benchmarkPreparing( StringRef ) override {}
+        void benchmarkStarting( BenchmarkInfo const& ) override {}
+        void benchmarkEnded( BenchmarkStats<> const& ) override {}
+        void benchmarkFailed( StringRef ) override {}
 
-        void reportInvalidArguments(std::string const&) override {}
+        void fatalErrorEncountered( StringRef /*error*/ ) override {}
+        void noMatchingTestCases( StringRef /*unmatchedSpec*/ ) override {}
+        void reportInvalidArguments( StringRef /*invalidArgument*/ ) override {}
 
         void testRunStarting( TestRunInfo const& _testRunInfo ) override;
 
-        void testGroupStarting( GroupInfo const& _groupInfo ) override;
-
         void testCaseStarting(TestCaseInfo const& _testInfo) override  {
             currentTestCaseInfo = &_testInfo;
         }
+        void testCasePartialStarting( TestCaseInfo const&, uint64_t ) override {}
         void sectionStarting(SectionInfo const& _sectionInfo) override {
             m_sectionStack.push_back(_sectionInfo);
         }
 
+        void assertionStarting( AssertionInfo const& ) override {}
+        void assertionEnded( AssertionStats const& ) override {}
+
         void sectionEnded(SectionStats const& /* _sectionStats */) override {
             m_sectionStack.pop_back();
         }
+        void testCasePartialEnded( TestCaseStats const&, uint64_t ) override {}
         void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override {
             currentTestCaseInfo = nullptr;
         }
-        void testGroupEnded( TestGroupStats const& ) override;
         void testRunEnded( TestRunStats const& /* _testRunStats */ ) override;
 
         void skipTest(TestCaseInfo const&) override {
@@ -78,7 +85,6 @@ namespace Catch {
         std::ostream& stream;
 
         LazyStat<TestRunInfo> currentTestRunInfo;
-        LazyStat<GroupInfo> currentGroupInfo;
         TestCaseInfo const* currentTestCaseInfo = nullptr;
 
         std::vector<SectionInfo> m_sectionStack;
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_tap.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_tap.cpp
index 6b0d5f42025b0fb01f8a4c5fe1909819ddde05a7..46a0994da8dbd420aea42e7135c7dc17c9947af8 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_tap.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_tap.cpp
@@ -168,7 +168,7 @@ namespace Catch {
 
                 {
                     Colour colourGuard(colour);
-                    stream << " with " << pluralise(N, "message") << ':';
+                    stream << " with " << pluralise(N, "message"_sr) << ':';
                 }
 
                 for (; itMessage != itEnd; ) {
@@ -194,13 +194,11 @@ namespace Catch {
 
     } // End anonymous namespace
 
-    TAPReporter::~TAPReporter() {}
-
-    void TAPReporter::noMatchingTestCases(std::string const& spec) {
-        stream << "# No test cases matched '" << spec << "'\n";
+    void TAPReporter::noMatchingTestCases( StringRef unmatchedSpec ) {
+        stream << "# No test cases matched '" << unmatchedSpec << "'\n";
     }
 
-    bool TAPReporter::assertionEnded(AssertionStats const& _assertionStats) {
+    void TAPReporter::assertionEnded(AssertionStats const& _assertionStats) {
         ++counter;
 
         stream << "# " << currentTestCaseInfo->name << '\n';
@@ -208,7 +206,6 @@ namespace Catch {
         printer.print();
 
         stream << '\n' << std::flush;
-        return true;
     }
 
     void TAPReporter::testRunEnded(TestRunStats const& _testRunStats) {
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_tap.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_tap.hpp
index d8d3e9a4e1caa4e336b2809c0c1f91d723190173..e7c22cfa9c04147b2a5c6ae46de2e79711befb4a 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_tap.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_tap.hpp
@@ -12,24 +12,22 @@
 
 namespace Catch {
 
-    struct TAPReporter : StreamingReporterBase {
+    struct TAPReporter final : StreamingReporterBase {
 
         TAPReporter( ReporterConfig const& config ):
             StreamingReporterBase( config ) {
             m_preferences.shouldReportAllAssertions = true;
         }
-        ~TAPReporter() override;
+        ~TAPReporter() override = default;
 
         static std::string getDescription() {
             using namespace std::string_literals;
             return "Reports test results in TAP format, suitable for test harnesses"s;
         }
 
-        void noMatchingTestCases(std::string const& spec) override;
+        void noMatchingTestCases( StringRef unmatchedSpec ) override;
 
-        void assertionStarting( AssertionInfo const& ) override {}
-
-        bool assertionEnded(AssertionStats const& _assertionStats) override;
+        void assertionEnded(AssertionStats const& _assertionStats) override;
 
         void testRunEnded(TestRunStats const& _testRunStats) override;
 
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.cpp
index 3a417bd48c9598f72dea5fe5ab553a90f556dc93..1972b8c49ffc60402e22484424bfe6a6a5ac0bc4 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.cpp
@@ -31,8 +31,8 @@ namespace Catch {
                   .initialIndent(indent) << '\n';
         }
 
-        std::string escape(std::string const& str) {
-            std::string escaped = str;
+        std::string escape(StringRef str) {
+            std::string escaped = static_cast<std::string>(str);
             replaceInPlace(escaped, "|", "||");
             replaceInPlace(escaped, "'", "|'");
             replaceInPlace(escaped, "\n", "|n");
@@ -46,19 +46,17 @@ namespace Catch {
 
     TeamCityReporter::~TeamCityReporter() {}
 
-    void TeamCityReporter::testGroupStarting(GroupInfo const& groupInfo) {
-        StreamingReporterBase::testGroupStarting(groupInfo);
-        stream << "##teamcity[testSuiteStarted name='"
-            << escape(groupInfo.name) << "']\n";
+    void TeamCityReporter::testRunStarting( TestRunInfo const& runInfo ) {
+        stream << "##teamcity[testSuiteStarted name='" << escape( runInfo.name )
+               << "']\n";
     }
 
-    void TeamCityReporter::testGroupEnded(TestGroupStats const& testGroupStats) {
-        StreamingReporterBase::testGroupEnded(testGroupStats);
+    void TeamCityReporter::testRunEnded( TestRunStats const& runStats ) {
         stream << "##teamcity[testSuiteFinished name='"
-            << escape(testGroupStats.groupInfo.name) << "']\n";
+               << escape( runStats.runInfo.name ) << "']\n";
     }
 
-    bool TeamCityReporter::assertionEnded(AssertionStats const& assertionStats) {
+    void TeamCityReporter::assertionEnded(AssertionStats const& assertionStats) {
         AssertionResult const& result = assertionStats.assertionResult;
         if (!result.isOk()) {
 
@@ -126,7 +124,6 @@ namespace Catch {
             }
         }
         stream.flush();
-        return true;
     }
 
     void TeamCityReporter::testCaseStarting(TestCaseInfo const& testInfo) {
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.hpp
index 300a1e9329765c09244db0b18081f77a5306ae28..7abe8185301f124640e3e71ccbabf1d3f54c5ced 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_teamcity.hpp
@@ -20,7 +20,7 @@
 
 namespace Catch {
 
-    struct TeamCityReporter : StreamingReporterBase {
+    struct TeamCityReporter final : StreamingReporterBase {
         TeamCityReporter( ReporterConfig const& _config )
         :   StreamingReporterBase( _config )
         {
@@ -34,17 +34,11 @@ namespace Catch {
             return "Reports test results as TeamCity service messages"s;
         }
 
-        void skipTest( TestCaseInfo const& /* testInfo */ ) override {}
+        void testRunStarting( TestRunInfo const& groupInfo ) override;
+        void testRunEnded( TestRunStats const& testGroupStats ) override;
 
-        void noMatchingTestCases( std::string const& /* spec */ ) override {}
 
-        void testGroupStarting(GroupInfo const& groupInfo) override;
-        void testGroupEnded(TestGroupStats const& testGroupStats) override;
-
-
-        void assertionStarting(AssertionInfo const&) override {}
-
-        bool assertionEnded(AssertionStats const& assertionStats) override;
+        void assertionEnded(AssertionStats const& assertionStats) override;
 
         void sectionStarting(SectionInfo const& sectionInfo) override {
             m_headerPrintedForThisSection = false;
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_xml.cpp b/packages/Catch2/src/catch2/reporters/catch_reporter_xml.cpp
index 273a9689dc35c11b53c4506b2bb856436b00715c..4e2a9a34fc9ddb16aacb0206563f6db6e9eefc9f 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_xml.cpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_xml.cpp
@@ -42,12 +42,8 @@ namespace Catch {
 
     void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) {
         m_xml
-            .writeAttribute( "filename", sourceInfo.file )
-            .writeAttribute( "line", sourceInfo.line );
-    }
-
-    void XmlReporter::noMatchingTestCases( std::string const& s ) {
-        StreamingReporterBase::noMatchingTestCases( s );
+            .writeAttribute( "filename"_sr, sourceInfo.file )
+            .writeAttribute( "line"_sr, sourceInfo.line );
     }
 
     void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) {
@@ -55,27 +51,21 @@ namespace Catch {
         std::string stylesheetRef = getStylesheetRef();
         if( !stylesheetRef.empty() )
             m_xml.writeStylesheetRef( stylesheetRef );
-        m_xml.startElement( "Catch" );
+        m_xml.startElement( "Catch2TestRun" );
         if( !m_config->name().empty() )
-            m_xml.writeAttribute( "name", m_config->name() );
+            m_xml.writeAttribute( "name"_sr, m_config->name() );
         if (m_config->testSpec().hasFilters())
-            m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) );
+            m_xml.writeAttribute( "filters"_sr, serializeFilters( m_config->getTestsOrTags() ) );
         if( m_config->rngSeed() != 0 )
             m_xml.scopedElement( "Randomness" )
-                .writeAttribute( "seed", m_config->rngSeed() );
-    }
-
-    void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) {
-        StreamingReporterBase::testGroupStarting( groupInfo );
-        m_xml.startElement( "Group" )
-            .writeAttribute( "name", groupInfo.name );
+                .writeAttribute( "seed"_sr, m_config->rngSeed() );
     }
 
     void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
         StreamingReporterBase::testCaseStarting(testInfo);
         m_xml.startElement( "TestCase" )
-            .writeAttribute( "name", trim( testInfo.name ) )
-            .writeAttribute( "tags", testInfo.tagsAsString() );
+            .writeAttribute( "name"_sr, trim( testInfo.name ) )
+            .writeAttribute( "tags"_sr, testInfo.tagsAsString() );
 
         writeSourceInfo( testInfo.lineInfo );
 
@@ -88,7 +78,7 @@ namespace Catch {
         StreamingReporterBase::sectionStarting( sectionInfo );
         if( m_sectionDepth++ > 0 ) {
             m_xml.startElement( "Section" )
-                .writeAttribute( "name", trim( sectionInfo.name ) );
+                .writeAttribute( "name"_sr, trim( sectionInfo.name ) );
             writeSourceInfo( sectionInfo.lineInfo );
             m_xml.ensureTagClosed();
         }
@@ -96,7 +86,7 @@ namespace Catch {
 
     void XmlReporter::assertionStarting( AssertionInfo const& ) { }
 
-    bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
+    void XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
 
         AssertionResult const& result = assertionStats.assertionResult;
 
@@ -117,14 +107,14 @@ namespace Catch {
 
         // Drop out if result was successful but we're not printing them.
         if( !includeResults && result.getResultType() != ResultWas::Warning )
-            return true;
+            return;
 
 
         // Print the expression if there is one.
         if( result.hasExpression() ) {
             m_xml.startElement( "Expression" )
-                .writeAttribute( "success", result.succeeded() )
-                .writeAttribute( "type", result.getTestMacroName() );
+                .writeAttribute( "success"_sr, result.succeeded() )
+                .writeAttribute( "type"_sr, result.getTestMacroName() );
 
             writeSourceInfo( result.getSourceInfo() );
 
@@ -150,7 +140,7 @@ namespace Catch {
                 break;
             case ResultWas::Info:
                 m_xml.scopedElement( "Info" )
-                    .writeText( result.getMessage() );
+                     .writeText( result.getMessage() );
                 break;
             case ResultWas::Warning:
                 // Warning will already have been written
@@ -167,20 +157,18 @@ namespace Catch {
 
         if( result.hasExpression() )
             m_xml.endElement();
-
-        return true;
     }
 
     void XmlReporter::sectionEnded( SectionStats const& sectionStats ) {
         StreamingReporterBase::sectionEnded( sectionStats );
         if( --m_sectionDepth > 0 ) {
             XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
-            e.writeAttribute( "successes", sectionStats.assertions.passed );
-            e.writeAttribute( "failures", sectionStats.assertions.failed );
-            e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+            e.writeAttribute( "successes"_sr, sectionStats.assertions.passed );
+            e.writeAttribute( "failures"_sr, sectionStats.assertions.failed );
+            e.writeAttribute( "expectedFailures"_sr, sectionStats.assertions.failedButOk );
 
             if ( m_config->showDurations() == ShowDurations::Always )
-                e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+                e.writeAttribute( "durationInSeconds"_sr, sectionStats.durationInSeconds );
 
             m_xml.endElement();
         }
@@ -189,10 +177,10 @@ namespace Catch {
     void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
         StreamingReporterBase::testCaseEnded( testCaseStats );
         XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
-        e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+        e.writeAttribute( "success"_sr, testCaseStats.totals.assertions.allOk() );
 
         if ( m_config->showDurations() == ShowDurations::Always )
-            e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+            e.writeAttribute( "durationInSeconds"_sr, m_testCaseTimer.getElapsedSeconds() );
 
         if( !testCaseStats.stdOut.empty() )
             m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), XmlFormatting::Newline );
@@ -202,73 +190,59 @@ namespace Catch {
         m_xml.endElement();
     }
 
-    void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
-        StreamingReporterBase::testGroupEnded( testGroupStats );
-        // TODO: Check testGroupStats.aborting and act accordingly.
-        m_xml.scopedElement( "OverallResults" )
-            .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
-            .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
-            .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
-        m_xml.scopedElement( "OverallResultsCases")
-            .writeAttribute( "successes", testGroupStats.totals.testCases.passed )
-            .writeAttribute( "failures", testGroupStats.totals.testCases.failed )
-            .writeAttribute( "expectedFailures", testGroupStats.totals.testCases.failedButOk );
-        m_xml.endElement();
-    }
-
     void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) {
         StreamingReporterBase::testRunEnded( testRunStats );
         m_xml.scopedElement( "OverallResults" )
-            .writeAttribute( "successes", testRunStats.totals.assertions.passed )
-            .writeAttribute( "failures", testRunStats.totals.assertions.failed )
-            .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+            .writeAttribute( "successes"_sr, testRunStats.totals.assertions.passed )
+            .writeAttribute( "failures"_sr, testRunStats.totals.assertions.failed )
+            .writeAttribute( "expectedFailures"_sr, testRunStats.totals.assertions.failedButOk );
         m_xml.scopedElement( "OverallResultsCases")
-            .writeAttribute( "successes", testRunStats.totals.testCases.passed )
-            .writeAttribute( "failures", testRunStats.totals.testCases.failed )
-            .writeAttribute( "expectedFailures", testRunStats.totals.testCases.failedButOk );
+            .writeAttribute( "successes"_sr, testRunStats.totals.testCases.passed )
+            .writeAttribute( "failures"_sr, testRunStats.totals.testCases.failed )
+            .writeAttribute( "expectedFailures"_sr, testRunStats.totals.testCases.failedButOk );
         m_xml.endElement();
     }
 
-    void XmlReporter::benchmarkPreparing(std::string const& name) {
+    void XmlReporter::benchmarkPreparing( StringRef name ) {
         m_xml.startElement("BenchmarkResults")
-            .writeAttribute("name", name);
+             .writeAttribute("name"_sr, name);
     }
 
     void XmlReporter::benchmarkStarting(BenchmarkInfo const &info) {
-        m_xml.writeAttribute("samples", info.samples)
-            .writeAttribute("resamples", info.resamples)
-            .writeAttribute("iterations", info.iterations)
-            .writeAttribute("clockResolution", info.clockResolution)
-            .writeAttribute("estimatedDuration", info.estimatedDuration)
-            .writeComment("All values in nano seconds");
+        m_xml.writeAttribute("samples"_sr, info.samples)
+            .writeAttribute("resamples"_sr, info.resamples)
+            .writeAttribute("iterations"_sr, info.iterations)
+            .writeAttribute("clockResolution"_sr, info.clockResolution)
+            .writeAttribute("estimatedDuration"_sr, info.estimatedDuration)
+            .writeComment("All values in nano seconds"_sr);
     }
 
     void XmlReporter::benchmarkEnded(BenchmarkStats<> const& benchmarkStats) {
         m_xml.startElement("mean")
-            .writeAttribute("value", benchmarkStats.mean.point.count())
-            .writeAttribute("lowerBound", benchmarkStats.mean.lower_bound.count())
-            .writeAttribute("upperBound", benchmarkStats.mean.upper_bound.count())
-            .writeAttribute("ci", benchmarkStats.mean.confidence_interval);
+            .writeAttribute("value"_sr, benchmarkStats.mean.point.count())
+            .writeAttribute("lowerBound"_sr, benchmarkStats.mean.lower_bound.count())
+            .writeAttribute("upperBound"_sr, benchmarkStats.mean.upper_bound.count())
+            .writeAttribute("ci"_sr, benchmarkStats.mean.confidence_interval);
         m_xml.endElement();
         m_xml.startElement("standardDeviation")
-            .writeAttribute("value", benchmarkStats.standardDeviation.point.count())
-            .writeAttribute("lowerBound", benchmarkStats.standardDeviation.lower_bound.count())
-            .writeAttribute("upperBound", benchmarkStats.standardDeviation.upper_bound.count())
-            .writeAttribute("ci", benchmarkStats.standardDeviation.confidence_interval);
+            .writeAttribute("value"_sr, benchmarkStats.standardDeviation.point.count())
+            .writeAttribute("lowerBound"_sr, benchmarkStats.standardDeviation.lower_bound.count())
+            .writeAttribute("upperBound"_sr, benchmarkStats.standardDeviation.upper_bound.count())
+            .writeAttribute("ci"_sr, benchmarkStats.standardDeviation.confidence_interval);
         m_xml.endElement();
         m_xml.startElement("outliers")
-            .writeAttribute("variance", benchmarkStats.outlierVariance)
-            .writeAttribute("lowMild", benchmarkStats.outliers.low_mild)
-            .writeAttribute("lowSevere", benchmarkStats.outliers.low_severe)
-            .writeAttribute("highMild", benchmarkStats.outliers.high_mild)
-            .writeAttribute("highSevere", benchmarkStats.outliers.high_severe);
+            .writeAttribute("variance"_sr, benchmarkStats.outlierVariance)
+            .writeAttribute("lowMild"_sr, benchmarkStats.outliers.low_mild)
+            .writeAttribute("lowSevere"_sr, benchmarkStats.outliers.low_severe)
+            .writeAttribute("highMild"_sr, benchmarkStats.outliers.high_mild)
+            .writeAttribute("highSevere"_sr, benchmarkStats.outliers.high_severe);
         m_xml.endElement();
         m_xml.endElement();
     }
 
-    void XmlReporter::benchmarkFailed(std::string const &error) {
+    void XmlReporter::benchmarkFailed(StringRef error) {
         m_xml.scopedElement("failed").
-            writeAttribute("message", error);
+            writeAttribute("message"_sr, error);
         m_xml.endElement();
     }
 
@@ -320,7 +294,7 @@ namespace Catch {
             auto aliasTag = m_xml.scopedElement("Aliases");
             for (auto const& alias : tag.spellings) {
                 m_xml.startElement("Alias", XmlFormatting::Indent)
-                     .writeText(static_cast<std::string>(alias), XmlFormatting::None)
+                     .writeText(alias, XmlFormatting::None)
                      .endElement(XmlFormatting::Newline);
             }
         }
diff --git a/packages/Catch2/src/catch2/reporters/catch_reporter_xml.hpp b/packages/Catch2/src/catch2/reporters/catch_reporter_xml.hpp
index dbcaaa93221a8ae8c04b3e2d14d40ff4fa2380e3..46dd667a90d0d6e4ca300cc9470696fcf30bd245 100644
--- a/packages/Catch2/src/catch2/reporters/catch_reporter_xml.hpp
+++ b/packages/Catch2/src/catch2/reporters/catch_reporter_xml.hpp
@@ -29,32 +29,26 @@ namespace Catch {
 
     public: // StreamingReporterBase
 
-        void noMatchingTestCases(std::string const& s) override;
-
         void testRunStarting(TestRunInfo const& testInfo) override;
 
-        void testGroupStarting(GroupInfo const& groupInfo) override;
-
         void testCaseStarting(TestCaseInfo const& testInfo) override;
 
         void sectionStarting(SectionInfo const& sectionInfo) override;
 
         void assertionStarting(AssertionInfo const&) override;
 
-        bool assertionEnded(AssertionStats const& assertionStats) override;
+        void assertionEnded(AssertionStats const& assertionStats) override;
 
         void sectionEnded(SectionStats const& sectionStats) override;
 
         void testCaseEnded(TestCaseStats const& testCaseStats) override;
 
-        void testGroupEnded(TestGroupStats const& testGroupStats) override;
-
         void testRunEnded(TestRunStats const& testRunStats) override;
 
-        void benchmarkPreparing(std::string const& name) override;
+        void benchmarkPreparing( StringRef name ) override;
         void benchmarkStarting(BenchmarkInfo const&) override;
         void benchmarkEnded(BenchmarkStats<> const&) override;
-        void benchmarkFailed(std::string const&) override;
+        void benchmarkFailed( StringRef error ) override;
 
         void listReporters(std::vector<ReporterDescription> const& descriptions) override;
         void listTests(std::vector<TestCaseHandle> const& tests) override;
diff --git a/packages/Catch2/tests/CMakeLists.txt b/packages/Catch2/tests/CMakeLists.txt
index f8bdb3b4d05978beb03b853e2ab88ca4b766b1a8..7f7c32fe7e37d2aa477ee491ca414e2fc52f92ac 100644
--- a/packages/Catch2/tests/CMakeLists.txt
+++ b/packages/Catch2/tests/CMakeLists.txt
@@ -1,5 +1,66 @@
 include(MiscFunctions)
 
+if (CATCH_BUILD_SURROGATES)
+  message(STATUS "Configuring targets for surrogate TUs")
+
+  # If the folder does not exist before we ask for output redirect to
+  # a file, it won't work.
+  file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/surrogates)
+
+  # Creates target to generate the surrogate TU for provided header.
+  # Returns the path to the generated file.
+  function(createSurrogateFileTarget sourceHeader pathToFile)
+    set(pathPrefix ${PROJECT_SOURCE_DIR}/src)
+
+    file(RELATIVE_PATH includePath ${pathPrefix} ${sourceHeader})
+
+    get_filename_component(basicFileName "${sourceHeader}" NAME_WE)
+
+    set(surrogateFilePath ${CMAKE_CURRENT_BINARY_DIR}/surrogates/surrogate_${basicFileName}.cpp)
+
+    add_custom_command(
+      OUTPUT ${surrogateFilePath}
+      COMMAND cmake -E echo "\#include <${includePath}>" > "${surrogateFilePath}"
+      VERBATIM
+    )
+
+    set(${pathToFile} ${surrogateFilePath} PARENT_SCOPE)
+  endfunction()
+
+  # Extracts all non-helper (e.g. catch_all.hpp) headers from the
+  # Catch2 target, and returns them through the argument.
+  function(ExtractCatch2Headers OutArg)
+    get_target_property(targetSources Catch2 SOURCES)
+    foreach(Source ${targetSources})
+      string(REGEX MATCH "^.*\\.hpp$" isHeader ${Source})
+      string(REGEX MATCH "_all.hpp" isAllHeader ${Source})
+      if(isHeader AND NOT isAllHeader)
+        list(APPEND AllHeaders ${Source})
+      endif()
+    endforeach()
+    set(${OutArg} ${AllHeaders} PARENT_SCOPE)
+  endfunction()
+
+
+  ExtractCatch2Headers(mainHeaders)
+
+  if (NOT mainHeaders)
+    message(FATAL_ERROR "No headers in the main target were detected. Something is broken.")
+  endif()
+
+  foreach(header ${mainHeaders})
+    createSurrogateFileTarget(${header} pathToGeneratedFile)
+    list(APPEND surrogateFiles ${pathToGeneratedFile})
+  endforeach()
+
+
+  add_executable(Catch2SurrogateTarget
+    ${surrogateFiles}
+  )
+  target_link_libraries(Catch2SurrogateTarget PRIVATE Catch2WithMain)
+
+endif(CATCH_BUILD_SURROGATES)
+
 ####
 # Temporary workaround for VS toolset changes in 2017
 # We need to disable <UseFullPaths> property, but CMake doesn't support it
@@ -19,6 +80,7 @@ set(TEST_SOURCES
         ${SELF_TEST_DIR}/IntrospectiveTests/Clara.tests.cpp
         ${SELF_TEST_DIR}/IntrospectiveTests/CmdLine.tests.cpp
         ${SELF_TEST_DIR}/IntrospectiveTests/Details.tests.cpp
+        ${SELF_TEST_DIR}/IntrospectiveTests/FloatingPoint.tests.cpp
         ${SELF_TEST_DIR}/IntrospectiveTests/GeneratorsImpl.tests.cpp
         ${SELF_TEST_DIR}/IntrospectiveTests/InternalBenchmark.tests.cpp
         ${SELF_TEST_DIR}/IntrospectiveTests/PartTracker.tests.cpp
@@ -253,6 +315,60 @@ add_test(NAME CheckConvenienceHeaders
     ${PYTHON_EXECUTABLE} ${CATCH_DIR}/tools/scripts/checkConvenienceHeaders.py
 )
 
+
+add_test(NAME "Benchmarking::FailureReporting::OptimizedOut"
+  COMMAND
+    $<TARGET_FILE:SelfTest> "Failing benchmarks" -c "empty" -r xml
+  # This test only makes sense with the optimizer being enabled when
+  # the tests are being compiled.
+  CONFIGURATIONS Release
+)
+set_tests_properties("Benchmarking::FailureReporting::OptimizedOut"
+  PROPERTIES
+    PASS_REGULAR_EXPRESSION "could not measure benchmark\, maybe it was optimized away"
+    FAIL_REGULAR_EXPRESSION "successes=\"1\""
+)
+
+add_test(NAME "Benchmarking::FailureReporting::ThrowingBenchmark"
+  COMMAND
+    $<TARGET_FILE:SelfTest> "Failing benchmarks" -c "throw" -r xml
+)
+set_tests_properties("Benchmarking::FailureReporting::ThrowingBenchmark"
+  PROPERTIES
+    PASS_REGULAR_EXPRESSION "<failed message=\"just a plain literal"
+    FAIL_REGULAR_EXPRESSION "successes=\"1\""
+)
+
+add_test(NAME "Benchmarking::FailureReporting::FailedAssertion"
+  COMMAND
+    $<TARGET_FILE:SelfTest> "Failing benchmarks" -c "assert" -r xml
+)
+set_tests_properties("Benchmarking::FailureReporting::FailedAssertion"
+  PROPERTIES
+    PASS_REGULAR_EXPRESSION "<Expression success=\"false\""
+    FAIL_REGULAR_EXPRESSION "successes=\"1\""
+)
+
+add_test(NAME "Benchmarking::FailureReporting::FailMacro"
+  COMMAND
+    $<TARGET_FILE:SelfTest> "Failing benchmarks" -c "fail" -r xml
+)
+set_tests_properties("Benchmarking::FailureReporting::FailMacro"
+  PROPERTIES
+    PASS_REGULAR_EXPRESSION "This benchmark only fails\, nothing else"
+    FAIL_REGULAR_EXPRESSION "successes=\"1\""
+)
+
+add_test(NAME "Benchmarking::FailureReporting::ShouldFailIsRespected"
+  COMMAND
+    $<TARGET_FILE:SelfTest> "Failing benchmark respects should-fail"
+)
+set_tests_properties("Benchmarking::FailureReporting::ShouldFailIsRespected"
+  PROPERTIES
+    PASS_REGULAR_EXPRESSION "1 failed as expected"
+)
+
+
 if (CATCH_USE_VALGRIND)
     add_test(NAME ValgrindRunTests COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest>)
     add_test(NAME ValgrindListTests COMMAND valgrind --leak-check=full --error-exitcode=1 $<TARGET_FILE:SelfTest> --list-tests --verbosity high)
diff --git a/packages/Catch2/tests/ExtraTests/CMakeLists.txt b/packages/Catch2/tests/ExtraTests/CMakeLists.txt
index 62319b380b9ecb4a5b9b33c3e2fab3f38a79b44d..bce10df5b284b070a86270b324a25a44189b60b2 100644
--- a/packages/Catch2/tests/ExtraTests/CMakeLists.txt
+++ b/packages/Catch2/tests/ExtraTests/CMakeLists.txt
@@ -11,11 +11,11 @@ message( STATUS "Extra tests included" )
 # The MinDuration reporting tests do not need separate compilation, but
 # they have non-trivial execution time, so they are categorized as
 # extra tests, so that they are run less.
-add_test(NAME MinDuration::SimpleThreshold COMMAND $<TARGET_FILE:SelfTest> --min-duration 0.245 [min_duration_test])
+add_test(NAME MinDuration::SimpleThreshold COMMAND $<TARGET_FILE:SelfTest> --min-duration 0.950 [min_duration_test])
 set_tests_properties(
     MinDuration::SimpleThreshold
   PROPERTIES
-    PASS_REGULAR_EXPRESSION "s: sleep_for_250ms"
+    PASS_REGULAR_EXPRESSION "s: sleep_for_1000ms"
     FAIL_REGULAR_EXPRESSION "sleep_for_100ms"
     RUN_SERIAL ON  # The test is timing sensitive, so we want to run it
     # serially to avoid false positives on oversubscribed machines
@@ -172,6 +172,63 @@ if (MSVC)
     list(APPEND CATCH_WARNING_TARGETS ${EXTRA_TEST_BINARIES} WindowsHeader)
 endif()
 
+
+add_executable(PartialTestCaseEvents ${TESTS_DIR}/X21-PartialTestCaseEvents.cpp)
+target_link_libraries(PartialTestCaseEvents PRIVATE Catch2WithMain)
+add_test(
+  NAME PartialTestCaseEvents
+  COMMAND ${PYTHON_EXECUTABLE} ${CATCH_DIR}/tests/TestScripts/testPartialTestCaseEvent.py $<TARGET_FILE:PartialTestCaseEvents>
+)
+
+
+add_executable(DuplicatedTestCases-SameNameAndTags ${TESTS_DIR}/X31-DuplicatedTestCases.cpp)
+target_link_libraries(DuplicatedTestCases-SameNameAndTags PRIVATE Catch2::Catch2WithMain)
+add_test(
+  NAME DuplicatedTestCases::SameNameAndTags
+  COMMAND $<TARGET_FILE:DuplicatedTestCases-SameNameAndTags>
+)
+set_tests_properties(
+    DuplicatedTestCases::SameNameAndTags
+  PROPERTIES
+     PASS_REGULAR_EXPRESSION "error: .* already defined\\."
+)
+
+add_executable(DuplicatedTestCases-SameNameDifferentTags ${TESTS_DIR}/X32-DuplicatedTestCasesDifferentTags.cpp)
+target_link_libraries(DuplicatedTestCases-SameNameDifferentTags PRIVATE Catch2::Catch2WithMain)
+add_test(
+  NAME DuplicatedTestCases::SameNameDifferentTags
+  COMMAND $<TARGET_FILE:DuplicatedTestCases-SameNameDifferentTags>
+)
+set_tests_properties(
+    DuplicatedTestCases::SameNameDifferentTags
+  PROPERTIES
+     FAIL_REGULAR_EXPRESSION "error: .* already defined\\."
+)
+
+add_executable(DuplicatedTestCases-DuplicatedTestCaseMethods ${TESTS_DIR}/X33-DuplicatedTestCaseMethods.cpp)
+target_link_libraries(DuplicatedTestCases-DuplicatedTestCaseMethods PRIVATE Catch2::Catch2WithMain)
+add_test(
+  NAME DuplicatedTestCases::DuplicatedTestCaseMethods
+  COMMAND $<TARGET_FILE:DuplicatedTestCases-DuplicatedTestCaseMethods>
+)
+set_tests_properties(
+    DuplicatedTestCases::DuplicatedTestCaseMethods
+  PROPERTIES
+     PASS_REGULAR_EXPRESSION "error: .* already defined\\."
+)
+
+add_executable(DuplicatedTestCases-DifferentFixtures ${TESTS_DIR}/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp)
+target_link_libraries(DuplicatedTestCases-DifferentFixtures PRIVATE Catch2::Catch2WithMain)
+add_test(
+  NAME DuplicatedTestCases::DuplicatedTestCaseMethodsDifferentFixtures
+  COMMAND $<TARGET_FILE:DuplicatedTestCases-DifferentFixtures>
+)
+set_tests_properties(
+    DuplicatedTestCases::DuplicatedTestCaseMethodsDifferentFixtures
+  PROPERTIES
+    FAIL_REGULAR_EXPRESSION "error: .* already defined\\."
+)
+
 #add_executable(DebugBreakMacros ${TESTS_DIR}/X12-CustomDebugBreakMacro.cpp)
 #target_link_libraries(DebugBreakMacros Catch2)
 #add_test(NAME DebugBreakMacros COMMAND DebugBreakMacros --break)
@@ -188,6 +245,10 @@ set( EXTRA_TEST_BINARIES
     DisabledExceptions-CustomHandler
     FallbackStringifier
     DisableStringification
+    PartialTestCaseEvents
+    DuplicatedTestCases-SameNameAndTags
+    DuplicatedTestCases-SameNameDifferentTags
+    DuplicatedTestCases-DuplicatedTestCaseMethods
 #    DebugBreakMacros
 )
 
diff --git a/packages/Catch2/tests/ExtraTests/X21-PartialTestCaseEvents.cpp b/packages/Catch2/tests/ExtraTests/X21-PartialTestCaseEvents.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..997b9b9f3cf23739b08fc27be460fe0de9beefcf
--- /dev/null
+++ b/packages/Catch2/tests/ExtraTests/X21-PartialTestCaseEvents.cpp
@@ -0,0 +1,74 @@
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+/**\file
+ * Registers custom reporter that reports testCase* events
+ *
+ * The resulting executable can then be used by an external Python script
+ * to verify that testCase{Starting,Ended} and testCasePartial{Starting,Ended}
+ * events are properly nested.
+ */
+
+
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/reporters/catch_reporter_streaming_base.hpp>
+#include <catch2/catch_test_case_info.hpp>
+#include <catch2/catch_reporter_registrars.hpp>
+#include <catch2/generators/catch_generators.hpp>
+
+
+#include <iostream>
+
+using Catch::TestCaseInfo;
+using Catch::TestCaseStats;
+
+class PartialReporter : public Catch::StreamingReporterBase {
+public:
+    using StreamingReporterBase::StreamingReporterBase;
+
+    ~PartialReporter() override; // = default
+
+    static std::string getDescription() {
+        return "Special reporter for testing TestCasePartialStarting/Ended events";
+    }
+
+    //! Called _once_ for each TEST_CASE, no matter how many times it is entered
+    void testCaseStarting(TestCaseInfo const& testInfo) override {
+        std::cout << "TestCaseStarting: " << testInfo.name << '\n';
+    }
+    //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
+    void testCasePartialStarting(TestCaseInfo const& testInfo, uint64_t partNumber) override {
+        std::cout << "TestCaseStartingPartial: " << testInfo.name << '#' << partNumber << '\n';
+    }
+
+
+    //! Called _every time_ a TEST_CASE is entered, including repeats (due to sections)
+    void testCasePartialEnded(TestCaseStats const& testCaseStats, uint64_t partNumber) override {
+        std::cout << "TestCasePartialEnded: " << testCaseStats.testInfo->name << '#' << partNumber << '\n';
+    }
+    //! Called _once_ for each TEST_CASE, no matter how many times it is entered
+    void testCaseEnded(TestCaseStats const& testCaseStats) override {
+        std::cout << "TestCaseEnded: " << testCaseStats.testInfo->name << '\n';
+    }
+};
+PartialReporter::~PartialReporter() = default;
+
+
+CATCH_REGISTER_REPORTER("partial", PartialReporter)
+
+TEST_CASE("section") {
+    SECTION("A") {}
+    SECTION("B") {}
+    SECTION("C") {}
+    SECTION("D") {}
+}
+
+TEST_CASE("generator") {
+    auto _ = GENERATE(1, 2, 3, 4);
+    (void)_;
+}
diff --git a/packages/Catch2/tests/ExtraTests/X31-DuplicatedTestCases.cpp b/packages/Catch2/tests/ExtraTests/X31-DuplicatedTestCases.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2235ca8396d57363625ae5e831fc8bea50489139
--- /dev/null
+++ b/packages/Catch2/tests/ExtraTests/X31-DuplicatedTestCases.cpp
@@ -0,0 +1,16 @@
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+/**\file
+ * Checks that test cases with identical name and tags are reported as error
+ */
+
+#include <catch2/catch_test_macros.hpp>
+
+TEST_CASE("A test case with duplicated name and tags", "[tag1][tag2]") {}
+TEST_CASE("A test case with duplicated name and tags", "[tag1][tag2]") {}
diff --git a/packages/Catch2/tests/ExtraTests/X32-DuplicatedTestCasesDifferentTags.cpp b/packages/Catch2/tests/ExtraTests/X32-DuplicatedTestCasesDifferentTags.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6a4246be51be9bc2549c58f71c113693b8485cd4
--- /dev/null
+++ b/packages/Catch2/tests/ExtraTests/X32-DuplicatedTestCasesDifferentTags.cpp
@@ -0,0 +1,17 @@
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+/**\file
+ * Checks that test cases with identical name but different tags are
+ * not reported as an error.
+ */
+
+#include <catch2/catch_test_macros.hpp>
+
+TEST_CASE("A test case with duplicated name but different tags", "[tag1]") {}
+TEST_CASE("A test case with duplicated name but different tags", "[tag2]") {}
diff --git a/packages/Catch2/tests/ExtraTests/X33-DuplicatedTestCaseMethods.cpp b/packages/Catch2/tests/ExtraTests/X33-DuplicatedTestCaseMethods.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ff6660a8a1c478f279a6611d8ffec2b6a64cac10
--- /dev/null
+++ b/packages/Catch2/tests/ExtraTests/X33-DuplicatedTestCaseMethods.cpp
@@ -0,0 +1,22 @@
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+/**\file
+ * Checks that test case methods with identical class, name and tags are
+ * reported as error.
+ */
+
+#include <catch2/catch_test_macros.hpp>
+
+class TestCaseFixture {
+public:
+    int m_a;
+};
+
+TEST_CASE_METHOD(TestCaseFixture, "A test case with duplicated name and tags", "[tag1]") {}
+TEST_CASE_METHOD(TestCaseFixture, "A test case with duplicated name and tags", "[tag1]") {}
diff --git a/packages/Catch2/tests/ExtraTests/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp b/packages/Catch2/tests/ExtraTests/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b2021f5fbff85fa564d82d138c8bb298969f456c
--- /dev/null
+++ b/packages/Catch2/tests/ExtraTests/X34-DuplicatedTestCaseMethodsDifferentFixtures.cpp
@@ -0,0 +1,27 @@
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
+
+/**\file
+ * Checks that test case methods with different class, but same name and
+ * tags name and tags are not reported as error.
+ */
+
+#include <catch2/catch_test_macros.hpp>
+
+class TestCaseFixture1 {
+public:
+    int m_a;
+};
+
+class TestCaseFixture2 {
+public:
+    int m_a;
+};
+
+TEST_CASE_METHOD(TestCaseFixture1, "A test case with duplicated name and tags", "[tag1]") {}
+TEST_CASE_METHOD(TestCaseFixture2, "A test case with duplicated name and tags", "[tag1]") {}
diff --git a/packages/Catch2/tests/SelfTest/Baselines/automake.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/automake.sw.approved.txt
index 983d97648460c4d43cb32b32b0150f6cedf0ca06..5d599b6efac9828ca918af303ab3c4a2140c945f 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/automake.sw.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/automake.sw.approved.txt
@@ -24,6 +24,8 @@ Nor would this
 :test-result: PASS #1954 - 7 arg template test case sig compiles - 1, 1, 1, 1, 1, 0, 0
 :test-result: PASS #1954 - 7 arg template test case sig compiles - 5, 1, 1, 1, 1, 0, 0
 :test-result: PASS #1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0
+:test-result: PASS #2152 - ULP checks between differently signed values were wrong - double
+:test-result: PASS #2152 - ULP checks between differently signed values were wrong - float
 :test-result: XFAIL #748 - captures with unexpected exceptions
 :test-result: PASS #809
 :test-result: PASS #833
@@ -205,6 +207,9 @@ Message from section two
 :test-result: PASS String matchers
 :test-result: PASS StringRef
 :test-result: PASS StringRef at compilation time
+:test-result: PASS Stringifying char arrays with statically known sizes - char
+:test-result: PASS Stringifying char arrays with statically known sizes - signed char
+:test-result: PASS Stringifying char arrays with statically known sizes - unsigned char
 :test-result: PASS Stringifying std::chrono::duration helpers
 :test-result: PASS Stringifying std::chrono::duration with weird ratios
 :test-result: PASS Stringifying std::chrono::time_point<system_clock>
@@ -231,6 +236,9 @@ Message from section two
 :test-result: PASS Test case with one argument
 :test-result: PASS Test enum bit values
 :test-result: PASS Test with special, characters "in name
+:test-result: PASS Testing checked-if
+:test-result: XFAIL Testing checked-if 2
+:test-result: XFAIL Testing checked-if 3
 :test-result: FAIL The NO_FAIL macro reports a failure but does not fail the test
 :test-result: PASS The default listing implementation write to provided stream
 :test-result: FAIL This test 'should' fail but doesn't
@@ -263,6 +271,7 @@ Message from section two
 :test-result: PASS X/level/1/a
 :test-result: PASS X/level/1/b
 :test-result: PASS XmlEncode
+:test-result: PASS XmlWriter writes boolean attributes as true/false
 :test-result: PASS analyse no analysis
 :test-result: PASS array<int, N> -> toString
 :test-result: PASS atomic if
@@ -275,6 +284,8 @@ Message from section two
 :test-result: PASS classify_outliers
 :test-result: PASS comparisons between const int variables
 :test-result: PASS comparisons between int variables
+:test-result: PASS convertToBits
+:test-result: PASS empty tags are not allowed
 :test-result: PASS erfc_inv
 :test-result: PASS estimate_clock_resolution
 :test-result: PASS even more nested SECTION tests
@@ -336,6 +347,7 @@ loose text artifact
 :test-result: PASS stringify( vectors<has_operator> )
 :test-result: PASS strlen3
 :test-result: PASS tables
+:test-result: PASS tags with dots in later positions are not parsed as hidden
 :test-result: FAIL thrown std::strings are translated
 :test-result: PASS toString on const wchar_t const pointer returns the string contents
 :test-result: PASS toString on const wchar_t pointer returns the string contents
diff --git a/packages/Catch2/tests/SelfTest/Baselines/compact.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/compact.sw.approved.txt
index 3b19a962d734709564684e6491a3ce42bbad944a..556db26166f4eb203ca15903d7f08e132e6002f8 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/compact.sw.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/compact.sw.approved.txt
@@ -80,6 +80,10 @@ PartTracker.tests.cpp:<line number>: passed: n for: 3
 Misc.tests.cpp:<line number>: passed:
 Misc.tests.cpp:<line number>: passed:
 Misc.tests.cpp:<line number>: passed:
+Matchers.tests.cpp:<line number>: passed: smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323, 4.9406564584124654e-324])
+Matchers.tests.cpp:<line number>: passed: smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-324, -0.0000000000000000e+00])
+Matchers.tests.cpp:<line number>: passed: smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
+Matchers.tests.cpp:<line number>: passed: smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
 Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'answer := 42' with 1 message: 'expected exception'
 Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'answer := 42'; expression was: thisThrows() with 1 message: 'expected exception'
 Exception.tests.cpp:<line number>: passed: thisThrows() with 1 message: 'answer := 42'
@@ -290,10 +294,10 @@ Approx.tests.cpp:<line number>: passed: 0 == Approx( dZero) for: 0 == Approx( 0.
 Approx.tests.cpp:<line number>: passed: 0 == Approx( dSmall ).margin( 0.001 ) for: 0 == Approx( 0.00001 )
 Approx.tests.cpp:<line number>: passed: 1.234f == Approx( dMedium ) for: 1.234f == Approx( 1.234 )
 Approx.tests.cpp:<line number>: passed: dMedium == Approx( 1.234f ) for: 1.234 == Approx( 1.2339999676 )
-Matchers.tests.cpp:<line number>: passed: 1, Predicate<int>(alwaysTrue, "always true") for: 1 matches predicate: "always true"
-Matchers.tests.cpp:<line number>: passed: 1, !Predicate<int>(alwaysFalse, "always false") for: 1 not matches predicate: "always false"
-Matchers.tests.cpp:<line number>: passed: "Hello olleH", Predicate<std::string>( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") for: "Hello olleH" matches predicate: "First and last character should be equal"
-Matchers.tests.cpp:<line number>: passed: "This wouldn't pass", !Predicate<std::string>( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) for: "This wouldn't pass" not matches undescribed predicate
+Matchers.tests.cpp:<line number>: passed: 1, Predicate<int>( alwaysTrue, "always true" ) for: 1 matches predicate: "always true"
+Matchers.tests.cpp:<line number>: passed: 1, !Predicate<int>( alwaysFalse, "always false" ) for: 1 not matches predicate: "always false"
+Matchers.tests.cpp:<line number>: passed: "Hello olleH", Predicate<std::string>( []( std::string const& str ) -> bool { return str.front() == str.back(); }, "First and last character should be equal" ) for: "Hello olleH" matches predicate: "First and last character should be equal"
+Matchers.tests.cpp:<line number>: passed: "This wouldn't pass", !Predicate<std::string>( []( std::string const& str ) -> bool { return str.front() == str.back(); } ) for: "This wouldn't pass" not matches undescribed predicate
 Compilation.tests.cpp:<line number>: passed: lhs | rhs for: Val: 1 | Val: 2
 Compilation.tests.cpp:<line number>: passed: !(lhs & rhs) for: !(Val: 1 & Val: 2)
 Compilation.tests.cpp:<line number>: passed: HasBitOperators{ 1 } & HasBitOperators{ 1 } for: Val: 1 & Val: 1
@@ -346,41 +350,41 @@ ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 4 == 4
 ToStringGeneral.tests.cpp:<line number>: passed: c == i for: 5 == 5
 Clara.tests.cpp:<line number>: passed: name.empty() for: true
 Clara.tests.cpp:<line number>: passed: name == "foo" for: "foo" == "foo"
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype((MatcherA() && MatcherB()) && MatcherC()), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
-Matchers.tests.cpp:<line number>: passed: 1, (MatcherA() && MatcherB()) && MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() && (MatcherB() && MatcherC())), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
-Matchers.tests.cpp:<line number>: passed: 1, MatcherA() && (MatcherB() && MatcherC()) for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype((MatcherA() && MatcherB()) && (MatcherC() && MatcherD())), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
-Matchers.tests.cpp:<line number>: passed: 1, (MatcherA() && MatcherB()) && (MatcherC() && MatcherD()) for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true )
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype((MatcherA() || MatcherB()) || MatcherC()), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
-Matchers.tests.cpp:<line number>: passed: 1, (MatcherA() || MatcherB()) || MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || (MatcherB() || MatcherC())), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
-Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || (MatcherB() || MatcherC()) for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype((MatcherA() || MatcherB()) || (MatcherC() || MatcherD())), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
-Matchers.tests.cpp:<line number>: passed: 1, (MatcherA() || MatcherB()) || (MatcherC() || MatcherD()) for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true )
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!MatcherA()), Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA> >::value'
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype( ( MatcherA() && MatcherB() ) && MatcherC() ), Catch::Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>>::value'
+Matchers.tests.cpp:<line number>: passed: 1, ( MatcherA() && MatcherB() ) && MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype( MatcherA() && ( MatcherB() && MatcherC() ) ), Catch::Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>>::value'
+Matchers.tests.cpp:<line number>: passed: 1, MatcherA() && ( MatcherB() && MatcherC() ) for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype( ( MatcherA() && MatcherB() ) && ( MatcherC() && MatcherD() ) ), Catch::Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>>:: value'
+Matchers.tests.cpp:<line number>: passed: 1, ( MatcherA() && MatcherB() ) && ( MatcherC() && MatcherD() ) for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true )
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype( ( MatcherA() || MatcherB() ) || MatcherC() ), Catch::Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>>::value'
+Matchers.tests.cpp:<line number>: passed: 1, ( MatcherA() || MatcherB() ) || MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype( MatcherA() || ( MatcherB() || MatcherC() ) ), Catch::Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>>::value'
+Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || ( MatcherB() || MatcherC() ) for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype( ( MatcherA() || MatcherB() ) || ( MatcherC() || MatcherD() ) ), Catch::Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>>:: value'
+Matchers.tests.cpp:<line number>: passed: 1, ( MatcherA() || MatcherB() ) || ( MatcherC() || MatcherD() ) for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true )
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype( !MatcherA() ), Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA>>::value'
 Matchers.tests.cpp:<line number>: passed: 0, !MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!!MatcherA()), MatcherA const& >::value'
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same<decltype( !!MatcherA() ), MatcherA const&>::value'
 Matchers.tests.cpp:<line number>: passed: 1, !!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!!!MatcherA()), Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA> >::value'
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype( !!!MatcherA() ), Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA>>::value'
 Matchers.tests.cpp:<line number>: passed: 0, !!!MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(!!!!MatcherA()), MatcherA const & >::value'
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same<decltype( !!!!MatcherA() ), MatcherA const&>::value'
 Matchers.tests.cpp:<line number>: passed: 1, !!!!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith("bar") && !EndsWith("foo"))), Catch::Matchers::Detail::MatchAnyOf<std::string> >::value'
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB()), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB> >::value'
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same<decltype( StartsWith( "foo" ) || ( StartsWith( "bar" ) && EndsWith( "bar" ) && !EndsWith( "foo" ) ) ), Catch::Matchers::Detail::MatchAnyOf<std::string>>::value'
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same<decltype( MatcherA() || MatcherB() ), Catch::Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB>>::value'
 Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 )
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB()), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB> >::value'
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same<decltype( MatcherA() && MatcherB() ), Catch::Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB>>::value'
 Matchers.tests.cpp:<line number>: passed: 1, MatcherA() && MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 )
-Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype(MatcherA() || !MatcherB()), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, Catch::Matchers::Detail::MatchNotOfGeneric<MatcherB>> >::value'
+Matchers.tests.cpp:<line number>: passed: with 1 message: 'std::is_same< decltype( MatcherA() || !MatcherB() ), Catch::Matchers::Detail::MatchAnyOfGeneric< MatcherA, Catch::Matchers::Detail::MatchNotOfGeneric<MatcherB>>>::value'
 Matchers.tests.cpp:<line number>: passed: 1, MatcherA() || !MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 )
-Matchers.tests.cpp:<line number>: passed: vec, Predicate<std::vector<int>>([](auto const& v) { return std::all_of(v.begin(), v.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) for: { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } )
-Matchers.tests.cpp:<line number>: passed: str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" )
-Matchers.tests.cpp:<line number>: passed: str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" )
-Matchers.tests.cpp:<line number>: passed: str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
-Matchers.tests.cpp:<line number>: passed: str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
-Matchers.tests.cpp:<line number>: passed: str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) for: "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) )
-Matchers.tests.cpp:<line number>: passed: str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) for: "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } )
-Matchers.tests.cpp:<line number>: passed: container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) for: { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } )
+Matchers.tests.cpp:<line number>: passed: vec, Predicate<std::vector<int>>( []( auto const& v ) { return std::all_of( v.begin(), v.end(), []( int elem ) { return elem % 2 == 1; } ); }, "All elements are odd" ) && !EqualsRange( a ) for: { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } )
+Matchers.tests.cpp:<line number>: passed: str, StartsWith( "foo" ) && EqualsRange( arr ) && EndsWith( "bar" ) for: "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" )
+Matchers.tests.cpp:<line number>: passed: str, StartsWith( "foo" ) && !EqualsRange( bad_arr ) && EndsWith( "bar" ) for: "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" )
+Matchers.tests.cpp:<line number>: passed: str, EqualsRange( arr ) && StartsWith( "foo" ) && EndsWith( "bar" ) for: "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
+Matchers.tests.cpp:<line number>: passed: str, !EqualsRange( bad_arr ) && StartsWith( "foo" ) && EndsWith( "bar" ) for: "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
+Matchers.tests.cpp:<line number>: passed: str, EqualsRange( bad_arr ) || ( StartsWith( "foo" ) && EndsWith( "bar" ) ) for: "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) )
+Matchers.tests.cpp:<line number>: passed: str, ( StartsWith( "foo" ) && EndsWith( "bar" ) ) || EqualsRange( bad_arr ) for: "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } )
+Matchers.tests.cpp:<line number>: passed: container, EqualsRange( a ) || EqualsRange( b ) || EqualsRange( c ) for: { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } )
 Tricky.tests.cpp:<line number>: passed: std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}}
 Tricky.tests.cpp:<line number>: passed: std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}}
 Tricky.tests.cpp:<line number>: passed: std::vector<int>{1, 2, 3} == std::vector<int>{1, 2, 3}
@@ -434,7 +438,7 @@ Condition.tests.cpp:<line number>: passed: (std::numeric_limits<uint32_t>::max)(
 Matchers.tests.cpp:<line number>: passed: !(matcher.match( 1 )) for: !false
 Matchers.tests.cpp:<line number>: passed: first.matchCalled for: true
 Matchers.tests.cpp:<line number>: passed: !second.matchCalled for: true
-Matchers.tests.cpp:<line number>: passed: matcher.match(1) for: true
+Matchers.tests.cpp:<line number>: passed: matcher.match( 1 ) for: true
 Matchers.tests.cpp:<line number>: passed: first.matchCalled for: true
 Matchers.tests.cpp:<line number>: passed: !second.matchCalled for: true
 Matchers.tests.cpp:<line number>: passed: !(matcher.match( 1 )) for: !false
@@ -443,8 +447,8 @@ Matchers.tests.cpp:<line number>: passed: !second.matchCalled for: true
 Matchers.tests.cpp:<line number>: passed: matcher.match( 1 ) for: true
 Matchers.tests.cpp:<line number>: passed: first.matchCalled for: true
 Matchers.tests.cpp:<line number>: passed: !second.matchCalled for: true
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Contains("not there", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" contains: "not there" (case insensitive)
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Contains("STRING") for: "this string contains 'abc' as a substring" contains: "STRING"
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Contains( "not there", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "not there" (case insensitive)
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Contains( "STRING" ) for: "this string contains 'abc' as a substring" contains: "STRING"
 Generators.tests.cpp:<line number>: passed: elem % 2 == 1 for: 1 == 1
 Generators.tests.cpp:<line number>: passed: elem % 2 == 1 for: 1 == 1
 Generators.tests.cpp:<line number>: passed: elem % 2 == 1 for: 1 == 1
@@ -469,8 +473,8 @@ ToString.tests.cpp:<line number>: passed: enumInfo->lookup(1) == "Value2" for: V
 ToString.tests.cpp:<line number>: passed: enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **}
 ==
 "{** unexpected enum value **}"
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), EndsWith("Substring") for: "this string contains 'abc' as a substring" ends with: "Substring"
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive)
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), EndsWith( "Substring" ) for: "this string contains 'abc' as a substring" ends with: "Substring"
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive)
 EnumToString.tests.cpp:<line number>: passed: stringify( EnumClass3::Value1 ) == "Value1" for: "Value1" == "Value1"
 EnumToString.tests.cpp:<line number>: passed: stringify( EnumClass3::Value2 ) == "Value2" for: "Value2" == "Value2"
 EnumToString.tests.cpp:<line number>: passed: stringify( EnumClass3::Value3 ) == "Value3" for: "Value3" == "Value3"
@@ -501,10 +505,10 @@ Condition.tests.cpp:<line number>: passed: data.str_hello == "hello" for: "hello
 Condition.tests.cpp:<line number>: passed: "hello" == data.str_hello for: "hello" == "hello"
 Condition.tests.cpp:<line number>: passed: data.str_hello.size() == 5 for: 5 == 5
 Condition.tests.cpp:<line number>: passed: x == Approx( 1.3 ) for: 1.3 == Approx( 1.3 )
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals("this string contains 'abc' as a substring") for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals("this string contains 'ABC' as a substring", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals("this string contains 'ABC' as a substring") for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals("something else", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" equals: "something else" (case insensitive)
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "something else" (case insensitive)
 ToStringGeneral.tests.cpp:<line number>: passed: ::Catch::Detail::stringify(WhatException{}) == "This exception has overridden what() method" for: "This exception has overridden what() method"
 ==
 "This exception has overridden what() method"
@@ -512,24 +516,24 @@ ToStringGeneral.tests.cpp:<line number>: passed: ::Catch::Detail::stringify(Oper
 ToStringGeneral.tests.cpp:<line number>: passed: ::Catch::Detail::stringify(StringMakerException{}) == "StringMakerException" for: "StringMakerException"
 ==
 "StringMakerException"
-Matchers.tests.cpp:<line number>: failed: expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{1}
-Matchers.tests.cpp:<line number>: failed: expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{1}
-Matchers.tests.cpp:<line number>: failed: unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt(1), SpecialException, ExceptionMatcher{1}
-Matchers.tests.cpp:<line number>: failed: unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt(1), SpecialException, ExceptionMatcher{1}
-Matchers.tests.cpp:<line number>: failed: throwsSpecialException(3), SpecialException, ExceptionMatcher{1} for: SpecialException::what special exception has value of 1
-Matchers.tests.cpp:<line number>: failed: throwsSpecialException(4), SpecialException, ExceptionMatcher{1} for: SpecialException::what special exception has value of 1
-Matchers.tests.cpp:<line number>: passed: throwsSpecialException(1), SpecialException, ExceptionMatcher{1} for: SpecialException::what special exception has value of 1
-Matchers.tests.cpp:<line number>: passed: throwsSpecialException(2), SpecialException, ExceptionMatcher{2} for: SpecialException::what special exception has value of 2
+Matchers.tests.cpp:<line number>: failed: expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }
+Matchers.tests.cpp:<line number>: failed: expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }
+Matchers.tests.cpp:<line number>: failed: unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }
+Matchers.tests.cpp:<line number>: failed: unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }
+Matchers.tests.cpp:<line number>: failed: throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 } for: SpecialException::what special exception has value of 1
+Matchers.tests.cpp:<line number>: failed: throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 } for: SpecialException::what special exception has value of 1
+Matchers.tests.cpp:<line number>: passed: throwsSpecialException( 1 ), SpecialException, ExceptionMatcher{ 1 } for: SpecialException::what special exception has value of 1
+Matchers.tests.cpp:<line number>: passed: throwsSpecialException( 2 ), SpecialException, ExceptionMatcher{ 2 } for: SpecialException::what special exception has value of 2
 Exception.tests.cpp:<line number>: passed: thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
 Exception.tests.cpp:<line number>: passed: thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No ) for: "expected exception" equals: "expected exception" (case insensitive)
 Exception.tests.cpp:<line number>: passed: thisThrows(), StartsWith( "expected" ) for: "expected exception" starts with: "expected"
 Exception.tests.cpp:<line number>: passed: thisThrows(), EndsWith( "exception" ) for: "expected exception" ends with: "exception"
 Exception.tests.cpp:<line number>: passed: thisThrows(), Contains( "except" ) for: "expected exception" contains: "except"
 Exception.tests.cpp:<line number>: passed: thisThrows(), Contains( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
-Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, Message("DerivedException::what") for: DerivedException::what exception message matches "DerivedException::what"
-Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, !Message("derivedexception::what") for: DerivedException::what not exception message matches "derivedexception::what"
-Matchers.tests.cpp:<line number>: passed: throwsSpecialException(2), SpecialException, !Message("DerivedException::what") for: SpecialException::what not exception message matches "DerivedException::what"
-Matchers.tests.cpp:<line number>: passed: throwsSpecialException(2), SpecialException, Message("SpecialException::what") for: SpecialException::what exception message matches "SpecialException::what"
+Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, Message( "DerivedException::what" ) for: DerivedException::what exception message matches "DerivedException::what"
+Matchers.tests.cpp:<line number>: passed: throwsDerivedException(), DerivedException, !Message( "derivedexception::what" ) for: DerivedException::what not exception message matches "derivedexception::what"
+Matchers.tests.cpp:<line number>: passed: throwsSpecialException( 2 ), SpecialException, !Message( "DerivedException::what" ) for: SpecialException::what not exception message matches "DerivedException::what"
+Matchers.tests.cpp:<line number>: passed: throwsSpecialException( 2 ), SpecialException, Message( "SpecialException::what" ) for: SpecialException::what exception message matches "SpecialException::what"
 Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'expected exception'; expression was: thisThrows(), std::string
 Exception.tests.cpp:<line number>: failed: expected exception, got none; expression was: thisDoesntThrow(), std::domain_error
 Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'expected exception'; expression was: thisThrows()
@@ -542,66 +546,67 @@ Misc.tests.cpp:<line number>: passed: Factorial(1) == 1 for: 1 == 1
 Misc.tests.cpp:<line number>: passed: Factorial(2) == 2 for: 2 == 2
 Misc.tests.cpp:<line number>: passed: Factorial(3) == 6 for: 6 == 6
 Misc.tests.cpp:<line number>: passed: Factorial(10) == 3628800 for: 3628800 (0x<hex digits>) == 3628800 (0x<hex digits>)
-Matchers.tests.cpp:<line number>: passed: 10., WithinRel(11.1, 0.1) for: 10.0 and 11.1 are within 10% of each other
-Matchers.tests.cpp:<line number>: passed: 10., !WithinRel(11.2, 0.1) for: 10.0 not and 11.2 are within 10% of each other
-Matchers.tests.cpp:<line number>: passed: 1., !WithinRel(0., 0.99) for: 1.0 not and 0 are within 99% of each other
-Matchers.tests.cpp:<line number>: passed: -0., WithinRel(0.) for: -0.0 and 0 are within 2.22045e-12% of each other
-Matchers.tests.cpp:<line number>: passed: v1, WithinRel(v2) for: 0.0 and 2.22507e-308 are within 2.22045e-12% of each other
-Matchers.tests.cpp:<line number>: passed: 1., WithinAbs(1., 0) for: 1.0 is within 0.0 of 1.0
-Matchers.tests.cpp:<line number>: passed: 0., WithinAbs(1., 1) for: 0.0 is within 1.0 of 1.0
-Matchers.tests.cpp:<line number>: passed: 0., !WithinAbs(1., 0.99) for: 0.0 not is within 0.99 of 1.0
-Matchers.tests.cpp:<line number>: passed: 0., !WithinAbs(1., 0.99) for: 0.0 not is within 0.99 of 1.0
-Matchers.tests.cpp:<line number>: passed: 11., !WithinAbs(10., 0.5) for: 11.0 not is within 0.5 of 10.0
-Matchers.tests.cpp:<line number>: passed: 10., !WithinAbs(11., 0.5) for: 10.0 not is within 0.5 of 11.0
-Matchers.tests.cpp:<line number>: passed: -10., WithinAbs(-10., 0.5) for: -10.0 is within 0.5 of -10.0
-Matchers.tests.cpp:<line number>: passed: -10., WithinAbs(-9.6, 0.5) for: -10.0 is within 0.5 of -9.6
-Matchers.tests.cpp:<line number>: passed: 1., WithinULP(1., 0) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
-Matchers.tests.cpp:<line number>: passed: nextafter(1., 2.), WithinULP(1., 1) for: 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00])
-Matchers.tests.cpp:<line number>: passed: 0., WithinULP(nextafter(0., 1.), 1) for: 0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324])
-Matchers.tests.cpp:<line number>: passed: 1., WithinULP(nextafter(1., 0.), 1) for: 1.0 is within 1 ULPs of 9.9999999999999989e-01 ([9.9999999999999978e-01, 1.0000000000000000e+00])
-Matchers.tests.cpp:<line number>: passed: 1., !WithinULP(nextafter(1., 2.), 0) for: 1.0 not is within 0 ULPs of 1.0000000000000002e+00 ([1.0000000000000002e+00, 1.0000000000000002e+00])
-Matchers.tests.cpp:<line number>: passed: 1., WithinULP(1., 0) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
-Matchers.tests.cpp:<line number>: passed: -0., WithinULP(0., 0) for: -0.0 is within 0 ULPs of 0.0000000000000000e+00 ([0.0000000000000000e+00, 0.0000000000000000e+00])
-Matchers.tests.cpp:<line number>: passed: 1., WithinAbs(1., 0.5) || WithinULP(2., 1) for: 1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0000000000000000e+00 ([1.9999999999999998e+00, 2.0000000000000004e+00]) )
-Matchers.tests.cpp:<line number>: passed: 1., WithinAbs(2., 0.5) || WithinULP(1., 0) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) )
-Matchers.tests.cpp:<line number>: passed: 0.0001, WithinAbs(0., 0.001) || WithinRel(0., 0.1) for: 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
-Matchers.tests.cpp:<line number>: passed: WithinAbs(1., 0.)
-Matchers.tests.cpp:<line number>: passed: WithinAbs(1., -1.), std::domain_error
-Matchers.tests.cpp:<line number>: passed: WithinULP(1., 0)
-Matchers.tests.cpp:<line number>: passed: WithinRel(1., 0.)
-Matchers.tests.cpp:<line number>: passed: WithinRel(1., -0.2), std::domain_error
-Matchers.tests.cpp:<line number>: passed: WithinRel(1., 1.), std::domain_error
-Matchers.tests.cpp:<line number>: passed: 10.f, WithinRel(11.1f, 0.1f) for: 10.0f and 11.1 are within 10% of each other
-Matchers.tests.cpp:<line number>: passed: 10.f, !WithinRel(11.2f, 0.1f) for: 10.0f not and 11.2 are within 10% of each other
-Matchers.tests.cpp:<line number>: passed: 1.f, !WithinRel(0.f, 0.99f) for: 1.0f not and 0 are within 99% of each other
-Matchers.tests.cpp:<line number>: passed: -0.f, WithinRel(0.f) for: -0.0f and 0 are within 0.00119209% of each other
-Matchers.tests.cpp:<line number>: passed: v1, WithinRel(v2) for: 0.0f and 1.17549e-38 are within 0.00119209% of each other
-Matchers.tests.cpp:<line number>: passed: 1.f, WithinAbs(1.f, 0) for: 1.0f is within 0.0 of 1.0
-Matchers.tests.cpp:<line number>: passed: 0.f, WithinAbs(1.f, 1) for: 0.0f is within 1.0 of 1.0
-Matchers.tests.cpp:<line number>: passed: 0.f, !WithinAbs(1.f, 0.99f) for: 0.0f not is within 0.9900000095 of 1.0
-Matchers.tests.cpp:<line number>: passed: 0.f, !WithinAbs(1.f, 0.99f) for: 0.0f not is within 0.9900000095 of 1.0
-Matchers.tests.cpp:<line number>: passed: 0.f, WithinAbs(-0.f, 0) for: 0.0f is within 0.0 of -0.0
-Matchers.tests.cpp:<line number>: passed: 11.f, !WithinAbs(10.f, 0.5f) for: 11.0f not is within 0.5 of 10.0
-Matchers.tests.cpp:<line number>: passed: 10.f, !WithinAbs(11.f, 0.5f) for: 10.0f not is within 0.5 of 11.0
-Matchers.tests.cpp:<line number>: passed: -10.f, WithinAbs(-10.f, 0.5f) for: -10.0f is within 0.5 of -10.0
-Matchers.tests.cpp:<line number>: passed: -10.f, WithinAbs(-9.6f, 0.5f) for: -10.0f is within 0.5 of -9.6000003815
-Matchers.tests.cpp:<line number>: passed: 1.f, WithinULP(1.f, 0) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
-Matchers.tests.cpp:<line number>: passed: nextafter(1.f, 2.f), WithinULP(1.f, 1) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00])
-Matchers.tests.cpp:<line number>: passed: 0.f, WithinULP(nextafter(0.f, 1.f), 1) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45])
-Matchers.tests.cpp:<line number>: passed: 1.f, WithinULP(nextafter(1.f, 0.f), 1) for: 1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00])
-Matchers.tests.cpp:<line number>: passed: 1.f, !WithinULP(nextafter(1.f, 2.f), 0) for: 1.0f not is within 0 ULPs of 1.00000012e+00f ([1.00000012e+00, 1.00000012e+00])
-Matchers.tests.cpp:<line number>: passed: 1.f, WithinULP(1.f, 0) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
-Matchers.tests.cpp:<line number>: passed: -0.f, WithinULP(0.f, 0) for: -0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00])
-Matchers.tests.cpp:<line number>: passed: 1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1) for: 1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) )
-Matchers.tests.cpp:<line number>: passed: 1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) )
-Matchers.tests.cpp:<line number>: passed: 0.0001f, WithinAbs(0.f, 0.001f) || WithinRel(0.f, 0.1f) for: 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
-Matchers.tests.cpp:<line number>: passed: WithinAbs(1.f, 0.f)
-Matchers.tests.cpp:<line number>: passed: WithinAbs(1.f, -1.f), std::domain_error
-Matchers.tests.cpp:<line number>: passed: WithinULP(1.f, 0)
-Matchers.tests.cpp:<line number>: passed: WithinULP(1.f, static_cast<uint64_t>(-1)), std::domain_error
-Matchers.tests.cpp:<line number>: passed: WithinRel(1.f, 0.f)
-Matchers.tests.cpp:<line number>: passed: WithinRel(1.f, -0.2f), std::domain_error
-Matchers.tests.cpp:<line number>: passed: WithinRel(1.f, 1.f), std::domain_error
+Matchers.tests.cpp:<line number>: passed: 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.1 are within 10% of each other
+Matchers.tests.cpp:<line number>: passed: 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.2 are within 10% of each other
+Matchers.tests.cpp:<line number>: passed: 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0 are within 99% of each other
+Matchers.tests.cpp:<line number>: passed: -0., WithinRel( 0. ) for: -0.0 and 0 are within 2.22045e-12% of each other
+Matchers.tests.cpp:<line number>: passed: v1, WithinRel( v2 ) for: 0.0 and 2.22507e-308 are within 2.22045e-12% of each other
+Matchers.tests.cpp:<line number>: passed: 1., WithinAbs( 1., 0 ) for: 1.0 is within 0.0 of 1.0
+Matchers.tests.cpp:<line number>: passed: 0., WithinAbs( 1., 1 ) for: 0.0 is within 1.0 of 1.0
+Matchers.tests.cpp:<line number>: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0
+Matchers.tests.cpp:<line number>: passed: 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0
+Matchers.tests.cpp:<line number>: passed: 11., !WithinAbs( 10., 0.5 ) for: 11.0 not is within 0.5 of 10.0
+Matchers.tests.cpp:<line number>: passed: 10., !WithinAbs( 11., 0.5 ) for: 10.0 not is within 0.5 of 11.0
+Matchers.tests.cpp:<line number>: passed: -10., WithinAbs( -10., 0.5 ) for: -10.0 is within 0.5 of -10.0
+Matchers.tests.cpp:<line number>: passed: -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.6
+Matchers.tests.cpp:<line number>: passed: 1., WithinULP( 1., 0 ) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
+Matchers.tests.cpp:<line number>: passed: nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00])
+Matchers.tests.cpp:<line number>: passed: 0., WithinULP( nextafter( 0., 1. ), 1 ) for: 0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324])
+Matchers.tests.cpp:<line number>: passed: 1., WithinULP( nextafter( 1., 0. ), 1 ) for: 1.0 is within 1 ULPs of 9.9999999999999989e-01 ([9.9999999999999978e-01, 1.0000000000000000e+00])
+Matchers.tests.cpp:<line number>: passed: 1., !WithinULP( nextafter( 1., 2. ), 0 ) for: 1.0 not is within 0 ULPs of 1.0000000000000002e+00 ([1.0000000000000002e+00, 1.0000000000000002e+00])
+Matchers.tests.cpp:<line number>: passed: 1., WithinULP( 1., 0 ) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
+Matchers.tests.cpp:<line number>: passed: -0., WithinULP( 0., 0 ) for: -0.0 is within 0 ULPs of 0.0000000000000000e+00 ([0.0000000000000000e+00, 0.0000000000000000e+00])
+Matchers.tests.cpp:<line number>: passed: 1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 ) for: 1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0000000000000000e+00 ([1.9999999999999998e+00, 2.0000000000000004e+00]) )
+Matchers.tests.cpp:<line number>: passed: 1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 ) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) )
+Matchers.tests.cpp:<line number>: passed: 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
+Matchers.tests.cpp:<line number>: passed: WithinAbs( 1., 0. )
+Matchers.tests.cpp:<line number>: passed: WithinAbs( 1., -1. ), std::domain_error
+Matchers.tests.cpp:<line number>: passed: WithinULP( 1., 0 )
+Matchers.tests.cpp:<line number>: passed: WithinRel( 1., 0. )
+Matchers.tests.cpp:<line number>: passed: WithinRel( 1., -0.2 ), std::domain_error
+Matchers.tests.cpp:<line number>: passed: WithinRel( 1., 1. ), std::domain_error
+Matchers.tests.cpp:<line number>: passed: 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.1 are within 10% of each other
+Matchers.tests.cpp:<line number>: passed: 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.2 are within 10% of each other
+Matchers.tests.cpp:<line number>: passed: 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0 are within 99% of each other
+Matchers.tests.cpp:<line number>: passed: -0.f, WithinRel( 0.f ) for: -0.0f and 0 are within 0.00119209% of each other
+Matchers.tests.cpp:<line number>: passed: v1, WithinRel( v2 ) for: 0.0f and 1.17549e-38 are within 0.00119209% of each other
+Matchers.tests.cpp:<line number>: passed: 1.f, WithinAbs( 1.f, 0 ) for: 1.0f is within 0.0 of 1.0
+Matchers.tests.cpp:<line number>: passed: 0.f, WithinAbs( 1.f, 1 ) for: 0.0f is within 1.0 of 1.0
+Matchers.tests.cpp:<line number>: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0
+Matchers.tests.cpp:<line number>: passed: 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0
+Matchers.tests.cpp:<line number>: passed: 0.f, WithinAbs( -0.f, 0 ) for: 0.0f is within 0.0 of -0.0
+Matchers.tests.cpp:<line number>: passed: 11.f, !WithinAbs( 10.f, 0.5f ) for: 11.0f not is within 0.5 of 10.0
+Matchers.tests.cpp:<line number>: passed: 10.f, !WithinAbs( 11.f, 0.5f ) for: 10.0f not is within 0.5 of 11.0
+Matchers.tests.cpp:<line number>: passed: -10.f, WithinAbs( -10.f, 0.5f ) for: -10.0f is within 0.5 of -10.0
+Matchers.tests.cpp:<line number>: passed: -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.6000003815
+Matchers.tests.cpp:<line number>: passed: 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
+Matchers.tests.cpp:<line number>: passed: -1.f, WithinULP( -1.f, 0 ) for: -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00])
+Matchers.tests.cpp:<line number>: passed: nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00])
+Matchers.tests.cpp:<line number>: passed: 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45])
+Matchers.tests.cpp:<line number>: passed: 1.f, WithinULP( nextafter( 1.f, 0.f ), 1 ) for: 1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00])
+Matchers.tests.cpp:<line number>: passed: 1.f, !WithinULP( nextafter( 1.f, 2.f ), 0 ) for: 1.0f not is within 0 ULPs of 1.00000012e+00f ([1.00000012e+00, 1.00000012e+00])
+Matchers.tests.cpp:<line number>: passed: 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
+Matchers.tests.cpp:<line number>: passed: -0.f, WithinULP( 0.f, 0 ) for: -0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00])
+Matchers.tests.cpp:<line number>: passed: 1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 ) for: 1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) )
+Matchers.tests.cpp:<line number>: passed: 1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 ) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) )
+Matchers.tests.cpp:<line number>: passed: 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
+Matchers.tests.cpp:<line number>: passed: WithinAbs( 1.f, 0.f )
+Matchers.tests.cpp:<line number>: passed: WithinAbs( 1.f, -1.f ), std::domain_error
+Matchers.tests.cpp:<line number>: passed: WithinULP( 1.f, 0 )
+Matchers.tests.cpp:<line number>: passed: WithinULP( 1.f, static_cast<uint64_t>( -1 ) ), std::domain_error
+Matchers.tests.cpp:<line number>: passed: WithinRel( 1.f, 0.f )
+Matchers.tests.cpp:<line number>: passed: WithinRel( 1.f, -0.2f ), std::domain_error
+Matchers.tests.cpp:<line number>: passed: WithinRel( 1.f, 1.f ), std::domain_error
 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
 Generators.tests.cpp:<line number>: passed: i % 2 == 0 for: 0 == 0
@@ -899,13 +904,13 @@ Approx.tests.cpp:<line number>: passed: d <= Approx( 1.23 ) for: 1.23 <= Approx(
 Approx.tests.cpp:<line number>: passed: !(d <= Approx( 1.22 )) for: !(1.23 <= Approx( 1.22 ))
 Approx.tests.cpp:<line number>: passed: d <= Approx( 1.22 ).epsilon(0.1) for: 1.23 <= Approx( 1.22 )
 Misc.tests.cpp:<line number>: passed: with 1 message: 'was called'
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains("string") && Contains("abc") && Contains("substring") && Contains("contains") for: "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" )
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains("string") || Contains("different") || Contains("random") for: "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" )
-Matchers.tests.cpp:<line number>: passed: testStringForMatching2(), Contains("string") || Contains("different") || Contains("random") for: "some completely different text that contains one common word" ( contains: "string" or contains: "different" or contains: "random" )
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), (Contains("string") || Contains("different")) && Contains("substring") for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "substring" )
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), (Contains("string") || Contains("different")) && Contains("random") for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), !Contains("different") for: "this string contains 'abc' as a substring" not contains: "different"
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), !Contains("substring") for: "this string contains 'abc' as a substring" not contains: "substring"
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains( "string" ) && Contains( "abc" ) && Contains( "substring" ) && Contains( "contains" ) for: "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" )
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains( "string" ) || Contains( "different" ) || Contains( "random" ) for: "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" )
+Matchers.tests.cpp:<line number>: passed: testStringForMatching2(), Contains( "string" ) || Contains( "different" ) || Contains( "random" ) for: "some completely different text that contains one common word" ( contains: "string" or contains: "different" or contains: "random" )
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) && Contains( "substring" ) for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "substring" )
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) && Contains( "random" ) for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), !Contains( "different" ) for: "this string contains 'abc' as a substring" not contains: "different"
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), !Contains( "substring" ) for: "this string contains 'abc' as a substring" not contains: "substring"
 Exception.tests.cpp:<line number>: passed: thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
 Exception.tests.cpp:<line number>: failed: thisThrows(), "should fail" for: "expected exception" equals: "should fail"
 Generators.tests.cpp:<line number>: passed: values > -6 for: 3 > -6
@@ -1014,10 +1019,10 @@ RandomNumberGeneration.tests.cpp:<line number>: passed: rng() == 0x<hex digits>
 4261393167 (0x<hex digits>)
 Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'Message from section one'
 Message.tests.cpp:<line number>: failed: explicitly with 1 message: 'Message from section two'
-Matchers.tests.cpp:<line number>: passed: (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed
+Matchers.tests.cpp:<line number>: passed: ( EvilMatcher(), EvilMatcher() ), EvilCommaOperatorUsed
 Matchers.tests.cpp:<line number>: passed: &EvilMatcher(), EvilAddressOfOperatorUsed
-Matchers.tests.cpp:<line number>: passed: EvilMatcher() || (EvilMatcher() && !EvilMatcher())
-Matchers.tests.cpp:<line number>: passed: (EvilMatcher() && EvilMatcher()) || !EvilMatcher()
+Matchers.tests.cpp:<line number>: passed: EvilMatcher() || ( EvilMatcher() && !EvilMatcher() )
+Matchers.tests.cpp:<line number>: passed: ( EvilMatcher() && EvilMatcher() ) || !EvilMatcher()
 CmdLine.tests.cpp:<line number>: passed: spec.hasFilters() == false for: false == false
 CmdLine.tests.cpp:<line number>: passed: spec.matches( *tcA ) == false for: false == false
 CmdLine.tests.cpp:<line number>: passed: spec.matches( *tcB ) == false for: false == false
@@ -1181,7 +1186,7 @@ ToStringGeneral.tests.cpp:<line number>: passed: str1.size() == 3 + 5 for: 8 ==
 ToStringGeneral.tests.cpp:<line number>: passed: str2.size() == 3 + 10 for: 13 == 13
 ToStringGeneral.tests.cpp:<line number>: passed: str1.size() == 2 + 5 for: 7 == 7
 ToStringGeneral.tests.cpp:<line number>: passed: str2.size() == 2 + 15 for: 17 == 17
-Matchers.tests.cpp:<line number>: passed: "foo", Predicate<const char*>([] (const char* const&) { return true; }) for: "foo" matches undescribed predicate
+Matchers.tests.cpp:<line number>: passed: "foo", Predicate<const char*>( []( const char* const& ) { return true; } ) for: "foo" matches undescribed predicate
 CmdLine.tests.cpp:<line number>: passed: result for: {?}
 CmdLine.tests.cpp:<line number>: passed: config.processName == "" for: "" == ""
 CmdLine.tests.cpp:<line number>: passed: result for: {?}
@@ -1269,10 +1274,10 @@ Misc.tests.cpp:<line number>: passed: std::tuple_size<TestType>::value >= 1 for:
 Misc.tests.cpp:<line number>: passed: std::tuple_size<TestType>::value >= 1 for: 1 >= 1
 ToString.tests.cpp:<line number>: passed: Catch::Detail::stringify(UsesSentinel{}) == "{  }" for: "{  }" == "{  }"
 Decomposition.tests.cpp:<line number>: failed: truthy(false) for: Hey, its truthy!
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Matches("this STRING contains 'abc' as a substring") for: "this string contains 'abc' as a substring" matches "this STRING contains 'abc' as a substring" case sensitively
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Matches("contains 'abc' as a substring") for: "this string contains 'abc' as a substring" matches "contains 'abc' as a substring" case sensitively
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Matches("this string contains 'abc' as a") for: "this string contains 'abc' as a substring" matches "this string contains 'abc' as a" case sensitively
-Matchers.tests.cpp:<line number>: passed: actual, !UnorderedEquals(expected) for: { 'a', 'b' } not UnorderedEquals: { 'c', 'b' }
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Matches( "this STRING contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" matches "this STRING contains 'abc' as a substring" case sensitively
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Matches( "contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" matches "contains 'abc' as a substring" case sensitively
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), Matches( "this string contains 'abc' as a" ) for: "this string contains 'abc' as a substring" matches "this string contains 'abc' as a" case sensitively
+Matchers.tests.cpp:<line number>: passed: actual, !UnorderedEquals( expected ) for: { 'a', 'b' } not UnorderedEquals: { 'c', 'b' }
 Reporters.tests.cpp:<line number>: passed: !(factories.empty()) for: !false
 Reporters.tests.cpp:<line number>: passed: listingString, Contains("fakeTag"s) for: "All available tags:
    1  [fakeTag]
@@ -1464,46 +1469,40 @@ Approx.tests.cpp:<line number>: passed: Approx( d ) != 1.22 for: Approx( 1.23 )
 Approx.tests.cpp:<line number>: passed: Approx( d ) != 1.24 for: Approx( 1.23 ) != 1.24
 Message from section one
 Message from section two
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), StartsWith("This String") for: "this string contains 'abc' as a substring" starts with: "This String"
-Matchers.tests.cpp:<line number>: failed: testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" starts with: "string" (case insensitive)
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), StartsWith( "This String" ) for: "this string contains 'abc' as a substring" starts with: "This String"
+Matchers.tests.cpp:<line number>: failed: testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "string" (case insensitive)
 ToStringGeneral.tests.cpp:<line number>: passed: Catch::Detail::stringify(singular) == "{ 1 }" for: "{ 1 }" == "{ 1 }"
 ToStringGeneral.tests.cpp:<line number>: passed: Catch::Detail::stringify(arr) == "{ 3, 2, 1 }" for: "{ 3, 2, 1 }" == "{ 3, 2, 1 }"
 ToStringGeneral.tests.cpp:<line number>: passed: Catch::Detail::stringify(arr) == R"({ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } })" for: "{ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } }"
 ==
 "{ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } }"
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains("string") for: "this string contains 'abc' as a substring" contains: "string"
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains("string", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" contains: "string" (case insensitive)
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains("abc") for: "this string contains 'abc' as a substring" contains: "abc"
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains("aBC", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith("this") for: "this string contains 'abc' as a substring" starts with: "this"
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith("THIS", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith("substring") for: "this string contains 'abc' as a substring" ends with: "substring"
-Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith(" SuBsTrInG", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains( "string" ) for: "this string contains 'abc' as a substring" contains: "string"
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains( "string", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "string" (case insensitive)
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains( "abc" ) for: "this string contains 'abc' as a substring" contains: "abc"
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), Contains( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith( "this" ) for: "this string contains 'abc' as a substring" starts with: "this"
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith( "substring" ) for: "this string contains 'abc' as a substring" ends with: "substring"
+Matchers.tests.cpp:<line number>: passed: testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
 String.tests.cpp:<line number>: passed: empty.empty() for: true
 String.tests.cpp:<line number>: passed: empty.size() == 0 for: 0 == 0
-String.tests.cpp:<line number>: passed: empty.isNullTerminated() for: true
-String.tests.cpp:<line number>: passed: std::strcmp( empty.c_str(), "" ) == 0 for: 0 == 0
+String.tests.cpp:<line number>: passed: std::strcmp( empty.data(), "" ) == 0 for: 0 == 0
 String.tests.cpp:<line number>: passed: s.empty() == false for: false == false
 String.tests.cpp:<line number>: passed: s.size() == 5 for: 5 == 5
-String.tests.cpp:<line number>: passed: s.isNullTerminated() for: true
 String.tests.cpp:<line number>: passed: std::strcmp( rawChars, "hello" ) == 0 for: 0 == 0
-String.tests.cpp:<line number>: passed: s.c_str()
-String.tests.cpp:<line number>: passed: s.c_str() == rawChars for: "hello" == "hello"
 String.tests.cpp:<line number>: passed: s.data() == rawChars for: "hello" == "hello"
 String.tests.cpp:<line number>: passed: original == "original"
-String.tests.cpp:<line number>: passed: !(original.isNullTerminated()) for: !false
-String.tests.cpp:<line number>: passed: original.c_str()
 String.tests.cpp:<line number>: passed: original.data()
 String.tests.cpp:<line number>: passed: ss.empty() == false for: false == false
 String.tests.cpp:<line number>: passed: ss.size() == 5 for: 5 == 5
 String.tests.cpp:<line number>: passed: std::strncmp( ss.data(), "hello", 5 ) == 0 for: 0 == 0
 String.tests.cpp:<line number>: passed: ss == "hello" for: hello == "hello"
 String.tests.cpp:<line number>: passed: ss.size() == 6 for: 6 == 6
-String.tests.cpp:<line number>: passed: std::strcmp( ss.c_str(), "world!" ) == 0 for: 0 == 0
+String.tests.cpp:<line number>: passed: std::strcmp( ss.data(), "world!" ) == 0 for: 0 == 0
 String.tests.cpp:<line number>: passed: s.data() == s2.data() for: "hello world!" == "hello world!"
 String.tests.cpp:<line number>: passed: s.data() == ss.data() for: "hello world!" == "hello world!"
 String.tests.cpp:<line number>: passed: s.substr(s.size() + 1, 123).empty() for: true
-String.tests.cpp:<line number>: passed: std::strcmp(ss.c_str(), "world!") == 0 for: 0 == 0
+String.tests.cpp:<line number>: passed: std::strcmp(ss.data(), "world!") == 0 for: 0 == 0
 String.tests.cpp:<line number>: passed: s.substr(1'000'000, 1).empty() for: true
 String.tests.cpp:<line number>: passed: reinterpret_cast<char*>(buffer1) != reinterpret_cast<char*>(buffer2) for: "Hello" != "Hello"
 String.tests.cpp:<line number>: passed: left == right for: Hello == Hello
@@ -1525,7 +1524,6 @@ String.tests.cpp:<line number>: passed: together == "abrakadabra" for: "abrakada
 String.tests.cpp:<line number>: passed: with 1 message: 'empty.size() == 0'
 String.tests.cpp:<line number>: passed: with 1 message: 'empty.begin() == empty.end()'
 String.tests.cpp:<line number>: passed: with 1 message: 'stringref.size() == 3'
-String.tests.cpp:<line number>: passed: with 1 message: 'stringref.isNullTerminated()'
 String.tests.cpp:<line number>: passed: with 1 message: 'stringref.data() == abc'
 String.tests.cpp:<line number>: passed: with 1 message: 'stringref.begin() == abc'
 String.tests.cpp:<line number>: passed: with 1 message: 'stringref.begin() != stringref.end()'
@@ -1535,14 +1533,16 @@ String.tests.cpp:<line number>: passed: with 1 message: 'stringref[1] == 'b''
 String.tests.cpp:<line number>: passed: with 1 message: 'shortened.size() == 2'
 String.tests.cpp:<line number>: passed: with 1 message: 'shortened.data() == abc'
 String.tests.cpp:<line number>: passed: with 1 message: 'shortened.begin() != shortened.end()'
-String.tests.cpp:<line number>: passed: with 1 message: '!(shortened.isNullTerminated())'
-String.tests.cpp:<line number>: passed: with 1 message: '!(shortened.substr(1, 3).isNullTerminated())'
 String.tests.cpp:<line number>: passed: with 1 message: '!(sr1.empty())'
 String.tests.cpp:<line number>: passed: with 1 message: 'sr1.size() == 3'
-String.tests.cpp:<line number>: passed: with 1 message: 'sr1.isNullTerminated()'
 String.tests.cpp:<line number>: passed: with 1 message: 'sr2.empty()'
 String.tests.cpp:<line number>: passed: with 1 message: 'sr2.size() == 0'
-String.tests.cpp:<line number>: passed: with 1 message: 'sr2.isNullTerminated()'
+ToString.tests.cpp:<line number>: passed: ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+ToString.tests.cpp:<line number>: passed: ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+ToString.tests.cpp:<line number>: passed: ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+ToString.tests.cpp:<line number>: passed: ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+ToString.tests.cpp:<line number>: passed: ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+ToString.tests.cpp:<line number>: passed: ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
 ToStringChrono.tests.cpp:<line number>: passed: minute == seconds for: 1 m == 60 s
 ToStringChrono.tests.cpp:<line number>: passed: hour != seconds for: 1 h != 60 s
 ToStringChrono.tests.cpp:<line number>: passed: micro != milli for: 1 us != 1 ms
@@ -1725,6 +1725,16 @@ Misc.tests.cpp:<line number>: passed: v.capacity() >= V for: 15 >= 15
 VariadicMacros.tests.cpp:<line number>: passed: with 1 message: 'no assertions'
 Tricky.tests.cpp:<line number>: passed: 0x<hex digits> == bit30and31 for: 3221225472 (0x<hex digits>) == 3221225472
 CmdLine.tests.cpp:<line number>: passed:
+Misc.tests.cpp:<line number>: passed: true
+Misc.tests.cpp:<line number>: passed:
+Misc.tests.cpp:<line number>: failed - but was ok: false
+Misc.tests.cpp:<line number>: passed: true
+Misc.tests.cpp:<line number>: failed - but was ok: false
+Misc.tests.cpp:<line number>: passed:
+Misc.tests.cpp:<line number>: passed: true
+Misc.tests.cpp:<line number>: failed: explicitly
+Misc.tests.cpp:<line number>: failed - but was ok: false
+Misc.tests.cpp:<line number>: failed: explicitly
 Message.tests.cpp:<line number>: failed - but was ok: 1 == 2
 Reporters.tests.cpp:<line number>: passed: listingString, Contains("[fakeTag]"s) for: "All available tags:
    1  [fakeTag]
@@ -1900,53 +1910,53 @@ Approx.tests.cpp:<line number>: passed: approx( d ) == 1.22 for: Approx( 1.23 )
 Approx.tests.cpp:<line number>: passed: approx( d ) == 1.24 for: Approx( 1.23 ) == 1.24
 Approx.tests.cpp:<line number>: passed: approx( d ) != 1.25 for: Approx( 1.23 ) != 1.25
 VariadicMacros.tests.cpp:<line number>: passed: with 1 message: 'no assertions'
-Matchers.tests.cpp:<line number>: passed: empty, Approx(empty) for: {  } is approx: {  }
-Matchers.tests.cpp:<line number>: passed: v1, Approx(v1) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
-Matchers.tests.cpp:<line number>: passed: v1, Approx<double>({ 1., 2., 3. }) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
-Matchers.tests.cpp:<line number>: passed: v1, !Approx(temp) for: { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 }
-Matchers.tests.cpp:<line number>: passed: v1, !Approx(v2) for: { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 }
-Matchers.tests.cpp:<line number>: passed: v1, Approx(v2).margin(0.5) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
-Matchers.tests.cpp:<line number>: passed: v1, Approx(v2).epsilon(0.5) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
-Matchers.tests.cpp:<line number>: passed: v1, Approx(v2).epsilon(0.1).scale(500) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
-Matchers.tests.cpp:<line number>: failed: empty, Approx(t1) for: {  } is approx: { 1.0, 2.0 }
-Matchers.tests.cpp:<line number>: failed: v1, Approx(v2) for: { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
-Matchers.tests.cpp:<line number>: passed: v, VectorContains(1) for: { 1, 2, 3 } Contains: 1
-Matchers.tests.cpp:<line number>: passed: v, VectorContains(2) for: { 1, 2, 3 } Contains: 2
-Matchers.tests.cpp:<line number>: passed: v5, (VectorContains<int, CustomAllocator<int>>(2)) for: { 1, 2, 3 } Contains: 2
-Matchers.tests.cpp:<line number>: passed: v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2 }
-Matchers.tests.cpp:<line number>: passed: v, Contains<int>({ 1, 2 }) for: { 1, 2, 3 } Contains: { 1, 2 }
-Matchers.tests.cpp:<line number>: passed: v5, (Contains<int, std::allocator<int>, CustomAllocator<int>>(v2)) for: { 1, 2, 3 } Contains: { 1, 2 }
-Matchers.tests.cpp:<line number>: passed: v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: v, Contains(empty) for: { 1, 2, 3 } Contains: {  }
-Matchers.tests.cpp:<line number>: passed: empty, Contains(empty) for: {  } Contains: {  }
-Matchers.tests.cpp:<line number>: passed: v5, (Contains<int, std::allocator<int>, CustomAllocator<int>>(v2)) for: { 1, 2, 3 } Contains: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: v5, Contains(v6) for: { 1, 2, 3 } Contains: { 1, 2 }
-Matchers.tests.cpp:<line number>: passed: v, VectorContains(1) && VectorContains(2) for: { 1, 2, 3 } ( Contains: 1 and Contains: 2 )
-Matchers.tests.cpp:<line number>: passed: v, Equals(v) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: empty, Equals(empty) for: {  } Equals: {  }
-Matchers.tests.cpp:<line number>: passed: v, Equals<int>({ 1, 2, 3 }) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: v, Equals(v2) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: v5, (Equals<int, std::allocator<int>, CustomAllocator<int>>(v2)) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: v5, Equals(v6) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: v, UnorderedEquals(v) for: { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: v, UnorderedEquals<int>({ 3, 2, 1 }) for: { 1, 2, 3 } UnorderedEquals: { 3, 2, 1 }
-Matchers.tests.cpp:<line number>: passed: empty, UnorderedEquals(empty) for: {  } UnorderedEquals: {  }
-Matchers.tests.cpp:<line number>: passed: permuted, UnorderedEquals(v) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: permuted, UnorderedEquals(v) for: { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: passed: v5, (UnorderedEquals<int, std::allocator<int>, CustomAllocator<int>>(permuted)) for: { 1, 2, 3 } UnorderedEquals: { 2, 3, 1 }
-Matchers.tests.cpp:<line number>: passed: v5_permuted, UnorderedEquals(v5) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: failed: v, VectorContains(-1) for: { 1, 2, 3 } Contains: -1
-Matchers.tests.cpp:<line number>: failed: empty, VectorContains(1) for: {  } Contains: 1
-Matchers.tests.cpp:<line number>: failed: empty, Contains(v) for: {  } Contains: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: failed: v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2, 4 }
-Matchers.tests.cpp:<line number>: failed: v, Equals(v2) for: { 1, 2, 3 } Equals: { 1, 2 }
-Matchers.tests.cpp:<line number>: failed: v2, Equals(v) for: { 1, 2 } Equals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: failed: empty, Equals(v) for: {  } Equals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: failed: v, Equals(empty) for: { 1, 2, 3 } Equals: {  }
-Matchers.tests.cpp:<line number>: failed: v, UnorderedEquals(empty) for: { 1, 2, 3 } UnorderedEquals: {  }
-Matchers.tests.cpp:<line number>: failed: empty, UnorderedEquals(v) for: {  } UnorderedEquals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: failed: permuted, UnorderedEquals(v) for: { 1, 3 } UnorderedEquals: { 1, 2, 3 }
-Matchers.tests.cpp:<line number>: failed: permuted, UnorderedEquals(v) for: { 3, 1 } UnorderedEquals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: empty, Approx( empty ) for: {  } is approx: {  }
+Matchers.tests.cpp:<line number>: passed: v1, Approx( v1 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
+Matchers.tests.cpp:<line number>: passed: v1, Approx<double>( { 1., 2., 3. } ) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
+Matchers.tests.cpp:<line number>: passed: v1, !Approx( temp ) for: { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 }
+Matchers.tests.cpp:<line number>: passed: v1, !Approx( v2 ) for: { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 }
+Matchers.tests.cpp:<line number>: passed: v1, Approx( v2 ).margin( 0.5 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
+Matchers.tests.cpp:<line number>: passed: v1, Approx( v2 ).epsilon( 0.5 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
+Matchers.tests.cpp:<line number>: passed: v1, Approx( v2 ).epsilon( 0.1 ).scale( 500 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
+Matchers.tests.cpp:<line number>: failed: empty, Approx( t1 ) for: {  } is approx: { 1.0, 2.0 }
+Matchers.tests.cpp:<line number>: failed: v1, Approx( v2 ) for: { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
+Matchers.tests.cpp:<line number>: passed: v, VectorContains( 1 ) for: { 1, 2, 3 } Contains: 1
+Matchers.tests.cpp:<line number>: passed: v, VectorContains( 2 ) for: { 1, 2, 3 } Contains: 2
+Matchers.tests.cpp:<line number>: passed: v5, ( VectorContains<int, CustomAllocator<int>>( 2 ) ) for: { 1, 2, 3 } Contains: 2
+Matchers.tests.cpp:<line number>: passed: v, Contains( v2 ) for: { 1, 2, 3 } Contains: { 1, 2 }
+Matchers.tests.cpp:<line number>: passed: v, Contains<int>( { 1, 2 } ) for: { 1, 2, 3 } Contains: { 1, 2 }
+Matchers.tests.cpp:<line number>: passed: v5, ( Contains<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) for: { 1, 2, 3 } Contains: { 1, 2 }
+Matchers.tests.cpp:<line number>: passed: v, Contains( v2 ) for: { 1, 2, 3 } Contains: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: v, Contains( empty ) for: { 1, 2, 3 } Contains: {  }
+Matchers.tests.cpp:<line number>: passed: empty, Contains( empty ) for: {  } Contains: {  }
+Matchers.tests.cpp:<line number>: passed: v5, ( Contains<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) for: { 1, 2, 3 } Contains: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: v5, Contains( v6 ) for: { 1, 2, 3 } Contains: { 1, 2 }
+Matchers.tests.cpp:<line number>: passed: v, VectorContains( 1 ) && VectorContains( 2 ) for: { 1, 2, 3 } ( Contains: 1 and Contains: 2 )
+Matchers.tests.cpp:<line number>: passed: v, Equals( v ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: empty, Equals( empty ) for: {  } Equals: {  }
+Matchers.tests.cpp:<line number>: passed: v, Equals<int>( { 1, 2, 3 } ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: v, Equals( v2 ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: v5, ( Equals<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: v5, Equals( v6 ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: v, UnorderedEquals( v ) for: { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: v, UnorderedEquals<int>( { 3, 2, 1 } ) for: { 1, 2, 3 } UnorderedEquals: { 3, 2, 1 }
+Matchers.tests.cpp:<line number>: passed: empty, UnorderedEquals( empty ) for: {  } UnorderedEquals: {  }
+Matchers.tests.cpp:<line number>: passed: permuted, UnorderedEquals( v ) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: permuted, UnorderedEquals( v ) for: { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: passed: v5, ( UnorderedEquals<int, std::allocator<int>, CustomAllocator<int>>( permuted ) ) for: { 1, 2, 3 } UnorderedEquals: { 2, 3, 1 }
+Matchers.tests.cpp:<line number>: passed: v5_permuted, UnorderedEquals( v5 ) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: failed: v, VectorContains( -1 ) for: { 1, 2, 3 } Contains: -1
+Matchers.tests.cpp:<line number>: failed: empty, VectorContains( 1 ) for: {  } Contains: 1
+Matchers.tests.cpp:<line number>: failed: empty, Contains( v ) for: {  } Contains: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: failed: v, Contains( v2 ) for: { 1, 2, 3 } Contains: { 1, 2, 4 }
+Matchers.tests.cpp:<line number>: failed: v, Equals( v2 ) for: { 1, 2, 3 } Equals: { 1, 2 }
+Matchers.tests.cpp:<line number>: failed: v2, Equals( v ) for: { 1, 2 } Equals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: failed: empty, Equals( v ) for: {  } Equals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: failed: v, Equals( empty ) for: { 1, 2, 3 } Equals: {  }
+Matchers.tests.cpp:<line number>: failed: v, UnorderedEquals( empty ) for: { 1, 2, 3 } UnorderedEquals: {  }
+Matchers.tests.cpp:<line number>: failed: empty, UnorderedEquals( v ) for: {  } UnorderedEquals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: failed: permuted, UnorderedEquals( v ) for: { 1, 3 } UnorderedEquals: { 1, 2, 3 }
+Matchers.tests.cpp:<line number>: failed: permuted, UnorderedEquals( v ) for: { 3, 1 } UnorderedEquals: { 1, 2, 3 }
 Exception.tests.cpp:<line number>: passed: thisThrows(), std::domain_error
 Exception.tests.cpp:<line number>: passed: thisDoesntThrow()
 Exception.tests.cpp:<line number>: passed: thisThrows()
@@ -1977,6 +1987,9 @@ Xml.tests.cpp:<line number>: passed: encode( stringWithQuotes, Catch::XmlEncode:
 "don't &quot;quote&quot; me on that"
 Xml.tests.cpp:<line number>: passed: encode( "[\x01]" ) == "[\\x01]" for: "[\x01]" == "[\x01]"
 Xml.tests.cpp:<line number>: passed: encode( "[\x7F]" ) == "[\\x7F]" for: "[\x7F]" == "[\x7F]"
+Xml.tests.cpp:<line number>: passed: stream.str(), Contains(R"(attr1="true")") && Contains(R"(attr2="false")") for: "<?xml version="1.0" encoding="UTF-8"?>
+<Element1 attr1="true" attr2="false"/>
+" ( contains: "attr1="true"" and contains: "attr2="false"" )
 InternalBenchmark.tests.cpp:<line number>: passed: analysis.mean.point.count() == 23 for: 23.0 == 23
 InternalBenchmark.tests.cpp:<line number>: passed: analysis.mean.lower_bound.count() == 23 for: 23.0 == 23
 InternalBenchmark.tests.cpp:<line number>: passed: analysis.mean.upper_bound.count() == 23 for: 23.0 == 23
@@ -2007,11 +2020,11 @@ InternalBenchmark.tests.cpp:<line number>: passed: called == 1 for: 1 == 1
 Tricky.tests.cpp:<line number>: passed: obj.prop != 0 for: 0x<hex digits> != 0
 Misc.tests.cpp:<line number>: passed: flag for: true
 Misc.tests.cpp:<line number>: passed: testCheckedElse( true ) for: true
-Misc.tests.cpp:<line number>: failed: flag for: false
+Misc.tests.cpp:<line number>: failed - but was ok: flag for: false
 Misc.tests.cpp:<line number>: failed: testCheckedElse( false ) for: false
 Misc.tests.cpp:<line number>: passed: flag for: true
 Misc.tests.cpp:<line number>: passed: testCheckedIf( true ) for: true
-Misc.tests.cpp:<line number>: failed: flag for: false
+Misc.tests.cpp:<line number>: failed - but was ok: flag for: false
 Misc.tests.cpp:<line number>: failed: testCheckedIf( false ) for: false
 InternalBenchmark.tests.cpp:<line number>: passed: o.samples_seen == static_cast<int>(x.size()) for: 6 == 6
 InternalBenchmark.tests.cpp:<line number>: passed: o.low_severe == los for: 0 == 0
@@ -2057,6 +2070,17 @@ Condition.tests.cpp:<line number>: passed: long_var == unsigned_char_var for: 1
 Condition.tests.cpp:<line number>: passed: long_var == unsigned_short_var for: 1 == 1
 Condition.tests.cpp:<line number>: passed: long_var == unsigned_int_var for: 1 == 1
 Condition.tests.cpp:<line number>: passed: long_var == unsigned_long_var for: 1 == 1
+FloatingPoint.tests.cpp:<line number>: passed: convertToBits( 0.f ) == 0 for: 0 == 0
+FloatingPoint.tests.cpp:<line number>: passed: convertToBits( -0.f ) == ( 1ULL << 31 ) for: 2147483648 (0x<hex digits>)
+==
+2147483648 (0x<hex digits>)
+FloatingPoint.tests.cpp:<line number>: passed: convertToBits( 0. ) == 0 for: 0 == 0
+FloatingPoint.tests.cpp:<line number>: passed: convertToBits( -0. ) == ( 1ULL << 63 ) for: 9223372036854775808 (0x<hex digits>)
+==
+9223372036854775808 (0x<hex digits>)
+FloatingPoint.tests.cpp:<line number>: passed: convertToBits( std::numeric_limits<float>::denorm_min() ) == 1 for: 1 == 1
+FloatingPoint.tests.cpp:<line number>: passed: convertToBits( std::numeric_limits<double>::denorm_min() ) == 1 for: 1 == 1
+Tag.tests.cpp:<line number>: passed: Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo)
 InternalBenchmark.tests.cpp:<line number>: passed: erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.0920368762 == Approx( -0.0920368762 )
 InternalBenchmark.tests.cpp:<line number>: passed: erfc_inv(1.067400) == Approx(-0.05980291115763361) for: -0.0598029112 == Approx( -0.0598029112 )
 InternalBenchmark.tests.cpp:<line number>: passed: erfc_inv(0.050000) == Approx(1.38590382434967796) for: 1.3859038243 == Approx( 1.3859038243 )
@@ -2254,6 +2278,8 @@ Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_
 Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 6 == 6
 Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
 Generators.tests.cpp:<line number>: passed: strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 6 == 6
+Tag.tests.cpp:<line number>: passed: testcase.tags.size() == 1 for: 1 == 1
+Tag.tests.cpp:<line number>: passed: testcase.tags[0].original == "magic.tag"_catch_sr for: magic.tag == magic.tag
 Exception.tests.cpp:<line number>: failed: unexpected exception with message: 'Why would you throw a std::string?'
 Misc.tests.cpp:<line number>: passed: result == "\"wide load\"" for: ""wide load"" == ""wide load""
 Misc.tests.cpp:<line number>: passed: result == "\"wide load\"" for: ""wide load"" == ""wide load""
@@ -2353,5 +2379,5 @@ InternalBenchmark.tests.cpp:<line number>: passed: med == 18. for: 18.0 == 18.0
 InternalBenchmark.tests.cpp:<line number>: passed: q3 == 23. for: 23.0 == 23.0
 Misc.tests.cpp:<line number>: passed:
 Misc.tests.cpp:<line number>: passed:
-Failed 86 test cases, failed 148 assertions.
+Failed 86 test cases, failed 146 assertions.
 
diff --git a/packages/Catch2/tests/SelfTest/Baselines/console.std.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/console.std.approved.txt
index d781a60e316cc38de07ed386f0f6acb40c282ba7..8704f8b373385c0d6b778d9b731a3bc19844e0aa 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/console.std.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/console.std.approved.txt
@@ -332,13 +332,13 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Contains("not there", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), Contains( "not there", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "not there" (case
   insensitive)
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Contains("STRING") )
+  CHECK_THAT( testStringForMatching(), Contains( "STRING" ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "STRING"
 
@@ -381,12 +381,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), EndsWith("Substring") )
+  CHECK_THAT( testStringForMatching(), EndsWith( "Substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" ends with: "Substring"
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" ends with: "this" (case
   insensitive)
@@ -469,13 +469,13 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Equals("this string contains 'ABC' as a substring") )
+  CHECK_THAT( testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" equals: "this string contains
   'ABC' as a substring"
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Equals("something else", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" equals: "something else" (case
   insensitive)
@@ -488,11 +488,11 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )
 because no exception was thrown where one was expected:
 
 Matchers.tests.cpp:<line number>: FAILED:
-  REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )
+  REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )
 because no exception was thrown where one was expected:
 
 -------------------------------------------------------------------------------
@@ -503,12 +503,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )
 due to unexpected exception with message:
   Unknown exception
 
 Matchers.tests.cpp:<line number>: FAILED:
-  REQUIRE_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )
+  REQUIRE_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )
 due to unexpected exception with message:
   Unknown exception
 
@@ -520,12 +520,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THROWS_MATCHES( throwsSpecialException(3), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 } )
 with expansion:
   SpecialException::what special exception has value of 1
 
 Matchers.tests.cpp:<line number>: FAILED:
-  REQUIRE_THROWS_MATCHES( throwsSpecialException(4), SpecialException, ExceptionMatcher{1} )
+  REQUIRE_THROWS_MATCHES( throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 } )
 with expansion:
   SpecialException::what special exception has value of 1
 
@@ -678,7 +678,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), (Contains("string") || Contains("different")) && Contains("random") )
+  CHECK_THAT( testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) && Contains( "random" ) )
 with expansion:
   "this string contains 'abc' as a substring" ( ( contains: "string" or
   contains: "different" ) and contains: "random" )
@@ -690,7 +690,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), !Contains("substring") )
+  CHECK_THAT( testStringForMatching(), !Contains( "substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" not contains: "substring"
 
@@ -865,19 +865,19 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Matches("this STRING contains 'abc' as a substring") )
+  CHECK_THAT( testStringForMatching(), Matches( "this STRING contains 'abc' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" matches "this STRING contains
   'abc' as a substring" case sensitively
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Matches("contains 'abc' as a substring") )
+  CHECK_THAT( testStringForMatching(), Matches( "contains 'abc' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" matches "contains 'abc' as a
   substring" case sensitively
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Matches("this string contains 'abc' as a") )
+  CHECK_THAT( testStringForMatching(), Matches( "this string contains 'abc' as a" ) )
 with expansion:
   "this string contains 'abc' as a substring" matches "this string contains
   'abc' as a" case sensitively
@@ -894,12 +894,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), StartsWith("This String") )
+  CHECK_THAT( testStringForMatching(), StartsWith( "This String" ) )
 with expansion:
   "this string contains 'abc' as a substring" starts with: "This String"
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" starts with: "string" (case
   insensitive)
@@ -922,6 +922,22 @@ with expansion:
   }
   "
 
+-------------------------------------------------------------------------------
+Testing checked-if 2
+-------------------------------------------------------------------------------
+Misc.tests.cpp:<line number>
+...............................................................................
+
+Misc.tests.cpp:<line number>: FAILED:
+
+-------------------------------------------------------------------------------
+Testing checked-if 3
+-------------------------------------------------------------------------------
+Misc.tests.cpp:<line number>
+...............................................................................
+
+Misc.tests.cpp:<line number>: FAILED:
+
 -------------------------------------------------------------------------------
 Thrown string literals are translated
 -------------------------------------------------------------------------------
@@ -950,7 +966,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, Approx(t1) )
+  CHECK_THAT( empty, Approx( t1 ) )
 with expansion:
   {  } is approx: { 1.0, 2.0 }
 
@@ -962,7 +978,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v1, Approx(v2) )
+  CHECK_THAT( v1, Approx( v2 ) )
 with expansion:
   { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
 
@@ -974,12 +990,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, VectorContains(-1) )
+  CHECK_THAT( v, VectorContains( -1 ) )
 with expansion:
   { 1, 2, 3 } Contains: -1
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, VectorContains(1) )
+  CHECK_THAT( empty, VectorContains( 1 ) )
 with expansion:
   {  } Contains: 1
 
@@ -991,12 +1007,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, Contains(v) )
+  CHECK_THAT( empty, Contains( v ) )
 with expansion:
   {  } Contains: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, Contains(v2) )
+  CHECK_THAT( v, Contains( v2 ) )
 with expansion:
   { 1, 2, 3 } Contains: { 1, 2, 4 }
 
@@ -1008,22 +1024,22 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, Equals(v2) )
+  CHECK_THAT( v, Equals( v2 ) )
 with expansion:
   { 1, 2, 3 } Equals: { 1, 2 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v2, Equals(v) )
+  CHECK_THAT( v2, Equals( v ) )
 with expansion:
   { 1, 2 } Equals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, Equals(v) )
+  CHECK_THAT( empty, Equals( v ) )
 with expansion:
   {  } Equals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, Equals(empty) )
+  CHECK_THAT( v, Equals( empty ) )
 with expansion:
   { 1, 2, 3 } Equals: {  }
 
@@ -1035,22 +1051,22 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, UnorderedEquals(empty) )
+  CHECK_THAT( v, UnorderedEquals( empty ) )
 with expansion:
   { 1, 2, 3 } UnorderedEquals: {  }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, UnorderedEquals(v) )
+  CHECK_THAT( empty, UnorderedEquals( v ) )
 with expansion:
   {  } UnorderedEquals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( permuted, UnorderedEquals(v) )
+  CHECK_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
   { 1, 3 } UnorderedEquals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( permuted, UnorderedEquals(v) )
+  CHECK_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
   { 3, 1 } UnorderedEquals: { 1, 2, 3 }
 
@@ -1135,11 +1151,6 @@ checkedElse, failing
 Misc.tests.cpp:<line number>
 ...............................................................................
 
-Misc.tests.cpp:<line number>: FAILED:
-  CHECKED_ELSE( flag )
-with expansion:
-  false
-
 Misc.tests.cpp:<line number>: FAILED:
   REQUIRE( testCheckedElse( false ) )
 with expansion:
@@ -1151,11 +1162,6 @@ checkedIf, failing
 Misc.tests.cpp:<line number>
 ...............................................................................
 
-Misc.tests.cpp:<line number>: FAILED:
-  CHECKED_IF( flag )
-with expansion:
-  false
-
 Misc.tests.cpp:<line number>: FAILED:
   REQUIRE( testCheckedIf( false ) )
 with expansion:
@@ -1380,6 +1386,6 @@ due to unexpected exception with message:
   Why would you throw a std::string?
 
 ===============================================================================
-test cases:  356 |  282 passed |  70 failed |  4 failed as expected
-assertions: 2088 | 1936 passed | 131 failed | 21 failed as expected
+test cases:  368 |  292 passed |  70 failed |  6 failed as expected
+assertions: 2103 | 1951 passed | 129 failed | 23 failed as expected
 
diff --git a/packages/Catch2/tests/SelfTest/Baselines/console.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/console.sw.approved.txt
index 7066d5938ed8da7966b7430a1916b9ed0cc79455..08cef9ace3d04e517a50191c4466ac7fe89d7520 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/console.sw.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/console.sw.approved.txt
@@ -744,6 +744,41 @@ Misc.tests.cpp:<line number>
 
 Misc.tests.cpp:<line number>: PASSED:
 
+-------------------------------------------------------------------------------
+#2152 - ULP checks between differently signed values were wrong - double
+-------------------------------------------------------------------------------
+Matchers.tests.cpp:<line number>
+...............................................................................
+
+Matchers.tests.cpp:<line number>: PASSED:
+  CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) )
+with expansion:
+  0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323,
+  4.9406564584124654e-324])
+
+Matchers.tests.cpp:<line number>: PASSED:
+  CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) )
+with expansion:
+  0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-
+  324, -0.0000000000000000e+00])
+
+-------------------------------------------------------------------------------
+#2152 - ULP checks between differently signed values were wrong - float
+-------------------------------------------------------------------------------
+Matchers.tests.cpp:<line number>
+...............................................................................
+
+Matchers.tests.cpp:<line number>: PASSED:
+  CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) )
+with expansion:
+  0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
+
+Matchers.tests.cpp:<line number>: PASSED:
+  CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) )
+with expansion:
+  0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.
+  00000000e+00])
+
 -------------------------------------------------------------------------------
 #748 - captures with unexpected exceptions
   outside assertions
@@ -2409,12 +2444,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1, Predicate<int>(alwaysTrue, "always true") )
+  REQUIRE_THAT( 1, Predicate<int>( alwaysTrue, "always true" ) )
 with expansion:
   1 matches predicate: "always true"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1, !Predicate<int>(alwaysFalse, "always false") )
+  REQUIRE_THAT( 1, !Predicate<int>( alwaysFalse, "always false" ) )
 with expansion:
   1 not matches predicate: "always false"
 
@@ -2426,12 +2461,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( "Hello olleH", Predicate<std::string>( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") )
+  REQUIRE_THAT( "Hello olleH", Predicate<std::string>( []( std::string const& str ) -> bool { return str.front() == str.back(); }, "First and last character should be equal" ) )
 with expansion:
   "Hello olleH" matches predicate: "First and last character should be equal"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( "This wouldn't pass", !Predicate<std::string>( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) )
+  REQUIRE_THAT( "This wouldn't pass", !Predicate<std::string>( []( std::string const& str ) -> bool { return str.front() == str.back(); } ) )
 with expansion:
   "This wouldn't pass" not matches undescribed predicate
 
@@ -2881,34 +2916,34 @@ Matchers.tests.cpp:<line number>
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype((MatcherA() && MatcherB()) && MatcherC()), Catch::
-  Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value
+  std::is_same< decltype( ( MatcherA() && MatcherB() ) && MatcherC() ), Catch::
+  Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1, (MatcherA() && MatcherB()) && MatcherC() )
+  REQUIRE_THAT( 1, ( MatcherA() && MatcherB() ) && MatcherC() )
 with expansion:
   1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T)
   1 )
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(MatcherA() && (MatcherB() && MatcherC())), Catch::
-  Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value
+  std::is_same< decltype( MatcherA() && ( MatcherB() && MatcherC() ) ), Catch::
+  Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1, MatcherA() && (MatcherB() && MatcherC()) )
+  REQUIRE_THAT( 1, MatcherA() && ( MatcherB() && MatcherC() ) )
 with expansion:
   1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T)
   1 )
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype((MatcherA() && MatcherB()) && (MatcherC() && MatcherD
-  ())), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB,
-  MatcherC, MatcherD> >::value
+  std::is_same< decltype( ( MatcherA() && MatcherB() ) && ( MatcherC() &&
+  MatcherD() ) ), Catch::Matchers::Detail:: MatchAllOfGeneric<MatcherA,
+  MatcherB, MatcherC, MatcherD>>:: value
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1, (MatcherA() && MatcherB()) && (MatcherC() && MatcherD()) )
+  REQUIRE_THAT( 1, ( MatcherA() && MatcherB() ) && ( MatcherC() && MatcherD() ) )
 with expansion:
   1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T)
   1 and equals: true )
@@ -2921,34 +2956,34 @@ Matchers.tests.cpp:<line number>
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype((MatcherA() || MatcherB()) || MatcherC()), Catch::
-  Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value
+  std::is_same< decltype( ( MatcherA() || MatcherB() ) || MatcherC() ), Catch::
+  Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1, (MatcherA() || MatcherB()) || MatcherC() )
+  REQUIRE_THAT( 1, ( MatcherA() || MatcherB() ) || MatcherC() )
 with expansion:
   1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1
   )
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(MatcherA() || (MatcherB() || MatcherC())), Catch::
-  Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value
+  std::is_same< decltype( MatcherA() || ( MatcherB() || MatcherC() ) ), Catch::
+  Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1, MatcherA() || (MatcherB() || MatcherC()) )
+  REQUIRE_THAT( 1, MatcherA() || ( MatcherB() || MatcherC() ) )
 with expansion:
   1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1
   )
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype((MatcherA() || MatcherB()) || (MatcherC() || MatcherD
-  ())), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB,
-  MatcherC, MatcherD> >::value
+  std::is_same< decltype( ( MatcherA() || MatcherB() ) || ( MatcherC() ||
+  MatcherD() ) ), Catch::Matchers::Detail:: MatchAnyOfGeneric<MatcherA,
+  MatcherB, MatcherC, MatcherD>>:: value
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1, (MatcherA() || MatcherB()) || (MatcherC() || MatcherD()) )
+  REQUIRE_THAT( 1, ( MatcherA() || MatcherB() ) || ( MatcherC() || MatcherD() ) )
 with expansion:
   1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1
   or equals: true )
@@ -2961,8 +2996,8 @@ Matchers.tests.cpp:<line number>
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(!MatcherA()), Catch::Matchers::Detail::
-  MatchNotOfGeneric<MatcherA> >::value
+  std::is_same< decltype( !MatcherA() ), Catch::Matchers::Detail::
+  MatchNotOfGeneric<MatcherA>>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
   REQUIRE_THAT( 0, !MatcherA() )
@@ -2971,7 +3006,7 @@ with expansion:
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(!!MatcherA()), MatcherA const& >::value
+  std::is_same<decltype( !!MatcherA() ), MatcherA const&>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
   REQUIRE_THAT( 1, !!MatcherA() )
@@ -2980,8 +3015,8 @@ with expansion:
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(!!!MatcherA()), Catch::Matchers::Detail::
-  MatchNotOfGeneric<MatcherA> >::value
+  std::is_same< decltype( !!!MatcherA() ), Catch::Matchers::Detail::
+  MatchNotOfGeneric<MatcherA>>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
   REQUIRE_THAT( 0, !!!MatcherA() )
@@ -2990,7 +3025,7 @@ with expansion:
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(!!!!MatcherA()), MatcherA const & >::value
+  std::is_same<decltype( !!!!MatcherA() ), MatcherA const&>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
   REQUIRE_THAT( 1, !!!!MatcherA() )
@@ -3005,9 +3040,9 @@ Matchers.tests.cpp:<line number>
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith
-  ("bar") && !EndsWith("foo"))), Catch::Matchers::Detail::MatchAnyOf<std::
-  string> >::value
+  std::is_same<decltype( StartsWith( "foo" ) || ( StartsWith( "bar" ) &&
+  EndsWith( "bar" ) && !EndsWith( "foo" ) ) ), Catch::Matchers::Detail::
+  MatchAnyOf<std::string>>::value
 
 -------------------------------------------------------------------------------
 Combining only templated matchers
@@ -3017,8 +3052,8 @@ Matchers.tests.cpp:<line number>
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(MatcherA() || MatcherB()), Catch::Matchers::Detail::
-  MatchAnyOfGeneric<MatcherA, MatcherB> >::value
+  std::is_same<decltype( MatcherA() || MatcherB() ), Catch::Matchers::Detail::
+  MatchAnyOfGeneric<MatcherA, MatcherB>>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
   REQUIRE_THAT( 1, MatcherA() || MatcherB() )
@@ -3027,8 +3062,8 @@ with expansion:
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(MatcherA() && MatcherB()), Catch::Matchers::Detail::
-  MatchAllOfGeneric<MatcherA, MatcherB> >::value
+  std::is_same<decltype( MatcherA() && MatcherB() ), Catch::Matchers::Detail::
+  MatchAllOfGeneric<MatcherA, MatcherB>>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
   REQUIRE_THAT( 1, MatcherA() && MatcherB() )
@@ -3037,9 +3072,9 @@ with expansion:
 
 Matchers.tests.cpp:<line number>: PASSED:
 with message:
-  std::is_same< decltype(MatcherA() || !MatcherB()), Catch::Matchers::Detail::
-  MatchAnyOfGeneric<MatcherA, Catch::Matchers::Detail::MatchNotOfGeneric
-  <MatcherB>> >::value
+  std::is_same< decltype( MatcherA() || !MatcherB() ), Catch::Matchers::Detail:
+  :MatchAnyOfGeneric< MatcherA, Catch::Matchers::Detail::MatchNotOfGeneric
+  <MatcherB>>>::value
 
 Matchers.tests.cpp:<line number>: PASSED:
   REQUIRE_THAT( 1, MatcherA() || !MatcherB() )
@@ -3053,43 +3088,43 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( vec, Predicate<std::vector<int>>([](auto const& v) { return std::all_of(v.begin(), v.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) )
+  REQUIRE_THAT( vec, Predicate<std::vector<int>>( []( auto const& v ) { return std::all_of( v.begin(), v.end(), []( int elem ) { return elem % 2 == 1; } ); }, "All elements are odd" ) && !EqualsRange( a ) )
 with expansion:
   { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5,
   3, 1 } )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") )
+  REQUIRE_THAT( str, StartsWith( "foo" ) && EqualsRange( arr ) && EndsWith( "bar" ) )
 with expansion:
   "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' }
   and ends with: "bar" )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") )
+  REQUIRE_THAT( str, StartsWith( "foo" ) && !EqualsRange( bad_arr ) && EndsWith( "bar" ) )
 with expansion:
   "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r'
   } and ends with: "bar" )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") )
+  REQUIRE_THAT( str, EqualsRange( arr ) && StartsWith( "foo" ) && EndsWith( "bar" ) )
 with expansion:
   "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo"
   and ends with: "bar" )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") )
+  REQUIRE_THAT( str, !EqualsRange( bad_arr ) && StartsWith( "foo" ) && EndsWith( "bar" ) )
 with expansion:
   "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with:
   "foo" and ends with: "bar" )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) )
+  REQUIRE_THAT( str, EqualsRange( bad_arr ) || ( StartsWith( "foo" ) && EndsWith( "bar" ) ) )
 with expansion:
   "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo"
   and ends with: "bar" ) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) )
+  REQUIRE_THAT( str, ( StartsWith( "foo" ) && EndsWith( "bar" ) ) || EqualsRange( bad_arr ) )
 with expansion:
   "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o',
   'f', 'b', 'a', 'r' } )
@@ -3101,7 +3136,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) )
+  REQUIRE_THAT( container, EqualsRange( a ) || EqualsRange( b ) || EqualsRange( c ) )
 with expansion:
   { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6
   } )
@@ -3419,7 +3454,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK( matcher.match(1) )
+  CHECK( matcher.match( 1 ) )
 with expansion:
   true
 
@@ -3484,13 +3519,13 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Contains("not there", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), Contains( "not there", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "not there" (case
   insensitive)
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Contains("STRING") )
+  CHECK_THAT( testStringForMatching(), Contains( "STRING" ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "STRING"
 
@@ -3733,12 +3768,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), EndsWith("Substring") )
+  CHECK_THAT( testStringForMatching(), EndsWith( "Substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" ends with: "Substring"
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" ends with: "this" (case
   insensitive)
@@ -3923,13 +3958,13 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), Equals("this string contains 'abc' as a substring") )
+  CHECK_THAT( testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" equals: "this string contains
   'abc' as a substring"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), Equals("this string contains 'ABC' as a substring", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" equals: "this string contains
   'abc' as a substring" (case insensitive)
@@ -3941,13 +3976,13 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Equals("this string contains 'ABC' as a substring") )
+  CHECK_THAT( testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" equals: "this string contains
   'ABC' as a substring"
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Equals("something else", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" equals: "something else" (case
   insensitive)
@@ -3985,11 +4020,11 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )
 because no exception was thrown where one was expected:
 
 Matchers.tests.cpp:<line number>: FAILED:
-  REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )
+  REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )
 because no exception was thrown where one was expected:
 
 -------------------------------------------------------------------------------
@@ -4000,12 +4035,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )
 due to unexpected exception with message:
   Unknown exception
 
 Matchers.tests.cpp:<line number>: FAILED:
-  REQUIRE_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )
+  REQUIRE_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )
 due to unexpected exception with message:
   Unknown exception
 
@@ -4017,12 +4052,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THROWS_MATCHES( throwsSpecialException(3), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 } )
 with expansion:
   SpecialException::what special exception has value of 1
 
 Matchers.tests.cpp:<line number>: FAILED:
-  REQUIRE_THROWS_MATCHES( throwsSpecialException(4), SpecialException, ExceptionMatcher{1} )
+  REQUIRE_THROWS_MATCHES( throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 } )
 with expansion:
   SpecialException::what special exception has value of 1
 
@@ -4033,12 +4068,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THROWS_MATCHES( throwsSpecialException(1), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( throwsSpecialException( 1 ), SpecialException, ExceptionMatcher{ 1 } )
 with expansion:
   SpecialException::what special exception has value of 1
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_MATCHES( throwsSpecialException(2), SpecialException, ExceptionMatcher{2} )
+  REQUIRE_THROWS_MATCHES( throwsSpecialException( 2 ), SpecialException, ExceptionMatcher{ 2 } )
 with expansion:
   SpecialException::what special exception has value of 2
 
@@ -4100,22 +4135,22 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_MATCHES( throwsDerivedException(), DerivedException, Message("DerivedException::what") )
+  REQUIRE_THROWS_MATCHES( throwsDerivedException(), DerivedException, Message( "DerivedException::what" ) )
 with expansion:
   DerivedException::what exception message matches "DerivedException::what"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_MATCHES( throwsDerivedException(), DerivedException, !Message("derivedexception::what") )
+  REQUIRE_THROWS_MATCHES( throwsDerivedException(), DerivedException, !Message( "derivedexception::what" ) )
 with expansion:
   DerivedException::what not exception message matches "derivedexception::what"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_MATCHES( throwsSpecialException(2), SpecialException, !Message("DerivedException::what") )
+  REQUIRE_THROWS_MATCHES( throwsSpecialException( 2 ), SpecialException, !Message( "DerivedException::what" ) )
 with expansion:
   SpecialException::what not exception message matches "DerivedException::what"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_MATCHES( throwsSpecialException(2), SpecialException, Message("SpecialException::what") )
+  REQUIRE_THROWS_MATCHES( throwsSpecialException( 2 ), SpecialException, Message( "SpecialException::what" ) )
 with expansion:
   SpecialException::what exception message matches "SpecialException::what"
 
@@ -4209,22 +4244,22 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 10., WithinRel(11.1, 0.1) )
+  REQUIRE_THAT( 10., WithinRel( 11.1, 0.1 ) )
 with expansion:
   10.0 and 11.1 are within 10% of each other
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 10., !WithinRel(11.2, 0.1) )
+  REQUIRE_THAT( 10., !WithinRel( 11.2, 0.1 ) )
 with expansion:
   10.0 not and 11.2 are within 10% of each other
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1., !WithinRel(0., 0.99) )
+  REQUIRE_THAT( 1., !WithinRel( 0., 0.99 ) )
 with expansion:
   1.0 not and 0 are within 99% of each other
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( -0., WithinRel(0.) )
+  REQUIRE_THAT( -0., WithinRel( 0. ) )
 with expansion:
   -0.0 and 0 are within 2.22045e-12% of each other
 
@@ -4237,7 +4272,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( v1, WithinRel(v2) )
+  REQUIRE_THAT( v1, WithinRel( v2 ) )
 with expansion:
   0.0 and 2.22507e-308 are within 2.22045e-12% of each other
 
@@ -4249,42 +4284,42 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1., WithinAbs(1., 0) )
+  REQUIRE_THAT( 1., WithinAbs( 1., 0 ) )
 with expansion:
   1.0 is within 0.0 of 1.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0., WithinAbs(1., 1) )
+  REQUIRE_THAT( 0., WithinAbs( 1., 1 ) )
 with expansion:
   0.0 is within 1.0 of 1.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0., !WithinAbs(1., 0.99) )
+  REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) )
 with expansion:
   0.0 not is within 0.99 of 1.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0., !WithinAbs(1., 0.99) )
+  REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) )
 with expansion:
   0.0 not is within 0.99 of 1.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 11., !WithinAbs(10., 0.5) )
+  REQUIRE_THAT( 11., !WithinAbs( 10., 0.5 ) )
 with expansion:
   11.0 not is within 0.5 of 10.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 10., !WithinAbs(11., 0.5) )
+  REQUIRE_THAT( 10., !WithinAbs( 11., 0.5 ) )
 with expansion:
   10.0 not is within 0.5 of 11.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( -10., WithinAbs(-10., 0.5) )
+  REQUIRE_THAT( -10., WithinAbs( -10., 0.5 ) )
 with expansion:
   -10.0 is within 0.5 of -10.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( -10., WithinAbs(-9.6, 0.5) )
+  REQUIRE_THAT( -10., WithinAbs( -9.6, 0.5 ) )
 with expansion:
   -10.0 is within 0.5 of -9.6
 
@@ -4296,43 +4331,43 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1., WithinULP(1., 0) )
+  REQUIRE_THAT( 1., WithinULP( 1., 0 ) )
 with expansion:
   1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.
   0000000000000000e+00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( nextafter(1., 2.), WithinULP(1., 1) )
+  REQUIRE_THAT( nextafter( 1., 2. ), WithinULP( 1., 1 ) )
 with expansion:
   1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.
   0000000000000002e+00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0., WithinULP(nextafter(0., 1.), 1) )
+  REQUIRE_THAT( 0., WithinULP( nextafter( 0., 1. ), 1 ) )
 with expansion:
   0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.
   8813129168249309e-324])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1., WithinULP(nextafter(1., 0.), 1) )
+  REQUIRE_THAT( 1., WithinULP( nextafter( 1., 0. ), 1 ) )
 with expansion:
   1.0 is within 1 ULPs of 9.9999999999999989e-01 ([9.9999999999999978e-01, 1.
   0000000000000000e+00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1., !WithinULP(nextafter(1., 2.), 0) )
+  REQUIRE_THAT( 1., !WithinULP( nextafter( 1., 2. ), 0 ) )
 with expansion:
   1.0 not is within 0 ULPs of 1.0000000000000002e+00 ([1.0000000000000002e+00,
   1.0000000000000002e+00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1., WithinULP(1., 0) )
+  REQUIRE_THAT( 1., WithinULP( 1., 0 ) )
 with expansion:
   1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.
   0000000000000000e+00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( -0., WithinULP(0., 0) )
+  REQUIRE_THAT( -0., WithinULP( 0., 0 ) )
 with expansion:
   -0.0 is within 0 ULPs of 0.0000000000000000e+00 ([0.0000000000000000e+00, 0.
   0000000000000000e+00])
@@ -4345,19 +4380,19 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1., WithinAbs(1., 0.5) || WithinULP(2., 1) )
+  REQUIRE_THAT( 1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 ) )
 with expansion:
   1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0000000000000000e+00 ([1.
   9999999999999998e+00, 2.0000000000000004e+00]) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1., WithinAbs(2., 0.5) || WithinULP(1., 0) )
+  REQUIRE_THAT( 1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 ) )
 with expansion:
   1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.
   0000000000000000e+00, 1.0000000000000000e+00]) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0.0001, WithinAbs(0., 0.001) || WithinRel(0., 0.1) )
+  REQUIRE_THAT( 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) )
 with expansion:
   0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
 
@@ -4369,22 +4404,22 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_NOTHROW( WithinAbs(1., 0.) )
+  REQUIRE_NOTHROW( WithinAbs( 1., 0. ) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_AS( WithinAbs(1., -1.), std::domain_error )
+  REQUIRE_THROWS_AS( WithinAbs( 1., -1. ), std::domain_error )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_NOTHROW( WithinULP(1., 0) )
+  REQUIRE_NOTHROW( WithinULP( 1., 0 ) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_NOTHROW( WithinRel(1., 0.) )
+  REQUIRE_NOTHROW( WithinRel( 1., 0. ) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_AS( WithinRel(1., -0.2), std::domain_error )
+  REQUIRE_THROWS_AS( WithinRel( 1., -0.2 ), std::domain_error )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_AS( WithinRel(1., 1.), std::domain_error )
+  REQUIRE_THROWS_AS( WithinRel( 1., 1. ), std::domain_error )
 
 -------------------------------------------------------------------------------
 Floating point matchers: float
@@ -4394,22 +4429,22 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 10.f, WithinRel(11.1f, 0.1f) )
+  REQUIRE_THAT( 10.f, WithinRel( 11.1f, 0.1f ) )
 with expansion:
   10.0f and 11.1 are within 10% of each other
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 10.f, !WithinRel(11.2f, 0.1f) )
+  REQUIRE_THAT( 10.f, !WithinRel( 11.2f, 0.1f ) )
 with expansion:
   10.0f not and 11.2 are within 10% of each other
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1.f, !WithinRel(0.f, 0.99f) )
+  REQUIRE_THAT( 1.f, !WithinRel( 0.f, 0.99f ) )
 with expansion:
   1.0f not and 0 are within 99% of each other
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( -0.f, WithinRel(0.f) )
+  REQUIRE_THAT( -0.f, WithinRel( 0.f ) )
 with expansion:
   -0.0f and 0 are within 0.00119209% of each other
 
@@ -4422,7 +4457,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( v1, WithinRel(v2) )
+  REQUIRE_THAT( v1, WithinRel( v2 ) )
 with expansion:
   0.0f and 1.17549e-38 are within 0.00119209% of each other
 
@@ -4434,47 +4469,47 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1.f, WithinAbs(1.f, 0) )
+  REQUIRE_THAT( 1.f, WithinAbs( 1.f, 0 ) )
 with expansion:
   1.0f is within 0.0 of 1.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0.f, WithinAbs(1.f, 1) )
+  REQUIRE_THAT( 0.f, WithinAbs( 1.f, 1 ) )
 with expansion:
   0.0f is within 1.0 of 1.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0.f, !WithinAbs(1.f, 0.99f) )
+  REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) )
 with expansion:
   0.0f not is within 0.9900000095 of 1.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0.f, !WithinAbs(1.f, 0.99f) )
+  REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) )
 with expansion:
   0.0f not is within 0.9900000095 of 1.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0.f, WithinAbs(-0.f, 0) )
+  REQUIRE_THAT( 0.f, WithinAbs( -0.f, 0 ) )
 with expansion:
   0.0f is within 0.0 of -0.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 11.f, !WithinAbs(10.f, 0.5f) )
+  REQUIRE_THAT( 11.f, !WithinAbs( 10.f, 0.5f ) )
 with expansion:
   11.0f not is within 0.5 of 10.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 10.f, !WithinAbs(11.f, 0.5f) )
+  REQUIRE_THAT( 10.f, !WithinAbs( 11.f, 0.5f ) )
 with expansion:
   10.0f not is within 0.5 of 11.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( -10.f, WithinAbs(-10.f, 0.5f) )
+  REQUIRE_THAT( -10.f, WithinAbs( -10.f, 0.5f ) )
 with expansion:
   -10.0f is within 0.5 of -10.0
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( -10.f, WithinAbs(-9.6f, 0.5f) )
+  REQUIRE_THAT( -10.f, WithinAbs( -9.6f, 0.5f ) )
 with expansion:
   -10.0f is within 0.5 of -9.6000003815
 
@@ -4486,38 +4521,44 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1.f, WithinULP(1.f, 0) )
+  REQUIRE_THAT( 1.f, WithinULP( 1.f, 0 ) )
 with expansion:
   1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( nextafter(1.f, 2.f), WithinULP(1.f, 1) )
+  REQUIRE_THAT( -1.f, WithinULP( -1.f, 0 ) )
+with expansion:
+  -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+
+  00])
+
+Matchers.tests.cpp:<line number>: PASSED:
+  REQUIRE_THAT( nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) )
 with expansion:
   1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0.f, WithinULP(nextafter(0.f, 1.f), 1) )
+  REQUIRE_THAT( 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) )
 with expansion:
   0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1.f, WithinULP(nextafter(1.f, 0.f), 1) )
+  REQUIRE_THAT( 1.f, WithinULP( nextafter( 1.f, 0.f ), 1 ) )
 with expansion:
   1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1.f, !WithinULP(nextafter(1.f, 2.f), 0) )
+  REQUIRE_THAT( 1.f, !WithinULP( nextafter( 1.f, 2.f ), 0 ) )
 with expansion:
   1.0f not is within 0 ULPs of 1.00000012e+00f ([1.00000012e+00, 1.00000012e+
   00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1.f, WithinULP(1.f, 0) )
+  REQUIRE_THAT( 1.f, WithinULP( 1.f, 0 ) )
 with expansion:
   1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( -0.f, WithinULP(0.f, 0) )
+  REQUIRE_THAT( -0.f, WithinULP( 0.f, 0 ) )
 with expansion:
   -0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00])
 
@@ -4529,19 +4570,19 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1) )
+  REQUIRE_THAT( 1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 ) )
 with expansion:
   1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.00000000e+00f ([9.
   99999940e-01, 1.00000012e+00]) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0) )
+  REQUIRE_THAT( 1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 ) )
 with expansion:
   1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.
   00000000e+00, 1.00000000e+00]) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( 0.0001f, WithinAbs(0.f, 0.001f) || WithinRel(0.f, 0.1f) )
+  REQUIRE_THAT( 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) )
 with expansion:
   0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
 
@@ -4553,25 +4594,25 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_NOTHROW( WithinAbs(1.f, 0.f) )
+  REQUIRE_NOTHROW( WithinAbs( 1.f, 0.f ) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_AS( WithinAbs(1.f, -1.f), std::domain_error )
+  REQUIRE_THROWS_AS( WithinAbs( 1.f, -1.f ), std::domain_error )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_NOTHROW( WithinULP(1.f, 0) )
+  REQUIRE_NOTHROW( WithinULP( 1.f, 0 ) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_AS( WithinULP(1.f, static_cast<uint64_t>(-1)), std::domain_error )
+  REQUIRE_THROWS_AS( WithinULP( 1.f, static_cast<uint64_t>( -1 ) ), std::domain_error )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_NOTHROW( WithinRel(1.f, 0.f) )
+  REQUIRE_NOTHROW( WithinRel( 1.f, 0.f ) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_AS( WithinRel(1.f, -0.2f), std::domain_error )
+  REQUIRE_THROWS_AS( WithinRel( 1.f, -0.2f ), std::domain_error )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_AS( WithinRel(1.f, 1.f), std::domain_error )
+  REQUIRE_THROWS_AS( WithinRel( 1.f, 1.f ), std::domain_error )
 
 -------------------------------------------------------------------------------
 Generators -- adapters
@@ -6852,7 +6893,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), Contains("string") && Contains("abc") && Contains("substring") && Contains("contains") )
+  CHECK_THAT( testStringForMatching(), Contains( "string" ) && Contains( "abc" ) && Contains( "substring" ) && Contains( "contains" ) )
 with expansion:
   "this string contains 'abc' as a substring" ( contains: "string" and
   contains: "abc" and contains: "substring" and contains: "contains" )
@@ -6864,13 +6905,13 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), Contains("string") || Contains("different") || Contains("random") )
+  CHECK_THAT( testStringForMatching(), Contains( "string" ) || Contains( "different" ) || Contains( "random" ) )
 with expansion:
   "this string contains 'abc' as a substring" ( contains: "string" or contains:
   "different" or contains: "random" )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching2(), Contains("string") || Contains("different") || Contains("random") )
+  CHECK_THAT( testStringForMatching2(), Contains( "string" ) || Contains( "different" ) || Contains( "random" ) )
 with expansion:
   "some completely different text that contains one common word" ( contains:
   "string" or contains: "different" or contains: "random" )
@@ -6882,7 +6923,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), (Contains("string") || Contains("different")) && Contains("substring") )
+  CHECK_THAT( testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) && Contains( "substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" ( ( contains: "string" or
   contains: "different" ) and contains: "substring" )
@@ -6894,7 +6935,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), (Contains("string") || Contains("different")) && Contains("random") )
+  CHECK_THAT( testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) && Contains( "random" ) )
 with expansion:
   "this string contains 'abc' as a substring" ( ( contains: "string" or
   contains: "different" ) and contains: "random" )
@@ -6906,7 +6947,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), !Contains("different") )
+  CHECK_THAT( testStringForMatching(), !Contains( "different" ) )
 with expansion:
   "this string contains 'abc' as a substring" not contains: "different"
 
@@ -6917,7 +6958,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), !Contains("substring") )
+  CHECK_THAT( testStringForMatching(), !Contains( "substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" not contains: "substring"
 
@@ -7510,16 +7551,16 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS_AS( (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed )
+  REQUIRE_THROWS_AS( ( EvilMatcher(), EvilMatcher() ), EvilCommaOperatorUsed )
 
 Matchers.tests.cpp:<line number>: PASSED:
   REQUIRE_THROWS_AS( &EvilMatcher(), EvilAddressOfOperatorUsed )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_NOTHROW( EvilMatcher() || (EvilMatcher() && !EvilMatcher()) )
+  REQUIRE_NOTHROW( EvilMatcher() || ( EvilMatcher() && !EvilMatcher() ) )
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_NOTHROW( (EvilMatcher() && EvilMatcher()) || !EvilMatcher() )
+  REQUIRE_NOTHROW( ( EvilMatcher() && EvilMatcher() ) || !EvilMatcher() )
 
 -------------------------------------------------------------------------------
 Parse test names and tags
@@ -8608,7 +8649,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( "foo", Predicate<const char*>([] (const char* const&) { return true; }) )
+  REQUIRE_THAT( "foo", Predicate<const char*>( []( const char* const& ) { return true; } ) )
 with expansion:
   "foo" matches undescribed predicate
 
@@ -9371,19 +9412,19 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Matches("this STRING contains 'abc' as a substring") )
+  CHECK_THAT( testStringForMatching(), Matches( "this STRING contains 'abc' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" matches "this STRING contains
   'abc' as a substring" case sensitively
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Matches("contains 'abc' as a substring") )
+  CHECK_THAT( testStringForMatching(), Matches( "contains 'abc' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" matches "contains 'abc' as a
   substring" case sensitively
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), Matches("this string contains 'abc' as a") )
+  CHECK_THAT( testStringForMatching(), Matches( "this string contains 'abc' as a" ) )
 with expansion:
   "this string contains 'abc' as a substring" matches "this string contains
   'abc' as a" case sensitively
@@ -9395,7 +9436,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( actual, !UnorderedEquals(expected) )
+  CHECK_THAT( actual, !UnorderedEquals( expected ) )
 with expansion:
   { 'a', 'b' } not UnorderedEquals: { 'c', 'b' }
 
@@ -10378,12 +10419,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), StartsWith("This String") )
+  CHECK_THAT( testStringForMatching(), StartsWith( "This String" ) )
 with expansion:
   "this string contains 'abc' as a substring" starts with: "This String"
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" starts with: "string" (case
   insensitive)
@@ -10433,45 +10474,45 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( testStringForMatching(), Contains("string") )
+  REQUIRE_THAT( testStringForMatching(), Contains( "string" ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "string"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( testStringForMatching(), Contains("string", Catch::CaseSensitive::No) )
+  REQUIRE_THAT( testStringForMatching(), Contains( "string", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "string" (case
   insensitive)
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), Contains("abc") )
+  CHECK_THAT( testStringForMatching(), Contains( "abc" ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "abc"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), Contains("aBC", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), Contains( "aBC", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "abc" (case
   insensitive)
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), StartsWith("this") )
+  CHECK_THAT( testStringForMatching(), StartsWith( "this" ) )
 with expansion:
   "this string contains 'abc' as a substring" starts with: "this"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), StartsWith("THIS", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" starts with: "this" (case
   insensitive)
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), EndsWith("substring") )
+  CHECK_THAT( testStringForMatching(), EndsWith( "substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" ends with: "substring"
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( testStringForMatching(), EndsWith(" SuBsTrInG", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" ends with: " substring" (case
   insensitive)
@@ -10494,12 +10535,7 @@ with expansion:
   0 == 0
 
 String.tests.cpp:<line number>: PASSED:
-  REQUIRE( empty.isNullTerminated() )
-with expansion:
-  true
-
-String.tests.cpp:<line number>: PASSED:
-  REQUIRE( std::strcmp( empty.c_str(), "" ) == 0 )
+  REQUIRE( std::strcmp( empty.data(), "" ) == 0 )
 with expansion:
   0 == 0
 
@@ -10520,24 +10556,11 @@ String.tests.cpp:<line number>: PASSED:
 with expansion:
   5 == 5
 
-String.tests.cpp:<line number>: PASSED:
-  REQUIRE( s.isNullTerminated() )
-with expansion:
-  true
-
 String.tests.cpp:<line number>: PASSED:
   REQUIRE( std::strcmp( rawChars, "hello" ) == 0 )
 with expansion:
   0 == 0
 
-String.tests.cpp:<line number>: PASSED:
-  REQUIRE_NOTHROW( s.c_str() )
-
-String.tests.cpp:<line number>: PASSED:
-  REQUIRE( s.c_str() == rawChars )
-with expansion:
-  "hello" == "hello"
-
 String.tests.cpp:<line number>: PASSED:
   REQUIRE( s.data() == rawChars )
 with expansion:
@@ -10553,14 +10576,6 @@ String.tests.cpp:<line number>
 String.tests.cpp:<line number>: PASSED:
   REQUIRE( original == "original" )
 
-String.tests.cpp:<line number>: PASSED:
-  REQUIRE_FALSE( original.isNullTerminated() )
-with expansion:
-  !false
-
-String.tests.cpp:<line number>: PASSED:
-  REQUIRE_THROWS( original.c_str() )
-
 String.tests.cpp:<line number>: PASSED:
   REQUIRE_NOTHROW( original.data() )
 
@@ -10606,7 +10621,7 @@ with expansion:
   6 == 6
 
 String.tests.cpp:<line number>: PASSED:
-  REQUIRE( std::strcmp( ss.c_str(), "world!" ) == 0 )
+  REQUIRE( std::strcmp( ss.data(), "world!" ) == 0 )
 with expansion:
   0 == 0
 
@@ -10658,7 +10673,7 @@ String.tests.cpp:<line number>
 ...............................................................................
 
 String.tests.cpp:<line number>: PASSED:
-  REQUIRE( std::strcmp(ss.c_str(), "world!") == 0 )
+  REQUIRE( std::strcmp(ss.data(), "world!") == 0 )
 with expansion:
   0 == 0
 
@@ -10832,10 +10847,6 @@ String.tests.cpp:<line number>: PASSED:
 with message:
   stringref.size() == 3
 
-String.tests.cpp:<line number>: PASSED:
-with message:
-  stringref.isNullTerminated()
-
 String.tests.cpp:<line number>: PASSED:
 with message:
   stringref.data() == abc
@@ -10872,14 +10883,6 @@ String.tests.cpp:<line number>: PASSED:
 with message:
   shortened.begin() != shortened.end()
 
-String.tests.cpp:<line number>: PASSED:
-with message:
-  !(shortened.isNullTerminated())
-
-String.tests.cpp:<line number>: PASSED:
-with message:
-  !(shortened.substr(1, 3).isNullTerminated())
-
 -------------------------------------------------------------------------------
 StringRef at compilation time
   UDL construction
@@ -10895,10 +10898,6 @@ String.tests.cpp:<line number>: PASSED:
 with message:
   sr1.size() == 3
 
-String.tests.cpp:<line number>: PASSED:
-with message:
-  sr1.isNullTerminated()
-
 String.tests.cpp:<line number>: PASSED:
 with message:
   sr2.empty()
@@ -10907,9 +10906,53 @@ String.tests.cpp:<line number>: PASSED:
 with message:
   sr2.size() == 0
 
-String.tests.cpp:<line number>: PASSED:
-with message:
-  sr2.isNullTerminated()
+-------------------------------------------------------------------------------
+Stringifying char arrays with statically known sizes - char
+-------------------------------------------------------------------------------
+ToString.tests.cpp:<line number>
+...............................................................................
+
+ToString.tests.cpp:<line number>: PASSED:
+  CHECK( ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s )
+with expansion:
+  ""abc"" == ""abc""
+
+ToString.tests.cpp:<line number>: PASSED:
+  CHECK( ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s )
+with expansion:
+  ""abc"" == ""abc""
+
+-------------------------------------------------------------------------------
+Stringifying char arrays with statically known sizes - signed char
+-------------------------------------------------------------------------------
+ToString.tests.cpp:<line number>
+...............................................................................
+
+ToString.tests.cpp:<line number>: PASSED:
+  CHECK( ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s )
+with expansion:
+  ""abc"" == ""abc""
+
+ToString.tests.cpp:<line number>: PASSED:
+  CHECK( ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s )
+with expansion:
+  ""abc"" == ""abc""
+
+-------------------------------------------------------------------------------
+Stringifying char arrays with statically known sizes - unsigned char
+-------------------------------------------------------------------------------
+ToString.tests.cpp:<line number>
+...............................................................................
+
+ToString.tests.cpp:<line number>: PASSED:
+  CHECK( ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s )
+with expansion:
+  ""abc"" == ""abc""
+
+ToString.tests.cpp:<line number>: PASSED:
+  CHECK( ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s )
+with expansion:
+  ""abc"" == ""abc""
 
 -------------------------------------------------------------------------------
 Stringifying std::chrono::duration helpers
@@ -12344,6 +12387,50 @@ CmdLine.tests.cpp:<line number>
 
 CmdLine.tests.cpp:<line number>: PASSED:
 
+-------------------------------------------------------------------------------
+Testing checked-if
+-------------------------------------------------------------------------------
+Misc.tests.cpp:<line number>
+...............................................................................
+
+Misc.tests.cpp:<line number>: PASSED:
+  CHECKED_IF( true )
+
+Misc.tests.cpp:<line number>: PASSED:
+
+Misc.tests.cpp:<line number>: FAILED - but was ok:
+  CHECKED_IF( false )
+
+Misc.tests.cpp:<line number>: PASSED:
+  CHECKED_ELSE( true )
+
+Misc.tests.cpp:<line number>: FAILED - but was ok:
+  CHECKED_ELSE( false )
+
+Misc.tests.cpp:<line number>: PASSED:
+
+-------------------------------------------------------------------------------
+Testing checked-if 2
+-------------------------------------------------------------------------------
+Misc.tests.cpp:<line number>
+...............................................................................
+
+Misc.tests.cpp:<line number>: PASSED:
+  CHECKED_IF( true )
+
+Misc.tests.cpp:<line number>: FAILED:
+
+-------------------------------------------------------------------------------
+Testing checked-if 3
+-------------------------------------------------------------------------------
+Misc.tests.cpp:<line number>
+...............................................................................
+
+Misc.tests.cpp:<line number>: FAILED - but was ok:
+  CHECKED_ELSE( false )
+
+Misc.tests.cpp:<line number>: FAILED:
+
 -------------------------------------------------------------------------------
 The NO_FAIL macro reports a failure but does not fail the test
 -------------------------------------------------------------------------------
@@ -13438,7 +13525,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( empty, Approx(empty) )
+  REQUIRE_THAT( empty, Approx( empty ) )
 with expansion:
   {  } is approx: {  }
 
@@ -13451,12 +13538,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( v1, Approx(v1) )
+  REQUIRE_THAT( v1, Approx( v1 ) )
 with expansion:
   { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( v1, Approx<double>({ 1., 2., 3. }) )
+  REQUIRE_THAT( v1, Approx<double>( { 1., 2., 3. } ) )
 with expansion:
   { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
 
@@ -13469,7 +13556,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( v1, !Approx(temp) )
+  REQUIRE_THAT( v1, !Approx( temp ) )
 with expansion:
   { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 }
 
@@ -13482,22 +13569,22 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( v1, !Approx(v2) )
+  REQUIRE_THAT( v1, !Approx( v2 ) )
 with expansion:
   { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( v1, Approx(v2).margin(0.5) )
+  REQUIRE_THAT( v1, Approx( v2 ).margin( 0.5 ) )
 with expansion:
   { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( v1, Approx(v2).epsilon(0.5) )
+  REQUIRE_THAT( v1, Approx( v2 ).epsilon( 0.5 ) )
 with expansion:
   { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( v1, Approx(v2).epsilon(0.1).scale(500) )
+  REQUIRE_THAT( v1, Approx( v2 ).epsilon( 0.1 ).scale( 500 ) )
 with expansion:
   { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
 
@@ -13509,7 +13596,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, Approx(t1) )
+  CHECK_THAT( empty, Approx( t1 ) )
 with expansion:
   {  } is approx: { 1.0, 2.0 }
 
@@ -13521,7 +13608,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v1, Approx(v2) )
+  CHECK_THAT( v1, Approx( v2 ) )
 with expansion:
   { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
 
@@ -13533,17 +13620,17 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, VectorContains(1) )
+  CHECK_THAT( v, VectorContains( 1 ) )
 with expansion:
   { 1, 2, 3 } Contains: 1
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, VectorContains(2) )
+  CHECK_THAT( v, VectorContains( 2 ) )
 with expansion:
   { 1, 2, 3 } Contains: 2
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v5, (VectorContains<int, CustomAllocator<int>>(2)) )
+  CHECK_THAT( v5, ( VectorContains<int, CustomAllocator<int>>( 2 ) ) )
 with expansion:
   { 1, 2, 3 } Contains: 2
 
@@ -13555,42 +13642,42 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, Contains(v2) )
+  CHECK_THAT( v, Contains( v2 ) )
 with expansion:
   { 1, 2, 3 } Contains: { 1, 2 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, Contains<int>({ 1, 2 }) )
+  CHECK_THAT( v, Contains<int>( { 1, 2 } ) )
 with expansion:
   { 1, 2, 3 } Contains: { 1, 2 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v5, (Contains<int, std::allocator<int>, CustomAllocator<int>>(v2)) )
+  CHECK_THAT( v5, ( Contains<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) )
 with expansion:
   { 1, 2, 3 } Contains: { 1, 2 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, Contains(v2) )
+  CHECK_THAT( v, Contains( v2 ) )
 with expansion:
   { 1, 2, 3 } Contains: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, Contains(empty) )
+  CHECK_THAT( v, Contains( empty ) )
 with expansion:
   { 1, 2, 3 } Contains: {  }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( empty, Contains(empty) )
+  CHECK_THAT( empty, Contains( empty ) )
 with expansion:
   {  } Contains: {  }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v5, (Contains<int, std::allocator<int>, CustomAllocator<int>>(v2)) )
+  CHECK_THAT( v5, ( Contains<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) )
 with expansion:
   { 1, 2, 3 } Contains: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v5, Contains(v6) )
+  CHECK_THAT( v5, Contains( v6 ) )
 with expansion:
   { 1, 2, 3 } Contains: { 1, 2 }
 
@@ -13602,7 +13689,7 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, VectorContains(1) && VectorContains(2) )
+  CHECK_THAT( v, VectorContains( 1 ) && VectorContains( 2 ) )
 with expansion:
   { 1, 2, 3 } ( Contains: 1 and Contains: 2 )
 
@@ -13614,32 +13701,32 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, Equals(v) )
+  CHECK_THAT( v, Equals( v ) )
 with expansion:
   { 1, 2, 3 } Equals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( empty, Equals(empty) )
+  CHECK_THAT( empty, Equals( empty ) )
 with expansion:
   {  } Equals: {  }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, Equals<int>({ 1, 2, 3 }) )
+  CHECK_THAT( v, Equals<int>( { 1, 2, 3 } ) )
 with expansion:
   { 1, 2, 3 } Equals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, Equals(v2) )
+  CHECK_THAT( v, Equals( v2 ) )
 with expansion:
   { 1, 2, 3 } Equals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v5, (Equals<int, std::allocator<int>, CustomAllocator<int>>(v2)) )
+  CHECK_THAT( v5, ( Equals<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) )
 with expansion:
   { 1, 2, 3 } Equals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v5, Equals(v6) )
+  CHECK_THAT( v5, Equals( v6 ) )
 with expansion:
   { 1, 2, 3 } Equals: { 1, 2, 3 }
 
@@ -13651,37 +13738,37 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, UnorderedEquals(v) )
+  CHECK_THAT( v, UnorderedEquals( v ) )
 with expansion:
   { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v, UnorderedEquals<int>({ 3, 2, 1 }) )
+  CHECK_THAT( v, UnorderedEquals<int>( { 3, 2, 1 } ) )
 with expansion:
   { 1, 2, 3 } UnorderedEquals: { 3, 2, 1 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( empty, UnorderedEquals(empty) )
+  CHECK_THAT( empty, UnorderedEquals( empty ) )
 with expansion:
   {  } UnorderedEquals: {  }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( permuted, UnorderedEquals(v) )
+  REQUIRE_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
   { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  REQUIRE_THAT( permuted, UnorderedEquals(v) )
+  REQUIRE_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
   { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v5, (UnorderedEquals<int, std::allocator<int>, CustomAllocator<int>>(permuted)) )
+  CHECK_THAT( v5, ( UnorderedEquals<int, std::allocator<int>, CustomAllocator<int>>( permuted ) ) )
 with expansion:
   { 1, 2, 3 } UnorderedEquals: { 2, 3, 1 }
 
 Matchers.tests.cpp:<line number>: PASSED:
-  CHECK_THAT( v5_permuted, UnorderedEquals(v5) )
+  CHECK_THAT( v5_permuted, UnorderedEquals( v5 ) )
 with expansion:
   { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
 
@@ -13693,12 +13780,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, VectorContains(-1) )
+  CHECK_THAT( v, VectorContains( -1 ) )
 with expansion:
   { 1, 2, 3 } Contains: -1
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, VectorContains(1) )
+  CHECK_THAT( empty, VectorContains( 1 ) )
 with expansion:
   {  } Contains: 1
 
@@ -13710,12 +13797,12 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, Contains(v) )
+  CHECK_THAT( empty, Contains( v ) )
 with expansion:
   {  } Contains: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, Contains(v2) )
+  CHECK_THAT( v, Contains( v2 ) )
 with expansion:
   { 1, 2, 3 } Contains: { 1, 2, 4 }
 
@@ -13727,22 +13814,22 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, Equals(v2) )
+  CHECK_THAT( v, Equals( v2 ) )
 with expansion:
   { 1, 2, 3 } Equals: { 1, 2 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v2, Equals(v) )
+  CHECK_THAT( v2, Equals( v ) )
 with expansion:
   { 1, 2 } Equals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, Equals(v) )
+  CHECK_THAT( empty, Equals( v ) )
 with expansion:
   {  } Equals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, Equals(empty) )
+  CHECK_THAT( v, Equals( empty ) )
 with expansion:
   { 1, 2, 3 } Equals: {  }
 
@@ -13754,22 +13841,22 @@ Matchers.tests.cpp:<line number>
 ...............................................................................
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( v, UnorderedEquals(empty) )
+  CHECK_THAT( v, UnorderedEquals( empty ) )
 with expansion:
   { 1, 2, 3 } UnorderedEquals: {  }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( empty, UnorderedEquals(v) )
+  CHECK_THAT( empty, UnorderedEquals( v ) )
 with expansion:
   {  } UnorderedEquals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( permuted, UnorderedEquals(v) )
+  CHECK_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
   { 1, 3 } UnorderedEquals: { 1, 2, 3 }
 
 Matchers.tests.cpp:<line number>: FAILED:
-  CHECK_THAT( permuted, UnorderedEquals(v) )
+  CHECK_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
   { 3, 1 } UnorderedEquals: { 1, 2, 3 }
 
@@ -14022,6 +14109,19 @@ Xml.tests.cpp:<line number>: PASSED:
 with expansion:
   "[\x7F]" == "[\x7F]"
 
+-------------------------------------------------------------------------------
+XmlWriter writes boolean attributes as true/false
+-------------------------------------------------------------------------------
+Xml.tests.cpp:<line number>
+...............................................................................
+
+Xml.tests.cpp:<line number>: PASSED:
+  REQUIRE_THAT( stream.str(), Contains(R"(attr1="true")") && Contains(R"(attr2="false")") )
+with expansion:
+  "<?xml version="1.0" encoding="UTF-8"?>
+  <Element1 attr1="true" attr2="false"/>
+  " ( contains: "attr1="true"" and contains: "attr2="false"" )
+
 -------------------------------------------------------------------------------
 analyse no analysis
 -------------------------------------------------------------------------------
@@ -14222,7 +14322,7 @@ checkedElse, failing
 Misc.tests.cpp:<line number>
 ...............................................................................
 
-Misc.tests.cpp:<line number>: FAILED:
+Misc.tests.cpp:<line number>: FAILED - but was ok:
   CHECKED_ELSE( flag )
 with expansion:
   false
@@ -14254,7 +14354,7 @@ checkedIf, failing
 Misc.tests.cpp:<line number>
 ...............................................................................
 
-Misc.tests.cpp:<line number>: FAILED:
+Misc.tests.cpp:<line number>: FAILED - but was ok:
   CHECKED_IF( flag )
 with expansion:
   false
@@ -14538,6 +14638,55 @@ Condition.tests.cpp:<line number>: PASSED:
 with expansion:
   1 == 1
 
+-------------------------------------------------------------------------------
+convertToBits
+-------------------------------------------------------------------------------
+FloatingPoint.tests.cpp:<line number>
+...............................................................................
+
+FloatingPoint.tests.cpp:<line number>: PASSED:
+  CHECK( convertToBits( 0.f ) == 0 )
+with expansion:
+  0 == 0
+
+FloatingPoint.tests.cpp:<line number>: PASSED:
+  CHECK( convertToBits( -0.f ) == ( 1ULL << 31 ) )
+with expansion:
+  2147483648 (0x<hex digits>)
+  ==
+  2147483648 (0x<hex digits>)
+
+FloatingPoint.tests.cpp:<line number>: PASSED:
+  CHECK( convertToBits( 0. ) == 0 )
+with expansion:
+  0 == 0
+
+FloatingPoint.tests.cpp:<line number>: PASSED:
+  CHECK( convertToBits( -0. ) == ( 1ULL << 63 ) )
+with expansion:
+  9223372036854775808 (0x<hex digits>)
+  ==
+  9223372036854775808 (0x<hex digits>)
+
+FloatingPoint.tests.cpp:<line number>: PASSED:
+  CHECK( convertToBits( std::numeric_limits<float>::denorm_min() ) == 1 )
+with expansion:
+  1 == 1
+
+FloatingPoint.tests.cpp:<line number>: PASSED:
+  CHECK( convertToBits( std::numeric_limits<double>::denorm_min() ) == 1 )
+with expansion:
+  1 == 1
+
+-------------------------------------------------------------------------------
+empty tags are not allowed
+-------------------------------------------------------------------------------
+Tag.tests.cpp:<line number>
+...............................................................................
+
+Tag.tests.cpp:<line number>: PASSED:
+  REQUIRE_THROWS( Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo) )
+
 -------------------------------------------------------------------------------
 erfc_inv
 -------------------------------------------------------------------------------
@@ -16048,6 +16197,22 @@ Generators.tests.cpp:<line number>: PASSED:
 with expansion:
   6 == 6
 
+-------------------------------------------------------------------------------
+tags with dots in later positions are not parsed as hidden
+-------------------------------------------------------------------------------
+Tag.tests.cpp:<line number>
+...............................................................................
+
+Tag.tests.cpp:<line number>: PASSED:
+  REQUIRE( testcase.tags.size() == 1 )
+with expansion:
+  1 == 1
+
+Tag.tests.cpp:<line number>: PASSED:
+  REQUIRE( testcase.tags[0].original == "magic.tag"_catch_sr )
+with expansion:
+  magic.tag == magic.tag
+
 -------------------------------------------------------------------------------
 thrown std::strings are translated
 -------------------------------------------------------------------------------
@@ -16768,6 +16933,6 @@ Misc.tests.cpp:<line number>
 Misc.tests.cpp:<line number>: PASSED:
 
 ===============================================================================
-test cases:  356 |  266 passed |  86 failed |  4 failed as expected
-assertions: 2105 | 1936 passed | 148 failed | 21 failed as expected
+test cases:  368 |  276 passed |  86 failed |  6 failed as expected
+assertions: 2120 | 1951 passed | 146 failed | 23 failed as expected
 
diff --git a/packages/Catch2/tests/SelfTest/Baselines/console.swa4.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/console.swa4.approved.txt
index ee504415a897eb84252b9621733d341d64ac32e3..df003bd3fc778c80d54b9fb07f58916df32a027e 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/console.swa4.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/console.swa4.approved.txt
@@ -744,6 +744,41 @@ Misc.tests.cpp:<line number>
 
 Misc.tests.cpp:<line number>: PASSED:
 
+-------------------------------------------------------------------------------
+#2152 - ULP checks between differently signed values were wrong - double
+-------------------------------------------------------------------------------
+Matchers.tests.cpp:<line number>
+...............................................................................
+
+Matchers.tests.cpp:<line number>: PASSED:
+  CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) )
+with expansion:
+  0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323,
+  4.9406564584124654e-324])
+
+Matchers.tests.cpp:<line number>: PASSED:
+  CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) )
+with expansion:
+  0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-
+  324, -0.0000000000000000e+00])
+
+-------------------------------------------------------------------------------
+#2152 - ULP checks between differently signed values were wrong - float
+-------------------------------------------------------------------------------
+Matchers.tests.cpp:<line number>
+...............................................................................
+
+Matchers.tests.cpp:<line number>: PASSED:
+  CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) )
+with expansion:
+  0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
+
+Matchers.tests.cpp:<line number>: PASSED:
+  CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) )
+with expansion:
+  0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.
+  00000000e+00])
+
 -------------------------------------------------------------------------------
 #748 - captures with unexpected exceptions
   outside assertions
@@ -924,6 +959,6 @@ Condition.tests.cpp:<line number>: FAILED:
   CHECK( true != true )
 
 ===============================================================================
-test cases: 31 | 26 passed | 3 failed | 2 failed as expected
-assertions: 99 | 92 passed | 4 failed | 3 failed as expected
+test cases:  33 | 28 passed | 3 failed | 2 failed as expected
+assertions: 103 | 96 passed | 4 failed | 3 failed as expected
 
diff --git a/packages/Catch2/tests/SelfTest/Baselines/junit.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/junit.sw.approved.txt
index 5206ed2d18e12081a39a7ddc24e037407d48bcdd..88e96b0fcf64e556af68cca1df002ade68794c7b 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/junit.sw.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/junit.sw.approved.txt
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <testsuitesloose text artifact
 >
-  <testsuite name="<exe-name>" errors="17" failures="132" tests="2106" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
+  <testsuite name="<exe-name>" errors="17" failures="129" tests="2120" hostname="tbd" time="{duration}" timestamp="{iso8601-timestamp}">
     <properties>
       <property name="filters" value="~[!nonportable]~[!benchmark]~[approvals] *"/>
       <property name="random-seed" value="1"/>
@@ -47,6 +47,8 @@ Nor would this
     <testcase classname="<exe-name>.global" name="#1954 - 7 arg template test case sig compiles - 1, 1, 1, 1, 1, 0, 0" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="#1954 - 7 arg template test case sig compiles - 5, 1, 1, 1, 1, 0, 0" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="#2152 - ULP checks between differently signed values were wrong - double" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="#2152 - ULP checks between differently signed values were wrong - float" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="#748 - captures with unexpected exceptions/outside assertions" time="{duration}" status="run">
       <error type="TEST_CASE">
 FAILED:
@@ -396,17 +398,17 @@ Exception.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="Composed matchers shortcircuit/MatchAllOf" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Composed matchers shortcircuit/MatchAnyOf" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Contains string matcher" time="{duration}" status="run">
-      <failure message="testStringForMatching(), Contains(&quot;not there&quot;, Catch::CaseSensitive::No)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), Contains( &quot;not there&quot;, Catch::CaseSensitive::No )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), Contains("not there", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), Contains( "not there", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "not there" (case
   insensitive)
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="testStringForMatching(), Contains(&quot;STRING&quot;)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), Contains( &quot;STRING&quot; )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), Contains("STRING") )
+  CHECK_THAT( testStringForMatching(), Contains( "STRING" ) )
 with expansion:
   "this string contains 'abc' as a substring" contains: "STRING"
 Matchers.tests.cpp:<line number>
@@ -441,16 +443,16 @@ Exception.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="Default scale is invisible to comparison" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Directly creating an EnumInfo" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="EndsWith string matcher" time="{duration}" status="run">
-      <failure message="testStringForMatching(), EndsWith(&quot;Substring&quot;)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), EndsWith( &quot;Substring&quot; )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), EndsWith("Substring") )
+  CHECK_THAT( testStringForMatching(), EndsWith( "Substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" ends with: "Substring"
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="testStringForMatching(), EndsWith(&quot;this&quot;, Catch::CaseSensitive::No)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), EndsWith( &quot;this&quot;, Catch::CaseSensitive::No )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" ends with: "this" (case
   insensitive)
@@ -556,17 +558,17 @@ Condition.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="Equality checks that should succeed" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Equals" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Equals string matcher" time="{duration}" status="run">
-      <failure message="testStringForMatching(), Equals(&quot;this string contains 'ABC' as a substring&quot;)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), Equals( &quot;this string contains 'ABC' as a substring&quot; )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), Equals("this string contains 'ABC' as a substring") )
+  CHECK_THAT( testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" equals: "this string contains
   'ABC' as a substring"
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="testStringForMatching(), Equals(&quot;something else&quot;, Catch::CaseSensitive::No)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), Equals( &quot;something else&quot;, Catch::CaseSensitive::No )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), Equals("something else", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" equals: "something else" (case
   insensitive)
@@ -575,42 +577,42 @@ Matchers.tests.cpp:<line number>
     </testcase>
     <testcase classname="<exe-name>.global" name="Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Exception matchers that fail/No exception" time="{duration}" status="run">
-      <failure message="doesNotThrow(), SpecialException, ExceptionMatcher{1}" type="CHECK_THROWS_MATCHES">
+      <failure message="doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }" type="CHECK_THROWS_MATCHES">
 FAILED:
-  CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="doesNotThrow(), SpecialException, ExceptionMatcher{1}" type="REQUIRE_THROWS_MATCHES">
+      <failure message="doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }" type="REQUIRE_THROWS_MATCHES">
 FAILED:
-  REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )
+  REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )
 Matchers.tests.cpp:<line number>
       </failure>
     </testcase>
     <testcase classname="<exe-name>.global" name="Exception matchers that fail/Type mismatch" time="{duration}" status="run">
-      <error message="throwsAsInt(1), SpecialException, ExceptionMatcher{1}" type="CHECK_THROWS_MATCHES">
+      <error message="throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }" type="CHECK_THROWS_MATCHES">
 FAILED:
-  CHECK_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )
 Unknown exception
 Matchers.tests.cpp:<line number>
       </error>
-      <error message="throwsAsInt(1), SpecialException, ExceptionMatcher{1}" type="REQUIRE_THROWS_MATCHES">
+      <error message="throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }" type="REQUIRE_THROWS_MATCHES">
 FAILED:
-  REQUIRE_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )
+  REQUIRE_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )
 Unknown exception
 Matchers.tests.cpp:<line number>
       </error>
     </testcase>
     <testcase classname="<exe-name>.global" name="Exception matchers that fail/Contents are wrong" time="{duration}" status="run">
-      <failure message="throwsSpecialException(3), SpecialException, ExceptionMatcher{1}" type="CHECK_THROWS_MATCHES">
+      <failure message="throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 }" type="CHECK_THROWS_MATCHES">
 FAILED:
-  CHECK_THROWS_MATCHES( throwsSpecialException(3), SpecialException, ExceptionMatcher{1} )
+  CHECK_THROWS_MATCHES( throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 } )
 with expansion:
   SpecialException::what special exception has value of 1
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="throwsSpecialException(4), SpecialException, ExceptionMatcher{1}" type="REQUIRE_THROWS_MATCHES">
+      <failure message="throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 }" type="REQUIRE_THROWS_MATCHES">
 FAILED:
-  REQUIRE_THROWS_MATCHES( throwsSpecialException(4), SpecialException, ExceptionMatcher{1} )
+  REQUIRE_THROWS_MATCHES( throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 } )
 with expansion:
   SpecialException::what special exception has value of 1
 Matchers.tests.cpp:<line number>
@@ -798,9 +800,9 @@ Condition.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="Matchers can be (AnyOf) composed with the || operator" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Matchers can be composed with both &amp;&amp; and ||" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Matchers can be composed with both &amp;&amp; and || - failing" time="{duration}" status="run">
-      <failure message="testStringForMatching(), (Contains(&quot;string&quot;) || Contains(&quot;different&quot;)) &amp;&amp; Contains(&quot;random&quot;)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), ( Contains( &quot;string&quot; ) || Contains( &quot;different&quot; ) ) &amp;&amp; Contains( &quot;random&quot; )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), (Contains("string") || Contains("different")) &amp;&amp; Contains("random") )
+  CHECK_THAT( testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) &amp;&amp; Contains( "random" ) )
 with expansion:
   "this string contains 'abc' as a substring" ( ( contains: "string" or
   contains: "different" ) and contains: "random" )
@@ -809,9 +811,9 @@ Matchers.tests.cpp:<line number>
     </testcase>
     <testcase classname="<exe-name>.global" name="Matchers can be negated (Not) with the ! operator" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Matchers can be negated (Not) with the ! operator - failing" time="{duration}" status="run">
-      <failure message="testStringForMatching(), !Contains(&quot;substring&quot;)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), !Contains( &quot;substring&quot; )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), !Contains("substring") )
+  CHECK_THAT( testStringForMatching(), !Contains( "substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" not contains: "substring"
 Matchers.tests.cpp:<line number>
@@ -1075,25 +1077,25 @@ Decomposition.tests.cpp:<line number>
       </failure>
     </testcase>
     <testcase classname="<exe-name>.global" name="Regex string matcher" time="{duration}" status="run">
-      <failure message="testStringForMatching(), Matches(&quot;this STRING contains 'abc' as a substring&quot;)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), Matches( &quot;this STRING contains 'abc' as a substring&quot; )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), Matches("this STRING contains 'abc' as a substring") )
+  CHECK_THAT( testStringForMatching(), Matches( "this STRING contains 'abc' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" matches "this STRING contains
   'abc' as a substring" case sensitively
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="testStringForMatching(), Matches(&quot;contains 'abc' as a substring&quot;)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), Matches( &quot;contains 'abc' as a substring&quot; )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), Matches("contains 'abc' as a substring") )
+  CHECK_THAT( testStringForMatching(), Matches( "contains 'abc' as a substring" ) )
 with expansion:
   "this string contains 'abc' as a substring" matches "contains 'abc' as a
   substring" case sensitively
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="testStringForMatching(), Matches(&quot;this string contains 'abc' as a&quot;)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), Matches( &quot;this string contains 'abc' as a&quot; )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), Matches("this string contains 'abc' as a") )
+  CHECK_THAT( testStringForMatching(), Matches( "this string contains 'abc' as a" ) )
 with expansion:
   "this string contains 'abc' as a substring" matches "this string contains
   'abc' as a" case sensitively
@@ -1154,16 +1156,16 @@ Message from section two
       </system-out>
     </testcase>
     <testcase classname="<exe-name>.global" name="StartsWith string matcher" time="{duration}" status="run">
-      <failure message="testStringForMatching(), StartsWith(&quot;This String&quot;)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), StartsWith( &quot;This String&quot; )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), StartsWith("This String") )
+  CHECK_THAT( testStringForMatching(), StartsWith( "This String" ) )
 with expansion:
   "this string contains 'abc' as a substring" starts with: "This String"
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="testStringForMatching(), StartsWith(&quot;string&quot;, Catch::CaseSensitive::No)" type="CHECK_THAT">
+      <failure message="testStringForMatching(), StartsWith( &quot;string&quot;, Catch::CaseSensitive::No )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No) )
+  CHECK_THAT( testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No ) )
 with expansion:
   "this string contains 'abc' as a substring" starts with: "string" (case
   insensitive)
@@ -1194,6 +1196,9 @@ Matchers.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="StringRef/StringRef + StringRef" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="StringRef at compilation time/Simple constructors" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="StringRef at compilation time/UDL construction" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="Stringifying char arrays with statically known sizes - char" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="Stringifying char arrays with statically known sizes - signed char" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="Stringifying char arrays with statically known sizes - unsigned char" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Stringifying std::chrono::duration helpers" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Stringifying std::chrono::duration with weird ratios" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Stringifying std::chrono::time_point&lt;system_clock>" time="{duration}" status="run"/>
@@ -1276,6 +1281,19 @@ Misc.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="Test case with one argument" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Test enum bit values" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Test with special, characters &quot;in name" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="Testing checked-if" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="Testing checked-if 2" time="{duration}" status="run">
+      <failure type="FAIL">
+FAILED:
+Misc.tests.cpp:<line number>
+      </failure>
+    </testcase>
+    <testcase classname="<exe-name>.global" name="Testing checked-if 3" time="{duration}" status="run">
+      <failure type="FAIL">
+FAILED:
+Misc.tests.cpp:<line number>
+      </failure>
+    </testcase>
     <testcase classname="<exe-name>.global" name="The NO_FAIL macro reports a failure but does not fail the test" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="The default listing implementation write to provided stream/Listing tags" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="The default listing implementation write to provided stream/Listing reporters" time="{duration}" status="run"/>
@@ -1330,18 +1348,18 @@ Exception.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="Vector Approx matcher/Vectors with elements/Different length" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Vector Approx matcher/Vectors with elements/Same length, different elements" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Vector Approx matcher -- failing/Empty and non empty vectors are not approx equal" time="{duration}" status="run">
-      <failure message="empty, Approx(t1)" type="CHECK_THAT">
+      <failure message="empty, Approx( t1 )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( empty, Approx(t1) )
+  CHECK_THAT( empty, Approx( t1 ) )
 with expansion:
   {  } is approx: { 1.0, 2.0 }
 Matchers.tests.cpp:<line number>
       </failure>
     </testcase>
     <testcase classname="<exe-name>.global" name="Vector Approx matcher -- failing/Just different vectors" time="{duration}" status="run">
-      <failure message="v1, Approx(v2)" type="CHECK_THAT">
+      <failure message="v1, Approx( v2 )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( v1, Approx(v2) )
+  CHECK_THAT( v1, Approx( v2 ) )
 with expansion:
   { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
 Matchers.tests.cpp:<line number>
@@ -1353,92 +1371,92 @@ Matchers.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="Vector matchers/Equals" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Vector matchers/UnorderedEquals" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="Vector matchers that fail/Contains (element)" time="{duration}" status="run">
-      <failure message="v, VectorContains(-1)" type="CHECK_THAT">
+      <failure message="v, VectorContains( -1 )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( v, VectorContains(-1) )
+  CHECK_THAT( v, VectorContains( -1 ) )
 with expansion:
   { 1, 2, 3 } Contains: -1
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="empty, VectorContains(1)" type="CHECK_THAT">
+      <failure message="empty, VectorContains( 1 )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( empty, VectorContains(1) )
+  CHECK_THAT( empty, VectorContains( 1 ) )
 with expansion:
   {  } Contains: 1
 Matchers.tests.cpp:<line number>
       </failure>
     </testcase>
     <testcase classname="<exe-name>.global" name="Vector matchers that fail/Contains (vector)" time="{duration}" status="run">
-      <failure message="empty, Contains(v)" type="CHECK_THAT">
+      <failure message="empty, Contains( v )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( empty, Contains(v) )
+  CHECK_THAT( empty, Contains( v ) )
 with expansion:
   {  } Contains: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="v, Contains(v2)" type="CHECK_THAT">
+      <failure message="v, Contains( v2 )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( v, Contains(v2) )
+  CHECK_THAT( v, Contains( v2 ) )
 with expansion:
   { 1, 2, 3 } Contains: { 1, 2, 4 }
 Matchers.tests.cpp:<line number>
       </failure>
     </testcase>
     <testcase classname="<exe-name>.global" name="Vector matchers that fail/Equals" time="{duration}" status="run">
-      <failure message="v, Equals(v2)" type="CHECK_THAT">
+      <failure message="v, Equals( v2 )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( v, Equals(v2) )
+  CHECK_THAT( v, Equals( v2 ) )
 with expansion:
   { 1, 2, 3 } Equals: { 1, 2 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="v2, Equals(v)" type="CHECK_THAT">
+      <failure message="v2, Equals( v )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( v2, Equals(v) )
+  CHECK_THAT( v2, Equals( v ) )
 with expansion:
   { 1, 2 } Equals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="empty, Equals(v)" type="CHECK_THAT">
+      <failure message="empty, Equals( v )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( empty, Equals(v) )
+  CHECK_THAT( empty, Equals( v ) )
 with expansion:
   {  } Equals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="v, Equals(empty)" type="CHECK_THAT">
+      <failure message="v, Equals( empty )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( v, Equals(empty) )
+  CHECK_THAT( v, Equals( empty ) )
 with expansion:
   { 1, 2, 3 } Equals: {  }
 Matchers.tests.cpp:<line number>
       </failure>
     </testcase>
     <testcase classname="<exe-name>.global" name="Vector matchers that fail/UnorderedEquals" time="{duration}" status="run">
-      <failure message="v, UnorderedEquals(empty)" type="CHECK_THAT">
+      <failure message="v, UnorderedEquals( empty )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( v, UnorderedEquals(empty) )
+  CHECK_THAT( v, UnorderedEquals( empty ) )
 with expansion:
   { 1, 2, 3 } UnorderedEquals: {  }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="empty, UnorderedEquals(v)" type="CHECK_THAT">
+      <failure message="empty, UnorderedEquals( v )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( empty, UnorderedEquals(v) )
+  CHECK_THAT( empty, UnorderedEquals( v ) )
 with expansion:
   {  } UnorderedEquals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="permuted, UnorderedEquals(v)" type="CHECK_THAT">
+      <failure message="permuted, UnorderedEquals( v )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( permuted, UnorderedEquals(v) )
+  CHECK_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
   { 1, 3 } UnorderedEquals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="permuted, UnorderedEquals(v)" type="CHECK_THAT">
+      <failure message="permuted, UnorderedEquals( v )" type="CHECK_THAT">
 FAILED:
-  CHECK_THAT( permuted, UnorderedEquals(v) )
+  CHECK_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
   { 3, 1 } UnorderedEquals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
@@ -1497,6 +1515,7 @@ Exception.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="XmlEncode/string with quotes" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="XmlEncode/string with control char (1)" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="XmlEncode/string with control char (x7F)" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="XmlWriter writes boolean attributes as true/false" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="analyse no analysis" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="array&lt;int, N> -> toString" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="atomic if" time="{duration}" status="run"/>
@@ -1505,13 +1524,6 @@ Exception.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="boolean member" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="checkedElse" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="checkedElse, failing" time="{duration}" status="run">
-      <failure message="flag" type="CHECKED_ELSE">
-FAILED:
-  CHECKED_ELSE( flag )
-with expansion:
-  false
-Misc.tests.cpp:<line number>
-      </failure>
       <failure message="testCheckedElse( false )" type="REQUIRE">
 FAILED:
   REQUIRE( testCheckedElse( false ) )
@@ -1522,13 +1534,6 @@ Misc.tests.cpp:<line number>
     </testcase>
     <testcase classname="<exe-name>.global" name="checkedIf" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="checkedIf, failing" time="{duration}" status="run">
-      <failure message="flag" type="CHECKED_IF">
-FAILED:
-  CHECKED_IF( flag )
-with expansion:
-  false
-Misc.tests.cpp:<line number>
-      </failure>
       <failure message="testCheckedIf( false )" type="REQUIRE">
 FAILED:
   REQUIRE( testCheckedIf( false ) )
@@ -1545,6 +1550,8 @@ Misc.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="classify_outliers/mixed" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="comparisons between const int variables" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="comparisons between int variables" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="convertToBits" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="empty tags are not allowed" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="erfc_inv" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="estimate_clock_resolution" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="even more nested SECTION tests/c/d (leaf)" time="{duration}" status="run"/>
@@ -1778,6 +1785,7 @@ Tricky.tests.cpp:<line number>
     <testcase classname="<exe-name>.global" name="stringify( vectors&lt;has_operator> )" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="strlen3" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="tables" time="{duration}" status="run"/>
+    <testcase classname="<exe-name>.global" name="tags with dots in later positions are not parsed as hidden" time="{duration}" status="run"/>
     <testcase classname="<exe-name>.global" name="thrown std::strings are translated" time="{duration}" status="run">
       <error type="TEST_CASE">
 FAILED:
diff --git a/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt
index 1f0cc5dad55d613eaec25b9c634eab82a854ef73..924a14b19c3ec7e2a7f70e94c6f47483e0a3ae6f 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/sonarqube.sw.approved.txt
@@ -77,6 +77,9 @@
     <testCase name="Process can be configured on command line/Benchmark options/warmup-time" duration="{duration}"/>
     <testCase name="Test with special, characters &quot;in name" duration="{duration}"/>
   </file>
+  <file path="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp">
+    <testCase name="convertToBits" duration="{duration}"/>
+  </file>
   <file path="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp">
     <testCase name="Generators internals/Single value" duration="{duration}"/>
     <testCase name="Generators internals/Preset values" duration="{duration}"/>
@@ -216,11 +219,16 @@
   <file path="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp">
     <testCase name="Tag alias can be registered against tag patterns/The same tag alias can only be registered once" duration="{duration}"/>
     <testCase name="Tag alias can be registered against tag patterns/Tag aliases must be of the form [@name]" duration="{duration}"/>
+    <testCase name="empty tags are not allowed" duration="{duration}"/>
     <testCase name="shortened hide tags are split apart" duration="{duration}"/>
+    <testCase name="tags with dots in later positions are not parsed as hidden" duration="{duration}"/>
   </file>
   <file path="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp">
     <testCase name="Directly creating an EnumInfo" duration="{duration}"/>
     <testCase name="Range type with sentinel" duration="{duration}"/>
+    <testCase name="Stringifying char arrays with statically known sizes - char" duration="{duration}"/>
+    <testCase name="Stringifying char arrays with statically known sizes - signed char" duration="{duration}"/>
+    <testCase name="Stringifying char arrays with statically known sizes - unsigned char" duration="{duration}"/>
     <testCase name="parseEnums/No enums" duration="{duration}"/>
     <testCase name="parseEnums/One enum value" duration="{duration}"/>
     <testCase name="parseEnums/Multiple enum values" duration="{duration}"/>
@@ -249,6 +257,7 @@
     <testCase name="XmlEncode/string with quotes" duration="{duration}"/>
     <testCase name="XmlEncode/string with control char (1)" duration="{duration}"/>
     <testCase name="XmlEncode/string with control char (x7F)" duration="{duration}"/>
+    <testCase name="XmlWriter writes boolean attributes as true/false" duration="{duration}"/>
   </file>
   <file path="tests/<exe-name>/UsageTests/Approx.tests.cpp">
     <testCase name="A comparison that uses literals instead of the normal constructor" duration="{duration}"/>
@@ -982,6 +991,8 @@ Exception.tests.cpp:<line number>
     <testCase name="tables" duration="{duration}"/>
   </file>
   <file path="tests/<exe-name>/UsageTests/Matchers.tests.cpp">
+    <testCase name="#2152 - ULP checks between differently signed values were wrong - double" duration="{duration}"/>
+    <testCase name="#2152 - ULP checks between differently signed values were wrong - float" duration="{duration}"/>
     <testCase name="Arbitrary predicate matcher/Function pointer" duration="{duration}"/>
     <testCase name="Arbitrary predicate matcher/Lambdas + different type" duration="{duration}"/>
     <testCase name="Combining MatchAllOfGeneric does not nest" duration="{duration}"/>
@@ -996,32 +1007,32 @@ Exception.tests.cpp:<line number>
     <testCase name="Composed matchers shortcircuit/MatchAllOf" duration="{duration}"/>
     <testCase name="Composed matchers shortcircuit/MatchAnyOf" duration="{duration}"/>
     <testCase name="Contains string matcher" duration="{duration}">
-      <failure message="CHECK_THAT(testStringForMatching(), Contains(&quot;not there&quot;, Catch::CaseSensitive::No))">
+      <failure message="CHECK_THAT(testStringForMatching(), Contains( &quot;not there&quot;, Catch::CaseSensitive::No ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), Contains("not there", Catch::CaseSensitive::No) )
+	CHECK_THAT( testStringForMatching(), Contains( "not there", Catch::CaseSensitive::No ) )
 with expansion:
 	"this string contains 'abc' as a substring" contains: "not there" (case insensitive)
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(testStringForMatching(), Contains(&quot;STRING&quot;))">
+      <failure message="CHECK_THAT(testStringForMatching(), Contains( &quot;STRING&quot; ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), Contains("STRING") )
+	CHECK_THAT( testStringForMatching(), Contains( "STRING" ) )
 with expansion:
 	"this string contains 'abc' as a substring" contains: "STRING"
 Matchers.tests.cpp:<line number>
       </failure>
     </testCase>
     <testCase name="EndsWith string matcher" duration="{duration}">
-      <failure message="CHECK_THAT(testStringForMatching(), EndsWith(&quot;Substring&quot;))">
+      <failure message="CHECK_THAT(testStringForMatching(), EndsWith( &quot;Substring&quot; ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), EndsWith("Substring") )
+	CHECK_THAT( testStringForMatching(), EndsWith( "Substring" ) )
 with expansion:
 	"this string contains 'abc' as a substring" ends with: "Substring"
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(testStringForMatching(), EndsWith(&quot;this&quot;, Catch::CaseSensitive::No))">
+      <failure message="CHECK_THAT(testStringForMatching(), EndsWith( &quot;this&quot;, Catch::CaseSensitive::No ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No) )
+	CHECK_THAT( testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) )
 with expansion:
 	"this string contains 'abc' as a substring" ends with: "this" (case insensitive)
 Matchers.tests.cpp:<line number>
@@ -1029,58 +1040,58 @@ Matchers.tests.cpp:<line number>
     </testCase>
     <testCase name="Equals" duration="{duration}"/>
     <testCase name="Equals string matcher" duration="{duration}">
-      <failure message="CHECK_THAT(testStringForMatching(), Equals(&quot;this string contains 'ABC' as a substring&quot;))">
+      <failure message="CHECK_THAT(testStringForMatching(), Equals( &quot;this string contains 'ABC' as a substring&quot; ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), Equals("this string contains 'ABC' as a substring") )
+	CHECK_THAT( testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) )
 with expansion:
 	"this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(testStringForMatching(), Equals(&quot;something else&quot;, Catch::CaseSensitive::No))">
+      <failure message="CHECK_THAT(testStringForMatching(), Equals( &quot;something else&quot;, Catch::CaseSensitive::No ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), Equals("something else", Catch::CaseSensitive::No) )
+	CHECK_THAT( testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) )
 with expansion:
 	"this string contains 'abc' as a substring" equals: "something else" (case insensitive)
 Matchers.tests.cpp:<line number>
       </failure>
     </testCase>
     <testCase name="Exception matchers that fail/No exception" duration="{duration}">
-      <failure message="CHECK_THROWS_MATCHES(doesNotThrow(), SpecialException, ExceptionMatcher{1})">
+      <failure message="CHECK_THROWS_MATCHES(doesNotThrow(), SpecialException, ExceptionMatcher{ 1 })">
 FAILED:
-	CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )
+	CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="REQUIRE_THROWS_MATCHES(doesNotThrow(), SpecialException, ExceptionMatcher{1})">
+      <failure message="REQUIRE_THROWS_MATCHES(doesNotThrow(), SpecialException, ExceptionMatcher{ 1 })">
 FAILED:
-	REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )
+	REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )
 Matchers.tests.cpp:<line number>
       </failure>
     </testCase>
     <testCase name="Exception matchers that fail/Type mismatch" duration="{duration}">
-      <error message="CHECK_THROWS_MATCHES(throwsAsInt(1), SpecialException, ExceptionMatcher{1})">
+      <error message="CHECK_THROWS_MATCHES(throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 })">
 FAILED:
-	CHECK_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )
+	CHECK_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )
 Unknown exception
 Matchers.tests.cpp:<line number>
       </error>
-      <error message="REQUIRE_THROWS_MATCHES(throwsAsInt(1), SpecialException, ExceptionMatcher{1})">
+      <error message="REQUIRE_THROWS_MATCHES(throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 })">
 FAILED:
-	REQUIRE_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )
+	REQUIRE_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )
 Unknown exception
 Matchers.tests.cpp:<line number>
       </error>
     </testCase>
     <testCase name="Exception matchers that fail/Contents are wrong" duration="{duration}">
-      <failure message="CHECK_THROWS_MATCHES(throwsSpecialException(3), SpecialException, ExceptionMatcher{1})">
+      <failure message="CHECK_THROWS_MATCHES(throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 })">
 FAILED:
-	CHECK_THROWS_MATCHES( throwsSpecialException(3), SpecialException, ExceptionMatcher{1} )
+	CHECK_THROWS_MATCHES( throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 } )
 with expansion:
 	SpecialException::what special exception has value of 1
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="REQUIRE_THROWS_MATCHES(throwsSpecialException(4), SpecialException, ExceptionMatcher{1})">
+      <failure message="REQUIRE_THROWS_MATCHES(throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 })">
 FAILED:
-	REQUIRE_THROWS_MATCHES( throwsSpecialException(4), SpecialException, ExceptionMatcher{1} )
+	REQUIRE_THROWS_MATCHES( throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 } )
 with expansion:
 	SpecialException::what special exception has value of 1
 Matchers.tests.cpp:<line number>
@@ -1104,9 +1115,9 @@ Matchers.tests.cpp:<line number>
     <testCase name="Matchers can be (AnyOf) composed with the || operator" duration="{duration}"/>
     <testCase name="Matchers can be composed with both &amp;&amp; and ||" duration="{duration}"/>
     <testCase name="Matchers can be composed with both &amp;&amp; and || - failing" duration="{duration}">
-      <failure message="CHECK_THAT(testStringForMatching(), (Contains(&quot;string&quot;) || Contains(&quot;different&quot;)) &amp;&amp; Contains(&quot;random&quot;))">
+      <failure message="CHECK_THAT(testStringForMatching(), ( Contains( &quot;string&quot; ) || Contains( &quot;different&quot; ) ) &amp;&amp; Contains( &quot;random&quot; ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), (Contains("string") || Contains("different")) &amp;&amp; Contains("random") )
+	CHECK_THAT( testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) &amp;&amp; Contains( "random" ) )
 with expansion:
 	"this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )
 Matchers.tests.cpp:<line number>
@@ -1114,9 +1125,9 @@ Matchers.tests.cpp:<line number>
     </testCase>
     <testCase name="Matchers can be negated (Not) with the ! operator" duration="{duration}"/>
     <testCase name="Matchers can be negated (Not) with the ! operator - failing" duration="{duration}">
-      <failure message="CHECK_THAT(testStringForMatching(), !Contains(&quot;substring&quot;))">
+      <failure message="CHECK_THAT(testStringForMatching(), !Contains( &quot;substring&quot; ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), !Contains("substring") )
+	CHECK_THAT( testStringForMatching(), !Contains( "substring" ) )
 with expansion:
 	"this string contains 'abc' as a substring" not contains: "substring"
 Matchers.tests.cpp:<line number>
@@ -1125,23 +1136,23 @@ Matchers.tests.cpp:<line number>
     <testCase name="Overloaded comma or address-of operators are not used" duration="{duration}"/>
     <testCase name="Predicate matcher can accept const char*" duration="{duration}"/>
     <testCase name="Regex string matcher" duration="{duration}">
-      <failure message="CHECK_THAT(testStringForMatching(), Matches(&quot;this STRING contains 'abc' as a substring&quot;))">
+      <failure message="CHECK_THAT(testStringForMatching(), Matches( &quot;this STRING contains 'abc' as a substring&quot; ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), Matches("this STRING contains 'abc' as a substring") )
+	CHECK_THAT( testStringForMatching(), Matches( "this STRING contains 'abc' as a substring" ) )
 with expansion:
 	"this string contains 'abc' as a substring" matches "this STRING contains 'abc' as a substring" case sensitively
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(testStringForMatching(), Matches(&quot;contains 'abc' as a substring&quot;))">
+      <failure message="CHECK_THAT(testStringForMatching(), Matches( &quot;contains 'abc' as a substring&quot; ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), Matches("contains 'abc' as a substring") )
+	CHECK_THAT( testStringForMatching(), Matches( "contains 'abc' as a substring" ) )
 with expansion:
 	"this string contains 'abc' as a substring" matches "contains 'abc' as a substring" case sensitively
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(testStringForMatching(), Matches(&quot;this string contains 'abc' as a&quot;))">
+      <failure message="CHECK_THAT(testStringForMatching(), Matches( &quot;this string contains 'abc' as a&quot; ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), Matches("this string contains 'abc' as a") )
+	CHECK_THAT( testStringForMatching(), Matches( "this string contains 'abc' as a" ) )
 with expansion:
 	"this string contains 'abc' as a substring" matches "this string contains 'abc' as a" case sensitively
 Matchers.tests.cpp:<line number>
@@ -1149,16 +1160,16 @@ Matchers.tests.cpp:<line number>
     </testCase>
     <testCase name="Regression test #1" duration="{duration}"/>
     <testCase name="StartsWith string matcher" duration="{duration}">
-      <failure message="CHECK_THAT(testStringForMatching(), StartsWith(&quot;This String&quot;))">
+      <failure message="CHECK_THAT(testStringForMatching(), StartsWith( &quot;This String&quot; ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), StartsWith("This String") )
+	CHECK_THAT( testStringForMatching(), StartsWith( "This String" ) )
 with expansion:
 	"this string contains 'abc' as a substring" starts with: "This String"
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(testStringForMatching(), StartsWith(&quot;string&quot;, Catch::CaseSensitive::No))">
+      <failure message="CHECK_THAT(testStringForMatching(), StartsWith( &quot;string&quot;, Catch::CaseSensitive::No ))">
 FAILED:
-	CHECK_THAT( testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No) )
+	CHECK_THAT( testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No ) )
 with expansion:
 	"this string contains 'abc' as a substring" starts with: "string" (case insensitive)
 Matchers.tests.cpp:<line number>
@@ -1170,18 +1181,18 @@ Matchers.tests.cpp:<line number>
     <testCase name="Vector Approx matcher/Vectors with elements/Different length" duration="{duration}"/>
     <testCase name="Vector Approx matcher/Vectors with elements/Same length, different elements" duration="{duration}"/>
     <testCase name="Vector Approx matcher -- failing/Empty and non empty vectors are not approx equal" duration="{duration}">
-      <failure message="CHECK_THAT(empty, Approx(t1))">
+      <failure message="CHECK_THAT(empty, Approx( t1 ))">
 FAILED:
-	CHECK_THAT( empty, Approx(t1) )
+	CHECK_THAT( empty, Approx( t1 ) )
 with expansion:
 	{  } is approx: { 1.0, 2.0 }
 Matchers.tests.cpp:<line number>
       </failure>
     </testCase>
     <testCase name="Vector Approx matcher -- failing/Just different vectors" duration="{duration}">
-      <failure message="CHECK_THAT(v1, Approx(v2))">
+      <failure message="CHECK_THAT(v1, Approx( v2 ))">
 FAILED:
-	CHECK_THAT( v1, Approx(v2) )
+	CHECK_THAT( v1, Approx( v2 ) )
 with expansion:
 	{ 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
 Matchers.tests.cpp:<line number>
@@ -1193,92 +1204,92 @@ Matchers.tests.cpp:<line number>
     <testCase name="Vector matchers/Equals" duration="{duration}"/>
     <testCase name="Vector matchers/UnorderedEquals" duration="{duration}"/>
     <testCase name="Vector matchers that fail/Contains (element)" duration="{duration}">
-      <failure message="CHECK_THAT(v, VectorContains(-1))">
+      <failure message="CHECK_THAT(v, VectorContains( -1 ))">
 FAILED:
-	CHECK_THAT( v, VectorContains(-1) )
+	CHECK_THAT( v, VectorContains( -1 ) )
 with expansion:
 	{ 1, 2, 3 } Contains: -1
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(empty, VectorContains(1))">
+      <failure message="CHECK_THAT(empty, VectorContains( 1 ))">
 FAILED:
-	CHECK_THAT( empty, VectorContains(1) )
+	CHECK_THAT( empty, VectorContains( 1 ) )
 with expansion:
 	{  } Contains: 1
 Matchers.tests.cpp:<line number>
       </failure>
     </testCase>
     <testCase name="Vector matchers that fail/Contains (vector)" duration="{duration}">
-      <failure message="CHECK_THAT(empty, Contains(v))">
+      <failure message="CHECK_THAT(empty, Contains( v ))">
 FAILED:
-	CHECK_THAT( empty, Contains(v) )
+	CHECK_THAT( empty, Contains( v ) )
 with expansion:
 	{  } Contains: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(v, Contains(v2))">
+      <failure message="CHECK_THAT(v, Contains( v2 ))">
 FAILED:
-	CHECK_THAT( v, Contains(v2) )
+	CHECK_THAT( v, Contains( v2 ) )
 with expansion:
 	{ 1, 2, 3 } Contains: { 1, 2, 4 }
 Matchers.tests.cpp:<line number>
       </failure>
     </testCase>
     <testCase name="Vector matchers that fail/Equals" duration="{duration}">
-      <failure message="CHECK_THAT(v, Equals(v2))">
+      <failure message="CHECK_THAT(v, Equals( v2 ))">
 FAILED:
-	CHECK_THAT( v, Equals(v2) )
+	CHECK_THAT( v, Equals( v2 ) )
 with expansion:
 	{ 1, 2, 3 } Equals: { 1, 2 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(v2, Equals(v))">
+      <failure message="CHECK_THAT(v2, Equals( v ))">
 FAILED:
-	CHECK_THAT( v2, Equals(v) )
+	CHECK_THAT( v2, Equals( v ) )
 with expansion:
 	{ 1, 2 } Equals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(empty, Equals(v))">
+      <failure message="CHECK_THAT(empty, Equals( v ))">
 FAILED:
-	CHECK_THAT( empty, Equals(v) )
+	CHECK_THAT( empty, Equals( v ) )
 with expansion:
 	{  } Equals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(v, Equals(empty))">
+      <failure message="CHECK_THAT(v, Equals( empty ))">
 FAILED:
-	CHECK_THAT( v, Equals(empty) )
+	CHECK_THAT( v, Equals( empty ) )
 with expansion:
 	{ 1, 2, 3 } Equals: {  }
 Matchers.tests.cpp:<line number>
       </failure>
     </testCase>
     <testCase name="Vector matchers that fail/UnorderedEquals" duration="{duration}">
-      <failure message="CHECK_THAT(v, UnorderedEquals(empty))">
+      <failure message="CHECK_THAT(v, UnorderedEquals( empty ))">
 FAILED:
-	CHECK_THAT( v, UnorderedEquals(empty) )
+	CHECK_THAT( v, UnorderedEquals( empty ) )
 with expansion:
 	{ 1, 2, 3 } UnorderedEquals: {  }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(empty, UnorderedEquals(v))">
+      <failure message="CHECK_THAT(empty, UnorderedEquals( v ))">
 FAILED:
-	CHECK_THAT( empty, UnorderedEquals(v) )
+	CHECK_THAT( empty, UnorderedEquals( v ) )
 with expansion:
 	{  } UnorderedEquals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(permuted, UnorderedEquals(v))">
+      <failure message="CHECK_THAT(permuted, UnorderedEquals( v ))">
 FAILED:
-	CHECK_THAT( permuted, UnorderedEquals(v) )
+	CHECK_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
 	{ 1, 3 } UnorderedEquals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
       </failure>
-      <failure message="CHECK_THAT(permuted, UnorderedEquals(v))">
+      <failure message="CHECK_THAT(permuted, UnorderedEquals( v ))">
 FAILED:
-	CHECK_THAT( permuted, UnorderedEquals(v) )
+	CHECK_THAT( permuted, UnorderedEquals( v ) )
 with expansion:
 	{ 3, 1 } UnorderedEquals: { 1, 2, 3 }
 Matchers.tests.cpp:<line number>
@@ -1583,17 +1594,23 @@ Misc.tests.cpp:<line number>
     <testCase name="TemplateTestSig: vectors can be sized and resized - std::string,15/resizing smaller changes size but not capacity/We can use the 'swap trick' to reset the capacity" duration="{duration}"/>
     <testCase name="TemplateTestSig: vectors can be sized and resized - std::string,15/reserving bigger changes capacity but not size" duration="{duration}"/>
     <testCase name="TemplateTestSig: vectors can be sized and resized - std::string,15/reserving smaller does not change size or capacity" duration="{duration}"/>
+    <testCase name="Testing checked-if" duration="{duration}"/>
+    <testCase name="Testing checked-if 2" duration="{duration}">
+      <skipped message="FAIL()">
+FAILED:
+Misc.tests.cpp:<line number>
+      </skipped>
+    </testCase>
+    <testCase name="Testing checked-if 3" duration="{duration}">
+      <skipped message="FAIL()">
+FAILED:
+Misc.tests.cpp:<line number>
+      </skipped>
+    </testCase>
     <testCase name="This test 'should' fail but doesn't" duration="{duration}"/>
     <testCase name="atomic if" duration="{duration}"/>
     <testCase name="checkedElse" duration="{duration}"/>
     <testCase name="checkedElse, failing" duration="{duration}">
-      <failure message="CHECKED_ELSE(flag)">
-FAILED:
-	CHECKED_ELSE( flag )
-with expansion:
-	false
-Misc.tests.cpp:<line number>
-      </failure>
       <failure message="REQUIRE(testCheckedElse( false ))">
 FAILED:
 	REQUIRE( testCheckedElse( false ) )
@@ -1604,13 +1621,6 @@ Misc.tests.cpp:<line number>
     </testCase>
     <testCase name="checkedIf" duration="{duration}"/>
     <testCase name="checkedIf, failing" duration="{duration}">
-      <failure message="CHECKED_IF(flag)">
-FAILED:
-	CHECKED_IF( flag )
-with expansion:
-	false
-Misc.tests.cpp:<line number>
-      </failure>
       <failure message="REQUIRE(testCheckedIf( false ))">
 FAILED:
 	REQUIRE( testCheckedIf( false ) )
diff --git a/packages/Catch2/tests/SelfTest/Baselines/tap.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/tap.sw.approved.txt
index edf6d1ed9517ed4aff1e95760065b455c70c9222..724cd499b98562199eb6310762be5a583eaf25a5 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/tap.sw.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/tap.sw.approved.txt
@@ -158,6 +158,14 @@ ok {test-number} -
 ok {test-number} -
 # #1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0
 ok {test-number} -
+# #2152 - ULP checks between differently signed values were wrong - double
+ok {test-number} - smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323, 4.9406564584124654e-324])
+# #2152 - ULP checks between differently signed values were wrong - double
+ok {test-number} - smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-324, -0.0000000000000000e+00])
+# #2152 - ULP checks between differently signed values were wrong - float
+ok {test-number} - smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) for: 0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
+# #2152 - ULP checks between differently signed values were wrong - float
+ok {test-number} - smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) for: 0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
 # #748 - captures with unexpected exceptions
 not ok {test-number} - unexpected exception with message: 'answer := 42' with 1 message: 'expected exception'
 # #748 - captures with unexpected exceptions
@@ -579,13 +587,13 @@ ok {test-number} - 1.234f == Approx( dMedium ) for: 1.234f == Approx( 1.234 )
 # Approximate comparisons with mixed numeric types
 ok {test-number} - dMedium == Approx( 1.234f ) for: 1.234 == Approx( 1.2339999676 )
 # Arbitrary predicate matcher
-ok {test-number} - 1, Predicate<int>(alwaysTrue, "always true") for: 1 matches predicate: "always true"
+ok {test-number} - 1, Predicate<int>( alwaysTrue, "always true" ) for: 1 matches predicate: "always true"
 # Arbitrary predicate matcher
-ok {test-number} - 1, !Predicate<int>(alwaysFalse, "always false") for: 1 not matches predicate: "always false"
+ok {test-number} - 1, !Predicate<int>( alwaysFalse, "always false" ) for: 1 not matches predicate: "always false"
 # Arbitrary predicate matcher
-ok {test-number} - "Hello olleH", Predicate<std::string>( [] (std::string const& str) -> bool { return str.front() == str.back(); }, "First and last character should be equal") for: "Hello olleH" matches predicate: "First and last character should be equal"
+ok {test-number} - "Hello olleH", Predicate<std::string>( []( std::string const& str ) -> bool { return str.front() == str.back(); }, "First and last character should be equal" ) for: "Hello olleH" matches predicate: "First and last character should be equal"
 # Arbitrary predicate matcher
-ok {test-number} - "This wouldn't pass", !Predicate<std::string>( [] (std::string const& str) -> bool { return str.front() == str.back(); } ) for: "This wouldn't pass" not matches undescribed predicate
+ok {test-number} - "This wouldn't pass", !Predicate<std::string>( []( std::string const& str ) -> bool { return str.front() == str.back(); } ) for: "This wouldn't pass" not matches undescribed predicate
 # Assertion macros support bit operators and bool conversions
 ok {test-number} - lhs | rhs for: Val: 1 | Val: 2
 # Assertion macros support bit operators and bool conversions
@@ -691,75 +699,75 @@ ok {test-number} - name.empty() for: true
 # Clara::Arg supports single-arg parse the way Opt does
 ok {test-number} - name == "foo" for: "foo" == "foo"
 # Combining MatchAllOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype((MatcherA() && MatcherB()) && MatcherC()), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
+ok {test-number} - with 1 message: 'std::is_same< decltype( ( MatcherA() && MatcherB() ) && MatcherC() ), Catch::Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>>::value'
 # Combining MatchAllOfGeneric does not nest
-ok {test-number} - 1, (MatcherA() && MatcherB()) && MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
+ok {test-number} - 1, ( MatcherA() && MatcherB() ) && MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
 # Combining MatchAllOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() && (MatcherB() && MatcherC())), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
+ok {test-number} - with 1 message: 'std::is_same< decltype( MatcherA() && ( MatcherB() && MatcherC() ) ), Catch::Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>>::value'
 # Combining MatchAllOfGeneric does not nest
-ok {test-number} - 1, MatcherA() && (MatcherB() && MatcherC()) for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
+ok {test-number} - 1, MatcherA() && ( MatcherB() && MatcherC() ) for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
 # Combining MatchAllOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype((MatcherA() && MatcherB()) && (MatcherC() && MatcherD())), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
+ok {test-number} - with 1 message: 'std::is_same< decltype( ( MatcherA() && MatcherB() ) && ( MatcherC() && MatcherD() ) ), Catch::Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>>:: value'
 # Combining MatchAllOfGeneric does not nest
-ok {test-number} - 1, (MatcherA() && MatcherB()) && (MatcherC() && MatcherD()) for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true )
+ok {test-number} - 1, ( MatcherA() && MatcherB() ) && ( MatcherC() && MatcherD() ) for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true )
 # Combining MatchAnyOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype((MatcherA() || MatcherB()) || MatcherC()), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
+ok {test-number} - with 1 message: 'std::is_same< decltype( ( MatcherA() || MatcherB() ) || MatcherC() ), Catch::Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>>::value'
 # Combining MatchAnyOfGeneric does not nest
-ok {test-number} - 1, (MatcherA() || MatcherB()) || MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
+ok {test-number} - 1, ( MatcherA() || MatcherB() ) || MatcherC() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
 # Combining MatchAnyOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || (MatcherB() || MatcherC())), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC> >::value'
+ok {test-number} - with 1 message: 'std::is_same< decltype( MatcherA() || ( MatcherB() || MatcherC() ) ), Catch::Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>>::value'
 # Combining MatchAnyOfGeneric does not nest
-ok {test-number} - 1, MatcherA() || (MatcherB() || MatcherC()) for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
+ok {test-number} - 1, MatcherA() || ( MatcherB() || MatcherC() ) for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
 # Combining MatchAnyOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype((MatcherA() || MatcherB()) || (MatcherC() || MatcherD())), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD> >::value'
+ok {test-number} - with 1 message: 'std::is_same< decltype( ( MatcherA() || MatcherB() ) || ( MatcherC() || MatcherD() ) ), Catch::Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>>:: value'
 # Combining MatchAnyOfGeneric does not nest
-ok {test-number} - 1, (MatcherA() || MatcherB()) || (MatcherC() || MatcherD()) for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true )
+ok {test-number} - 1, ( MatcherA() || MatcherB() ) || ( MatcherC() || MatcherD() ) for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true )
 # Combining MatchNotOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype(!MatcherA()), Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA> >::value'
+ok {test-number} - with 1 message: 'std::is_same< decltype( !MatcherA() ), Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA>>::value'
 # Combining MatchNotOfGeneric does not nest
 ok {test-number} - 0, !MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f
 # Combining MatchNotOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype(!!MatcherA()), MatcherA const& >::value'
+ok {test-number} - with 1 message: 'std::is_same<decltype( !!MatcherA() ), MatcherA const&>::value'
 # Combining MatchNotOfGeneric does not nest
 ok {test-number} - 1, !!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f
 # Combining MatchNotOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype(!!!MatcherA()), Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA> >::value'
+ok {test-number} - with 1 message: 'std::is_same< decltype( !!!MatcherA() ), Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA>>::value'
 # Combining MatchNotOfGeneric does not nest
 ok {test-number} - 0, !!!MatcherA() for: 0 not equals: (int) 1 or (float) 1.0f
 # Combining MatchNotOfGeneric does not nest
-ok {test-number} - with 1 message: 'std::is_same< decltype(!!!!MatcherA()), MatcherA const & >::value'
+ok {test-number} - with 1 message: 'std::is_same<decltype( !!!!MatcherA() ), MatcherA const&>::value'
 # Combining MatchNotOfGeneric does not nest
 ok {test-number} - 1, !!!!MatcherA() for: 1 equals: (int) 1 or (float) 1.0f
 # Combining concrete matchers does not use templated matchers
-ok {test-number} - with 1 message: 'std::is_same< decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith("bar") && !EndsWith("foo"))), Catch::Matchers::Detail::MatchAnyOf<std::string> >::value'
+ok {test-number} - with 1 message: 'std::is_same<decltype( StartsWith( "foo" ) || ( StartsWith( "bar" ) && EndsWith( "bar" ) && !EndsWith( "foo" ) ) ), Catch::Matchers::Detail::MatchAnyOf<std::string>>::value'
 # Combining only templated matchers
-ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || MatcherB()), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB> >::value'
+ok {test-number} - with 1 message: 'std::is_same<decltype( MatcherA() || MatcherB() ), Catch::Matchers::Detail:: MatchAnyOfGeneric<MatcherA, MatcherB>>::value'
 # Combining only templated matchers
 ok {test-number} - 1, MatcherA() || MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 )
 # Combining only templated matchers
-ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() && MatcherB()), Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB> >::value'
+ok {test-number} - with 1 message: 'std::is_same<decltype( MatcherA() && MatcherB() ), Catch::Matchers::Detail:: MatchAllOfGeneric<MatcherA, MatcherB>>::value'
 # Combining only templated matchers
 ok {test-number} - 1, MatcherA() && MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 )
 # Combining only templated matchers
-ok {test-number} - with 1 message: 'std::is_same< decltype(MatcherA() || !MatcherB()), Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, Catch::Matchers::Detail::MatchNotOfGeneric<MatcherB>> >::value'
+ok {test-number} - with 1 message: 'std::is_same< decltype( MatcherA() || !MatcherB() ), Catch::Matchers::Detail::MatchAnyOfGeneric< MatcherA, Catch::Matchers::Detail::MatchNotOfGeneric<MatcherB>>>::value'
 # Combining only templated matchers
 ok {test-number} - 1, MatcherA() || !MatcherB() for: 1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 )
 # Combining templated and concrete matchers
-ok {test-number} - vec, Predicate<std::vector<int>>([](auto const& v) { return std::all_of(v.begin(), v.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") && !EqualsRange(a) for: { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } )
+ok {test-number} - vec, Predicate<std::vector<int>>( []( auto const& v ) { return std::all_of( v.begin(), v.end(), []( int elem ) { return elem % 2 == 1; } ); }, "All elements are odd" ) && !EqualsRange( a ) for: { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } )
 # Combining templated and concrete matchers
-ok {test-number} - str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" )
+ok {test-number} - str, StartsWith( "foo" ) && EqualsRange( arr ) && EndsWith( "bar" ) for: "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" )
 # Combining templated and concrete matchers
-ok {test-number} - str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar") for: "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" )
+ok {test-number} - str, StartsWith( "foo" ) && !EqualsRange( bad_arr ) && EndsWith( "bar" ) for: "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" )
 # Combining templated and concrete matchers
-ok {test-number} - str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
+ok {test-number} - str, EqualsRange( arr ) && StartsWith( "foo" ) && EndsWith( "bar" ) for: "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
 # Combining templated and concrete matchers
-ok {test-number} - str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar") for: "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
+ok {test-number} - str, !EqualsRange( bad_arr ) && StartsWith( "foo" ) && EndsWith( "bar" ) for: "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
 # Combining templated and concrete matchers
-ok {test-number} - str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")) for: "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) )
+ok {test-number} - str, EqualsRange( bad_arr ) || ( StartsWith( "foo" ) && EndsWith( "bar" ) ) for: "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) )
 # Combining templated and concrete matchers
-ok {test-number} - str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr) for: "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } )
+ok {test-number} - str, ( StartsWith( "foo" ) && EndsWith( "bar" ) ) || EqualsRange( bad_arr ) for: "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } )
 # Combining templated matchers
-ok {test-number} - container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c) for: { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } )
+ok {test-number} - container, EqualsRange( a ) || EqualsRange( b ) || EqualsRange( c ) for: { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } )
 # Commas in various macros are allowed
 ok {test-number} - std::vector<constructor_throws>{constructor_throws{}, constructor_throws{}}
 # Commas in various macros are allowed
@@ -867,7 +875,7 @@ ok {test-number} - first.matchCalled for: true
 # Composed generic matchers shortcircuit
 ok {test-number} - !second.matchCalled for: true
 # Composed generic matchers shortcircuit
-ok {test-number} - matcher.match(1) for: true
+ok {test-number} - matcher.match( 1 ) for: true
 # Composed generic matchers shortcircuit
 ok {test-number} - first.matchCalled for: true
 # Composed generic matchers shortcircuit
@@ -885,9 +893,9 @@ ok {test-number} - first.matchCalled for: true
 # Composed matchers shortcircuit
 ok {test-number} - !second.matchCalled for: true
 # Contains string matcher
-not ok {test-number} - testStringForMatching(), Contains("not there", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" contains: "not there" (case insensitive)
+not ok {test-number} - testStringForMatching(), Contains( "not there", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "not there" (case insensitive)
 # Contains string matcher
-not ok {test-number} - testStringForMatching(), Contains("STRING") for: "this string contains 'abc' as a substring" contains: "STRING"
+not ok {test-number} - testStringForMatching(), Contains( "STRING" ) for: "this string contains 'abc' as a substring" contains: "STRING"
 # Copy and then generate a range
 ok {test-number} - elem % 2 == 1 for: 1 == 1
 # Copy and then generate a range
@@ -933,9 +941,9 @@ ok {test-number} - enumInfo->lookup(1) == "Value2" for: Value2 == "Value2"
 # Directly creating an EnumInfo
 ok {test-number} - enumInfo->lookup(3) == "{** unexpected enum value **}" for: {** unexpected enum value **} == "{** unexpected enum value **}"
 # EndsWith string matcher
-not ok {test-number} - testStringForMatching(), EndsWith("Substring") for: "this string contains 'abc' as a substring" ends with: "Substring"
+not ok {test-number} - testStringForMatching(), EndsWith( "Substring" ) for: "this string contains 'abc' as a substring" ends with: "Substring"
 # EndsWith string matcher
-not ok {test-number} - testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive)
+not ok {test-number} - testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: "this" (case insensitive)
 # Enums can quickly have stringification enabled using REGISTER_ENUM
 ok {test-number} - stringify( EnumClass3::Value1 ) == "Value1" for: "Value1" == "Value1"
 # Enums can quickly have stringification enabled using REGISTER_ENUM
@@ -993,13 +1001,13 @@ ok {test-number} - data.str_hello.size() == 5 for: 5 == 5
 # Equality checks that should succeed
 ok {test-number} - x == Approx( 1.3 ) for: 1.3 == Approx( 1.3 )
 # Equals
-ok {test-number} - testStringForMatching(), Equals("this string contains 'abc' as a substring") for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
+ok {test-number} - testStringForMatching(), Equals( "this string contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
 # Equals
-ok {test-number} - testStringForMatching(), Equals("this string contains 'ABC' as a substring", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
+ok {test-number} - testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
 # Equals string matcher
-not ok {test-number} - testStringForMatching(), Equals("this string contains 'ABC' as a substring") for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
+not ok {test-number} - testStringForMatching(), Equals( "this string contains 'ABC' as a substring" ) for: "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
 # Equals string matcher
-not ok {test-number} - testStringForMatching(), Equals("something else", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" equals: "something else" (case insensitive)
+not ok {test-number} - testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" equals: "something else" (case insensitive)
 # Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified
 ok {test-number} - ::Catch::Detail::stringify(WhatException{}) == "This exception has overridden what() method" for: "This exception has overridden what() method" == "This exception has overridden what() method"
 # Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified
@@ -1007,21 +1015,21 @@ ok {test-number} - ::Catch::Detail::stringify(OperatorException{}) == "OperatorE
 # Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified
 ok {test-number} - ::Catch::Detail::stringify(StringMakerException{}) == "StringMakerException" for: "StringMakerException" == "StringMakerException"
 # Exception matchers that fail
-not ok {test-number} - expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{1}
+not ok {test-number} - expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }
 # Exception matchers that fail
-not ok {test-number} - expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{1}
+not ok {test-number} - expected exception, got none; expression was: doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }
 # Exception matchers that fail
-not ok {test-number} - unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt(1), SpecialException, ExceptionMatcher{1}
+not ok {test-number} - unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }
 # Exception matchers that fail
-not ok {test-number} - unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt(1), SpecialException, ExceptionMatcher{1}
+not ok {test-number} - unexpected exception with message: 'Unknown exception'; expression was: throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }
 # Exception matchers that fail
-not ok {test-number} - throwsSpecialException(3), SpecialException, ExceptionMatcher{1} for: SpecialException::what special exception has value of 1
+not ok {test-number} - throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 } for: SpecialException::what special exception has value of 1
 # Exception matchers that fail
-not ok {test-number} - throwsSpecialException(4), SpecialException, ExceptionMatcher{1} for: SpecialException::what special exception has value of 1
+not ok {test-number} - throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 } for: SpecialException::what special exception has value of 1
 # Exception matchers that succeed
-ok {test-number} - throwsSpecialException(1), SpecialException, ExceptionMatcher{1} for: SpecialException::what special exception has value of 1
+ok {test-number} - throwsSpecialException( 1 ), SpecialException, ExceptionMatcher{ 1 } for: SpecialException::what special exception has value of 1
 # Exception matchers that succeed
-ok {test-number} - throwsSpecialException(2), SpecialException, ExceptionMatcher{2} for: SpecialException::what special exception has value of 2
+ok {test-number} - throwsSpecialException( 2 ), SpecialException, ExceptionMatcher{ 2 } for: SpecialException::what special exception has value of 2
 # Exception messages can be tested for
 ok {test-number} - thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
 # Exception messages can be tested for
@@ -1035,13 +1043,13 @@ ok {test-number} - thisThrows(), Contains( "except" ) for: "expected exception"
 # Exception messages can be tested for
 ok {test-number} - thisThrows(), Contains( "exCept", Catch::CaseSensitive::No ) for: "expected exception" contains: "except" (case insensitive)
 # Exceptions matchers
-ok {test-number} - throwsDerivedException(), DerivedException, Message("DerivedException::what") for: DerivedException::what exception message matches "DerivedException::what"
+ok {test-number} - throwsDerivedException(), DerivedException, Message( "DerivedException::what" ) for: DerivedException::what exception message matches "DerivedException::what"
 # Exceptions matchers
-ok {test-number} - throwsDerivedException(), DerivedException, !Message("derivedexception::what") for: DerivedException::what not exception message matches "derivedexception::what"
+ok {test-number} - throwsDerivedException(), DerivedException, !Message( "derivedexception::what" ) for: DerivedException::what not exception message matches "derivedexception::what"
 # Exceptions matchers
-ok {test-number} - throwsSpecialException(2), SpecialException, !Message("DerivedException::what") for: SpecialException::what not exception message matches "DerivedException::what"
+ok {test-number} - throwsSpecialException( 2 ), SpecialException, !Message( "DerivedException::what" ) for: SpecialException::what not exception message matches "DerivedException::what"
 # Exceptions matchers
-ok {test-number} - throwsSpecialException(2), SpecialException, Message("SpecialException::what") for: SpecialException::what exception message matches "SpecialException::what"
+ok {test-number} - throwsSpecialException( 2 ), SpecialException, Message( "SpecialException::what" ) for: SpecialException::what exception message matches "SpecialException::what"
 # Expected exceptions that don't throw or unexpected exceptions fail the test
 not ok {test-number} - unexpected exception with message: 'expected exception'; expression was: thisThrows(), std::string
 # Expected exceptions that don't throw or unexpected exceptions fail the test
@@ -1067,125 +1075,127 @@ ok {test-number} - Factorial(3) == 6 for: 6 == 6
 # Factorials are computed
 ok {test-number} - Factorial(10) == 3628800 for: 3628800 (0x<hex digits>) == 3628800 (0x<hex digits>)
 # Floating point matchers: double
-ok {test-number} - 10., WithinRel(11.1, 0.1) for: 10.0 and 11.1 are within 10% of each other
+ok {test-number} - 10., WithinRel( 11.1, 0.1 ) for: 10.0 and 11.1 are within 10% of each other
 # Floating point matchers: double
-ok {test-number} - 10., !WithinRel(11.2, 0.1) for: 10.0 not and 11.2 are within 10% of each other
+ok {test-number} - 10., !WithinRel( 11.2, 0.1 ) for: 10.0 not and 11.2 are within 10% of each other
 # Floating point matchers: double
-ok {test-number} - 1., !WithinRel(0., 0.99) for: 1.0 not and 0 are within 99% of each other
+ok {test-number} - 1., !WithinRel( 0., 0.99 ) for: 1.0 not and 0 are within 99% of each other
 # Floating point matchers: double
-ok {test-number} - -0., WithinRel(0.) for: -0.0 and 0 are within 2.22045e-12% of each other
+ok {test-number} - -0., WithinRel( 0. ) for: -0.0 and 0 are within 2.22045e-12% of each other
 # Floating point matchers: double
-ok {test-number} - v1, WithinRel(v2) for: 0.0 and 2.22507e-308 are within 2.22045e-12% of each other
+ok {test-number} - v1, WithinRel( v2 ) for: 0.0 and 2.22507e-308 are within 2.22045e-12% of each other
 # Floating point matchers: double
-ok {test-number} - 1., WithinAbs(1., 0) for: 1.0 is within 0.0 of 1.0
+ok {test-number} - 1., WithinAbs( 1., 0 ) for: 1.0 is within 0.0 of 1.0
 # Floating point matchers: double
-ok {test-number} - 0., WithinAbs(1., 1) for: 0.0 is within 1.0 of 1.0
+ok {test-number} - 0., WithinAbs( 1., 1 ) for: 0.0 is within 1.0 of 1.0
 # Floating point matchers: double
-ok {test-number} - 0., !WithinAbs(1., 0.99) for: 0.0 not is within 0.99 of 1.0
+ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0
 # Floating point matchers: double
-ok {test-number} - 0., !WithinAbs(1., 0.99) for: 0.0 not is within 0.99 of 1.0
+ok {test-number} - 0., !WithinAbs( 1., 0.99 ) for: 0.0 not is within 0.99 of 1.0
 # Floating point matchers: double
-ok {test-number} - 11., !WithinAbs(10., 0.5) for: 11.0 not is within 0.5 of 10.0
+ok {test-number} - 11., !WithinAbs( 10., 0.5 ) for: 11.0 not is within 0.5 of 10.0
 # Floating point matchers: double
-ok {test-number} - 10., !WithinAbs(11., 0.5) for: 10.0 not is within 0.5 of 11.0
+ok {test-number} - 10., !WithinAbs( 11., 0.5 ) for: 10.0 not is within 0.5 of 11.0
 # Floating point matchers: double
-ok {test-number} - -10., WithinAbs(-10., 0.5) for: -10.0 is within 0.5 of -10.0
+ok {test-number} - -10., WithinAbs( -10., 0.5 ) for: -10.0 is within 0.5 of -10.0
 # Floating point matchers: double
-ok {test-number} - -10., WithinAbs(-9.6, 0.5) for: -10.0 is within 0.5 of -9.6
+ok {test-number} - -10., WithinAbs( -9.6, 0.5 ) for: -10.0 is within 0.5 of -9.6
 # Floating point matchers: double
-ok {test-number} - 1., WithinULP(1., 0) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
+ok {test-number} - 1., WithinULP( 1., 0 ) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
 # Floating point matchers: double
-ok {test-number} - nextafter(1., 2.), WithinULP(1., 1) for: 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00])
+ok {test-number} - nextafter( 1., 2. ), WithinULP( 1., 1 ) for: 1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00])
 # Floating point matchers: double
-ok {test-number} - 0., WithinULP(nextafter(0., 1.), 1) for: 0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324])
+ok {test-number} - 0., WithinULP( nextafter( 0., 1. ), 1 ) for: 0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324])
 # Floating point matchers: double
-ok {test-number} - 1., WithinULP(nextafter(1., 0.), 1) for: 1.0 is within 1 ULPs of 9.9999999999999989e-01 ([9.9999999999999978e-01, 1.0000000000000000e+00])
+ok {test-number} - 1., WithinULP( nextafter( 1., 0. ), 1 ) for: 1.0 is within 1 ULPs of 9.9999999999999989e-01 ([9.9999999999999978e-01, 1.0000000000000000e+00])
 # Floating point matchers: double
-ok {test-number} - 1., !WithinULP(nextafter(1., 2.), 0) for: 1.0 not is within 0 ULPs of 1.0000000000000002e+00 ([1.0000000000000002e+00, 1.0000000000000002e+00])
+ok {test-number} - 1., !WithinULP( nextafter( 1., 2. ), 0 ) for: 1.0 not is within 0 ULPs of 1.0000000000000002e+00 ([1.0000000000000002e+00, 1.0000000000000002e+00])
 # Floating point matchers: double
-ok {test-number} - 1., WithinULP(1., 0) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
+ok {test-number} - 1., WithinULP( 1., 0 ) for: 1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
 # Floating point matchers: double
-ok {test-number} - -0., WithinULP(0., 0) for: -0.0 is within 0 ULPs of 0.0000000000000000e+00 ([0.0000000000000000e+00, 0.0000000000000000e+00])
+ok {test-number} - -0., WithinULP( 0., 0 ) for: -0.0 is within 0 ULPs of 0.0000000000000000e+00 ([0.0000000000000000e+00, 0.0000000000000000e+00])
 # Floating point matchers: double
-ok {test-number} - 1., WithinAbs(1., 0.5) || WithinULP(2., 1) for: 1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0000000000000000e+00 ([1.9999999999999998e+00, 2.0000000000000004e+00]) )
+ok {test-number} - 1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 ) for: 1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0000000000000000e+00 ([1.9999999999999998e+00, 2.0000000000000004e+00]) )
 # Floating point matchers: double
-ok {test-number} - 1., WithinAbs(2., 0.5) || WithinULP(1., 0) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) )
+ok {test-number} - 1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 ) for: 1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) )
 # Floating point matchers: double
-ok {test-number} - 0.0001, WithinAbs(0., 0.001) || WithinRel(0., 0.1) for: 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
+ok {test-number} - 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) for: 0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
 # Floating point matchers: double
-ok {test-number} - WithinAbs(1., 0.)
+ok {test-number} - WithinAbs( 1., 0. )
 # Floating point matchers: double
-ok {test-number} - WithinAbs(1., -1.), std::domain_error
+ok {test-number} - WithinAbs( 1., -1. ), std::domain_error
 # Floating point matchers: double
-ok {test-number} - WithinULP(1., 0)
+ok {test-number} - WithinULP( 1., 0 )
 # Floating point matchers: double
-ok {test-number} - WithinRel(1., 0.)
+ok {test-number} - WithinRel( 1., 0. )
 # Floating point matchers: double
-ok {test-number} - WithinRel(1., -0.2), std::domain_error
+ok {test-number} - WithinRel( 1., -0.2 ), std::domain_error
 # Floating point matchers: double
-ok {test-number} - WithinRel(1., 1.), std::domain_error
+ok {test-number} - WithinRel( 1., 1. ), std::domain_error
+# Floating point matchers: float
+ok {test-number} - 10.f, WithinRel( 11.1f, 0.1f ) for: 10.0f and 11.1 are within 10% of each other
 # Floating point matchers: float
-ok {test-number} - 10.f, WithinRel(11.1f, 0.1f) for: 10.0f and 11.1 are within 10% of each other
+ok {test-number} - 10.f, !WithinRel( 11.2f, 0.1f ) for: 10.0f not and 11.2 are within 10% of each other
 # Floating point matchers: float
-ok {test-number} - 10.f, !WithinRel(11.2f, 0.1f) for: 10.0f not and 11.2 are within 10% of each other
+ok {test-number} - 1.f, !WithinRel( 0.f, 0.99f ) for: 1.0f not and 0 are within 99% of each other
 # Floating point matchers: float
-ok {test-number} - 1.f, !WithinRel(0.f, 0.99f) for: 1.0f not and 0 are within 99% of each other
+ok {test-number} - -0.f, WithinRel( 0.f ) for: -0.0f and 0 are within 0.00119209% of each other
 # Floating point matchers: float
-ok {test-number} - -0.f, WithinRel(0.f) for: -0.0f and 0 are within 0.00119209% of each other
+ok {test-number} - v1, WithinRel( v2 ) for: 0.0f and 1.17549e-38 are within 0.00119209% of each other
 # Floating point matchers: float
-ok {test-number} - v1, WithinRel(v2) for: 0.0f and 1.17549e-38 are within 0.00119209% of each other
+ok {test-number} - 1.f, WithinAbs( 1.f, 0 ) for: 1.0f is within 0.0 of 1.0
 # Floating point matchers: float
-ok {test-number} - 1.f, WithinAbs(1.f, 0) for: 1.0f is within 0.0 of 1.0
+ok {test-number} - 0.f, WithinAbs( 1.f, 1 ) for: 0.0f is within 1.0 of 1.0
 # Floating point matchers: float
-ok {test-number} - 0.f, WithinAbs(1.f, 1) for: 0.0f is within 1.0 of 1.0
+ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0
 # Floating point matchers: float
-ok {test-number} - 0.f, !WithinAbs(1.f, 0.99f) for: 0.0f not is within 0.9900000095 of 1.0
+ok {test-number} - 0.f, !WithinAbs( 1.f, 0.99f ) for: 0.0f not is within 0.9900000095 of 1.0
 # Floating point matchers: float
-ok {test-number} - 0.f, !WithinAbs(1.f, 0.99f) for: 0.0f not is within 0.9900000095 of 1.0
+ok {test-number} - 0.f, WithinAbs( -0.f, 0 ) for: 0.0f is within 0.0 of -0.0
 # Floating point matchers: float
-ok {test-number} - 0.f, WithinAbs(-0.f, 0) for: 0.0f is within 0.0 of -0.0
+ok {test-number} - 11.f, !WithinAbs( 10.f, 0.5f ) for: 11.0f not is within 0.5 of 10.0
 # Floating point matchers: float
-ok {test-number} - 11.f, !WithinAbs(10.f, 0.5f) for: 11.0f not is within 0.5 of 10.0
+ok {test-number} - 10.f, !WithinAbs( 11.f, 0.5f ) for: 10.0f not is within 0.5 of 11.0
 # Floating point matchers: float
-ok {test-number} - 10.f, !WithinAbs(11.f, 0.5f) for: 10.0f not is within 0.5 of 11.0
+ok {test-number} - -10.f, WithinAbs( -10.f, 0.5f ) for: -10.0f is within 0.5 of -10.0
 # Floating point matchers: float
-ok {test-number} - -10.f, WithinAbs(-10.f, 0.5f) for: -10.0f is within 0.5 of -10.0
+ok {test-number} - -10.f, WithinAbs( -9.6f, 0.5f ) for: -10.0f is within 0.5 of -9.6000003815
 # Floating point matchers: float
-ok {test-number} - -10.f, WithinAbs(-9.6f, 0.5f) for: -10.0f is within 0.5 of -9.6000003815
+ok {test-number} - 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
 # Floating point matchers: float
-ok {test-number} - 1.f, WithinULP(1.f, 0) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
+ok {test-number} - -1.f, WithinULP( -1.f, 0 ) for: -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00])
 # Floating point matchers: float
-ok {test-number} - nextafter(1.f, 2.f), WithinULP(1.f, 1) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00])
+ok {test-number} - nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) for: 1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00])
 # Floating point matchers: float
-ok {test-number} - 0.f, WithinULP(nextafter(0.f, 1.f), 1) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45])
+ok {test-number} - 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) for: 0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45])
 # Floating point matchers: float
-ok {test-number} - 1.f, WithinULP(nextafter(1.f, 0.f), 1) for: 1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00])
+ok {test-number} - 1.f, WithinULP( nextafter( 1.f, 0.f ), 1 ) for: 1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00])
 # Floating point matchers: float
-ok {test-number} - 1.f, !WithinULP(nextafter(1.f, 2.f), 0) for: 1.0f not is within 0 ULPs of 1.00000012e+00f ([1.00000012e+00, 1.00000012e+00])
+ok {test-number} - 1.f, !WithinULP( nextafter( 1.f, 2.f ), 0 ) for: 1.0f not is within 0 ULPs of 1.00000012e+00f ([1.00000012e+00, 1.00000012e+00])
 # Floating point matchers: float
-ok {test-number} - 1.f, WithinULP(1.f, 0) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
+ok {test-number} - 1.f, WithinULP( 1.f, 0 ) for: 1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
 # Floating point matchers: float
-ok {test-number} - -0.f, WithinULP(0.f, 0) for: -0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00])
+ok {test-number} - -0.f, WithinULP( 0.f, 0 ) for: -0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00])
 # Floating point matchers: float
-ok {test-number} - 1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1) for: 1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) )
+ok {test-number} - 1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 ) for: 1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) )
 # Floating point matchers: float
-ok {test-number} - 1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) )
+ok {test-number} - 1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 ) for: 1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) )
 # Floating point matchers: float
-ok {test-number} - 0.0001f, WithinAbs(0.f, 0.001f) || WithinRel(0.f, 0.1f) for: 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
+ok {test-number} - 0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) for: 0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
 # Floating point matchers: float
-ok {test-number} - WithinAbs(1.f, 0.f)
+ok {test-number} - WithinAbs( 1.f, 0.f )
 # Floating point matchers: float
-ok {test-number} - WithinAbs(1.f, -1.f), std::domain_error
+ok {test-number} - WithinAbs( 1.f, -1.f ), std::domain_error
 # Floating point matchers: float
-ok {test-number} - WithinULP(1.f, 0)
+ok {test-number} - WithinULP( 1.f, 0 )
 # Floating point matchers: float
-ok {test-number} - WithinULP(1.f, static_cast<uint64_t>(-1)), std::domain_error
+ok {test-number} - WithinULP( 1.f, static_cast<uint64_t>( -1 ) ), std::domain_error
 # Floating point matchers: float
-ok {test-number} - WithinRel(1.f, 0.f)
+ok {test-number} - WithinRel( 1.f, 0.f )
 # Floating point matchers: float
-ok {test-number} - WithinRel(1.f, -0.2f), std::domain_error
+ok {test-number} - WithinRel( 1.f, -0.2f ), std::domain_error
 # Floating point matchers: float
-ok {test-number} - WithinRel(1.f, 1.f), std::domain_error
+ok {test-number} - WithinRel( 1.f, 1.f ), std::domain_error
 # Generators -- adapters
 ok {test-number} - i % 2 == 0 for: 0 == 0
 # Generators -- adapters
@@ -1781,19 +1791,19 @@ ok {test-number} - d <= Approx( 1.22 ).epsilon(0.1) for: 1.23 <= Approx( 1.22 )
 # ManuallyRegistered
 ok {test-number} - with 1 message: 'was called'
 # Matchers can be (AllOf) composed with the && operator
-ok {test-number} - testStringForMatching(), Contains("string") && Contains("abc") && Contains("substring") && Contains("contains") for: "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" )
+ok {test-number} - testStringForMatching(), Contains( "string" ) && Contains( "abc" ) && Contains( "substring" ) && Contains( "contains" ) for: "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" )
 # Matchers can be (AnyOf) composed with the || operator
-ok {test-number} - testStringForMatching(), Contains("string") || Contains("different") || Contains("random") for: "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" )
+ok {test-number} - testStringForMatching(), Contains( "string" ) || Contains( "different" ) || Contains( "random" ) for: "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" )
 # Matchers can be (AnyOf) composed with the || operator
-ok {test-number} - testStringForMatching2(), Contains("string") || Contains("different") || Contains("random") for: "some completely different text that contains one common word" ( contains: "string" or contains: "different" or contains: "random" )
+ok {test-number} - testStringForMatching2(), Contains( "string" ) || Contains( "different" ) || Contains( "random" ) for: "some completely different text that contains one common word" ( contains: "string" or contains: "different" or contains: "random" )
 # Matchers can be composed with both && and ||
-ok {test-number} - testStringForMatching(), (Contains("string") || Contains("different")) && Contains("substring") for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "substring" )
+ok {test-number} - testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) && Contains( "substring" ) for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "substring" )
 # Matchers can be composed with both && and || - failing
-not ok {test-number} - testStringForMatching(), (Contains("string") || Contains("different")) && Contains("random") for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )
+not ok {test-number} - testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) && Contains( "random" ) for: "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )
 # Matchers can be negated (Not) with the ! operator
-ok {test-number} - testStringForMatching(), !Contains("different") for: "this string contains 'abc' as a substring" not contains: "different"
+ok {test-number} - testStringForMatching(), !Contains( "different" ) for: "this string contains 'abc' as a substring" not contains: "different"
 # Matchers can be negated (Not) with the ! operator - failing
-not ok {test-number} - testStringForMatching(), !Contains("substring") for: "this string contains 'abc' as a substring" not contains: "substring"
+not ok {test-number} - testStringForMatching(), !Contains( "substring" ) for: "this string contains 'abc' as a substring" not contains: "substring"
 # Mismatching exception messages failing the test
 ok {test-number} - thisThrows(), "expected exception" for: "expected exception" equals: "expected exception"
 # Mismatching exception messages failing the test
@@ -1951,13 +1961,13 @@ not ok {test-number} - explicitly with 1 message: 'Message from section one'
 # Output from all sections is reported
 not ok {test-number} - explicitly with 1 message: 'Message from section two'
 # Overloaded comma or address-of operators are not used
-ok {test-number} - (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed
+ok {test-number} - ( EvilMatcher(), EvilMatcher() ), EvilCommaOperatorUsed
 # Overloaded comma or address-of operators are not used
 ok {test-number} - &EvilMatcher(), EvilAddressOfOperatorUsed
 # Overloaded comma or address-of operators are not used
-ok {test-number} - EvilMatcher() || (EvilMatcher() && !EvilMatcher())
+ok {test-number} - EvilMatcher() || ( EvilMatcher() && !EvilMatcher() )
 # Overloaded comma or address-of operators are not used
-ok {test-number} - (EvilMatcher() && EvilMatcher()) || !EvilMatcher()
+ok {test-number} - ( EvilMatcher() && EvilMatcher() ) || !EvilMatcher()
 # Parse test names and tags
 ok {test-number} - spec.hasFilters() == false for: false == false
 # Parse test names and tags
@@ -2285,7 +2295,7 @@ ok {test-number} - str1.size() == 2 + 5 for: 7 == 7
 # Precision of floating point stringification can be set
 ok {test-number} - str2.size() == 2 + 15 for: 17 == 17
 # Predicate matcher can accept const char*
-ok {test-number} - "foo", Predicate<const char*>([] (const char* const&) { return true; }) for: "foo" matches undescribed predicate
+ok {test-number} - "foo", Predicate<const char*>( []( const char* const& ) { return true; } ) for: "foo" matches undescribed predicate
 # Process can be configured on command line
 ok {test-number} - result for: {?}
 # Process can be configured on command line
@@ -2461,13 +2471,13 @@ ok {test-number} - Catch::Detail::stringify(UsesSentinel{}) == "{  }" for: "{  }
 # Reconstruction should be based on stringification: #914
 not ok {test-number} - truthy(false) for: Hey, its truthy!
 # Regex string matcher
-not ok {test-number} - testStringForMatching(), Matches("this STRING contains 'abc' as a substring") for: "this string contains 'abc' as a substring" matches "this STRING contains 'abc' as a substring" case sensitively
+not ok {test-number} - testStringForMatching(), Matches( "this STRING contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" matches "this STRING contains 'abc' as a substring" case sensitively
 # Regex string matcher
-not ok {test-number} - testStringForMatching(), Matches("contains 'abc' as a substring") for: "this string contains 'abc' as a substring" matches "contains 'abc' as a substring" case sensitively
+not ok {test-number} - testStringForMatching(), Matches( "contains 'abc' as a substring" ) for: "this string contains 'abc' as a substring" matches "contains 'abc' as a substring" case sensitively
 # Regex string matcher
-not ok {test-number} - testStringForMatching(), Matches("this string contains 'abc' as a") for: "this string contains 'abc' as a substring" matches "this string contains 'abc' as a" case sensitively
+not ok {test-number} - testStringForMatching(), Matches( "this string contains 'abc' as a" ) for: "this string contains 'abc' as a substring" matches "this string contains 'abc' as a" case sensitively
 # Regression test #1
-ok {test-number} - actual, !UnorderedEquals(expected) for: { 'a', 'b' } not UnorderedEquals: { 'c', 'b' }
+ok {test-number} - actual, !UnorderedEquals( expected ) for: { 'a', 'b' } not UnorderedEquals: { 'c', 'b' }
 # Reporter's write listings to provided stream
 ok {test-number} - !(factories.empty()) for: !false
 # Reporter's write listings to provided stream
@@ -2616,9 +2626,9 @@ ok {test-number} - Approx( d ) != 1.24 for: Approx( 1.23 ) != 1.24
 Message from section one
 Message from section two
 # StartsWith string matcher
-not ok {test-number} - testStringForMatching(), StartsWith("This String") for: "this string contains 'abc' as a substring" starts with: "This String"
+not ok {test-number} - testStringForMatching(), StartsWith( "This String" ) for: "this string contains 'abc' as a substring" starts with: "This String"
 # StartsWith string matcher
-not ok {test-number} - testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" starts with: "string" (case insensitive)
+not ok {test-number} - testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "string" (case insensitive)
 # Static arrays are convertible to string
 ok {test-number} - Catch::Detail::stringify(singular) == "{ 1 }" for: "{ 1 }" == "{ 1 }"
 # Static arrays are convertible to string
@@ -2626,50 +2636,38 @@ ok {test-number} - Catch::Detail::stringify(arr) == "{ 3, 2, 1 }" for: "{ 3, 2,
 # Static arrays are convertible to string
 ok {test-number} - Catch::Detail::stringify(arr) == R"({ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } })" for: "{ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } }" == "{ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } }"
 # String matchers
-ok {test-number} - testStringForMatching(), Contains("string") for: "this string contains 'abc' as a substring" contains: "string"
+ok {test-number} - testStringForMatching(), Contains( "string" ) for: "this string contains 'abc' as a substring" contains: "string"
 # String matchers
-ok {test-number} - testStringForMatching(), Contains("string", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" contains: "string" (case insensitive)
+ok {test-number} - testStringForMatching(), Contains( "string", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "string" (case insensitive)
 # String matchers
-ok {test-number} - testStringForMatching(), Contains("abc") for: "this string contains 'abc' as a substring" contains: "abc"
+ok {test-number} - testStringForMatching(), Contains( "abc" ) for: "this string contains 'abc' as a substring" contains: "abc"
 # String matchers
-ok {test-number} - testStringForMatching(), Contains("aBC", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
+ok {test-number} - testStringForMatching(), Contains( "aBC", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
 # String matchers
-ok {test-number} - testStringForMatching(), StartsWith("this") for: "this string contains 'abc' as a substring" starts with: "this"
+ok {test-number} - testStringForMatching(), StartsWith( "this" ) for: "this string contains 'abc' as a substring" starts with: "this"
 # String matchers
-ok {test-number} - testStringForMatching(), StartsWith("THIS", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
+ok {test-number} - testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
 # String matchers
-ok {test-number} - testStringForMatching(), EndsWith("substring") for: "this string contains 'abc' as a substring" ends with: "substring"
+ok {test-number} - testStringForMatching(), EndsWith( "substring" ) for: "this string contains 'abc' as a substring" ends with: "substring"
 # String matchers
-ok {test-number} - testStringForMatching(), EndsWith(" SuBsTrInG", Catch::CaseSensitive::No) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
+ok {test-number} - testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) for: "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
 # StringRef
 ok {test-number} - empty.empty() for: true
 # StringRef
 ok {test-number} - empty.size() == 0 for: 0 == 0
 # StringRef
-ok {test-number} - empty.isNullTerminated() for: true
-# StringRef
-ok {test-number} - std::strcmp( empty.c_str(), "" ) == 0 for: 0 == 0
+ok {test-number} - std::strcmp( empty.data(), "" ) == 0 for: 0 == 0
 # StringRef
 ok {test-number} - s.empty() == false for: false == false
 # StringRef
 ok {test-number} - s.size() == 5 for: 5 == 5
 # StringRef
-ok {test-number} - s.isNullTerminated() for: true
-# StringRef
 ok {test-number} - std::strcmp( rawChars, "hello" ) == 0 for: 0 == 0
 # StringRef
-ok {test-number} - s.c_str()
-# StringRef
-ok {test-number} - s.c_str() == rawChars for: "hello" == "hello"
-# StringRef
 ok {test-number} - s.data() == rawChars for: "hello" == "hello"
 # StringRef
 ok {test-number} - original == "original"
 # StringRef
-ok {test-number} - !(original.isNullTerminated()) for: !false
-# StringRef
-ok {test-number} - original.c_str()
-# StringRef
 ok {test-number} - original.data()
 # StringRef
 ok {test-number} - ss.empty() == false for: false == false
@@ -2682,7 +2680,7 @@ ok {test-number} - ss == "hello" for: hello == "hello"
 # StringRef
 ok {test-number} - ss.size() == 6 for: 6 == 6
 # StringRef
-ok {test-number} - std::strcmp( ss.c_str(), "world!" ) == 0 for: 0 == 0
+ok {test-number} - std::strcmp( ss.data(), "world!" ) == 0 for: 0 == 0
 # StringRef
 ok {test-number} - s.data() == s2.data() for: "hello world!" == "hello world!"
 # StringRef
@@ -2690,7 +2688,7 @@ ok {test-number} - s.data() == ss.data() for: "hello world!" == "hello world!"
 # StringRef
 ok {test-number} - s.substr(s.size() + 1, 123).empty() for: true
 # StringRef
-ok {test-number} - std::strcmp(ss.c_str(), "world!") == 0 for: 0 == 0
+ok {test-number} - std::strcmp(ss.data(), "world!") == 0 for: 0 == 0
 # StringRef
 ok {test-number} - s.substr(1'000'000, 1).empty() for: true
 # StringRef
@@ -2730,8 +2728,6 @@ ok {test-number} - with 1 message: 'empty.begin() == empty.end()'
 # StringRef at compilation time
 ok {test-number} - with 1 message: 'stringref.size() == 3'
 # StringRef at compilation time
-ok {test-number} - with 1 message: 'stringref.isNullTerminated()'
-# StringRef at compilation time
 ok {test-number} - with 1 message: 'stringref.data() == abc'
 # StringRef at compilation time
 ok {test-number} - with 1 message: 'stringref.begin() == abc'
@@ -2750,21 +2746,25 @@ ok {test-number} - with 1 message: 'shortened.data() == abc'
 # StringRef at compilation time
 ok {test-number} - with 1 message: 'shortened.begin() != shortened.end()'
 # StringRef at compilation time
-ok {test-number} - with 1 message: '!(shortened.isNullTerminated())'
-# StringRef at compilation time
-ok {test-number} - with 1 message: '!(shortened.substr(1, 3).isNullTerminated())'
-# StringRef at compilation time
 ok {test-number} - with 1 message: '!(sr1.empty())'
 # StringRef at compilation time
 ok {test-number} - with 1 message: 'sr1.size() == 3'
 # StringRef at compilation time
-ok {test-number} - with 1 message: 'sr1.isNullTerminated()'
-# StringRef at compilation time
 ok {test-number} - with 1 message: 'sr2.empty()'
 # StringRef at compilation time
 ok {test-number} - with 1 message: 'sr2.size() == 0'
-# StringRef at compilation time
-ok {test-number} - with 1 message: 'sr2.isNullTerminated()'
+# Stringifying char arrays with statically known sizes - char
+ok {test-number} - ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+# Stringifying char arrays with statically known sizes - char
+ok {test-number} - ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+# Stringifying char arrays with statically known sizes - signed char
+ok {test-number} - ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+# Stringifying char arrays with statically known sizes - signed char
+ok {test-number} - ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+# Stringifying char arrays with statically known sizes - unsigned char
+ok {test-number} - ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
+# Stringifying char arrays with statically known sizes - unsigned char
+ok {test-number} - ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s for: ""abc"" == ""abc""
 # Stringifying std::chrono::duration helpers
 ok {test-number} - minute == seconds for: 1 m == 60 s
 # Stringifying std::chrono::duration helpers
@@ -3095,6 +3095,26 @@ ok {test-number} - with 1 message: 'no assertions'
 ok {test-number} - 0x<hex digits> == bit30and31 for: 3221225472 (0x<hex digits>) == 3221225472
 # Test with special, characters "in name
 ok {test-number} -
+# Testing checked-if
+ok {test-number} - true
+# Testing checked-if
+ok {test-number} -
+# Testing checked-if
+ok {test-number} - false  # TODO
+# Testing checked-if
+ok {test-number} - true
+# Testing checked-if
+ok {test-number} - false  # TODO
+# Testing checked-if
+ok {test-number} -
+# Testing checked-if 2
+ok {test-number} - true
+# Testing checked-if 2
+not ok {test-number} - explicitly
+# Testing checked-if 3
+ok {test-number} - false  # TODO
+# Testing checked-if 3
+not ok {test-number} - explicitly
 # The NO_FAIL macro reports a failure but does not fail the test
 ok {test-number} - 1 == 2  # TODO
 # The default listing implementation write to provided stream
@@ -3390,99 +3410,99 @@ ok {test-number} - approx( d ) != 1.25 for: Approx( 1.23 ) != 1.25
 # Variadic macros
 ok {test-number} - with 1 message: 'no assertions'
 # Vector Approx matcher
-ok {test-number} - empty, Approx(empty) for: {  } is approx: {  }
+ok {test-number} - empty, Approx( empty ) for: {  } is approx: {  }
 # Vector Approx matcher
-ok {test-number} - v1, Approx(v1) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
+ok {test-number} - v1, Approx( v1 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
 # Vector Approx matcher
-ok {test-number} - v1, Approx<double>({ 1., 2., 3. }) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
+ok {test-number} - v1, Approx<double>( { 1., 2., 3. } ) for: { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
 # Vector Approx matcher
-ok {test-number} - v1, !Approx(temp) for: { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 }
+ok {test-number} - v1, !Approx( temp ) for: { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 }
 # Vector Approx matcher
-ok {test-number} - v1, !Approx(v2) for: { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 }
+ok {test-number} - v1, !Approx( v2 ) for: { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 }
 # Vector Approx matcher
-ok {test-number} - v1, Approx(v2).margin(0.5) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
+ok {test-number} - v1, Approx( v2 ).margin( 0.5 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
 # Vector Approx matcher
-ok {test-number} - v1, Approx(v2).epsilon(0.5) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
+ok {test-number} - v1, Approx( v2 ).epsilon( 0.5 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
 # Vector Approx matcher
-ok {test-number} - v1, Approx(v2).epsilon(0.1).scale(500) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
+ok {test-number} - v1, Approx( v2 ).epsilon( 0.1 ).scale( 500 ) for: { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
 # Vector Approx matcher -- failing
-not ok {test-number} - empty, Approx(t1) for: {  } is approx: { 1.0, 2.0 }
+not ok {test-number} - empty, Approx( t1 ) for: {  } is approx: { 1.0, 2.0 }
 # Vector Approx matcher -- failing
-not ok {test-number} - v1, Approx(v2) for: { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
+not ok {test-number} - v1, Approx( v2 ) for: { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
 # Vector matchers
-ok {test-number} - v, VectorContains(1) for: { 1, 2, 3 } Contains: 1
+ok {test-number} - v, VectorContains( 1 ) for: { 1, 2, 3 } Contains: 1
 # Vector matchers
-ok {test-number} - v, VectorContains(2) for: { 1, 2, 3 } Contains: 2
+ok {test-number} - v, VectorContains( 2 ) for: { 1, 2, 3 } Contains: 2
 # Vector matchers
-ok {test-number} - v5, (VectorContains<int, CustomAllocator<int>>(2)) for: { 1, 2, 3 } Contains: 2
+ok {test-number} - v5, ( VectorContains<int, CustomAllocator<int>>( 2 ) ) for: { 1, 2, 3 } Contains: 2
 # Vector matchers
-ok {test-number} - v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2 }
+ok {test-number} - v, Contains( v2 ) for: { 1, 2, 3 } Contains: { 1, 2 }
 # Vector matchers
-ok {test-number} - v, Contains<int>({ 1, 2 }) for: { 1, 2, 3 } Contains: { 1, 2 }
+ok {test-number} - v, Contains<int>( { 1, 2 } ) for: { 1, 2, 3 } Contains: { 1, 2 }
 # Vector matchers
-ok {test-number} - v5, (Contains<int, std::allocator<int>, CustomAllocator<int>>(v2)) for: { 1, 2, 3 } Contains: { 1, 2 }
+ok {test-number} - v5, ( Contains<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) for: { 1, 2, 3 } Contains: { 1, 2 }
 # Vector matchers
-ok {test-number} - v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2, 3 }
+ok {test-number} - v, Contains( v2 ) for: { 1, 2, 3 } Contains: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - v, Contains(empty) for: { 1, 2, 3 } Contains: {  }
+ok {test-number} - v, Contains( empty ) for: { 1, 2, 3 } Contains: {  }
 # Vector matchers
-ok {test-number} - empty, Contains(empty) for: {  } Contains: {  }
+ok {test-number} - empty, Contains( empty ) for: {  } Contains: {  }
 # Vector matchers
-ok {test-number} - v5, (Contains<int, std::allocator<int>, CustomAllocator<int>>(v2)) for: { 1, 2, 3 } Contains: { 1, 2, 3 }
+ok {test-number} - v5, ( Contains<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) for: { 1, 2, 3 } Contains: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - v5, Contains(v6) for: { 1, 2, 3 } Contains: { 1, 2 }
+ok {test-number} - v5, Contains( v6 ) for: { 1, 2, 3 } Contains: { 1, 2 }
 # Vector matchers
-ok {test-number} - v, VectorContains(1) && VectorContains(2) for: { 1, 2, 3 } ( Contains: 1 and Contains: 2 )
+ok {test-number} - v, VectorContains( 1 ) && VectorContains( 2 ) for: { 1, 2, 3 } ( Contains: 1 and Contains: 2 )
 # Vector matchers
-ok {test-number} - v, Equals(v) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+ok {test-number} - v, Equals( v ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - empty, Equals(empty) for: {  } Equals: {  }
+ok {test-number} - empty, Equals( empty ) for: {  } Equals: {  }
 # Vector matchers
-ok {test-number} - v, Equals<int>({ 1, 2, 3 }) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+ok {test-number} - v, Equals<int>( { 1, 2, 3 } ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - v, Equals(v2) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+ok {test-number} - v, Equals( v2 ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - v5, (Equals<int, std::allocator<int>, CustomAllocator<int>>(v2)) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+ok {test-number} - v5, ( Equals<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - v5, Equals(v6) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
+ok {test-number} - v5, Equals( v6 ) for: { 1, 2, 3 } Equals: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - v, UnorderedEquals(v) for: { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
+ok {test-number} - v, UnorderedEquals( v ) for: { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - v, UnorderedEquals<int>({ 3, 2, 1 }) for: { 1, 2, 3 } UnorderedEquals: { 3, 2, 1 }
+ok {test-number} - v, UnorderedEquals<int>( { 3, 2, 1 } ) for: { 1, 2, 3 } UnorderedEquals: { 3, 2, 1 }
 # Vector matchers
-ok {test-number} - empty, UnorderedEquals(empty) for: {  } UnorderedEquals: {  }
+ok {test-number} - empty, UnorderedEquals( empty ) for: {  } UnorderedEquals: {  }
 # Vector matchers
-ok {test-number} - permuted, UnorderedEquals(v) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
+ok {test-number} - permuted, UnorderedEquals( v ) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - permuted, UnorderedEquals(v) for: { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
+ok {test-number} - permuted, UnorderedEquals( v ) for: { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
 # Vector matchers
-ok {test-number} - v5, (UnorderedEquals<int, std::allocator<int>, CustomAllocator<int>>(permuted)) for: { 1, 2, 3 } UnorderedEquals: { 2, 3, 1 }
+ok {test-number} - v5, ( UnorderedEquals<int, std::allocator<int>, CustomAllocator<int>>( permuted ) ) for: { 1, 2, 3 } UnorderedEquals: { 2, 3, 1 }
 # Vector matchers
-ok {test-number} - v5_permuted, UnorderedEquals(v5) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
+ok {test-number} - v5_permuted, UnorderedEquals( v5 ) for: { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
 # Vector matchers that fail
-not ok {test-number} - v, VectorContains(-1) for: { 1, 2, 3 } Contains: -1
+not ok {test-number} - v, VectorContains( -1 ) for: { 1, 2, 3 } Contains: -1
 # Vector matchers that fail
-not ok {test-number} - empty, VectorContains(1) for: {  } Contains: 1
+not ok {test-number} - empty, VectorContains( 1 ) for: {  } Contains: 1
 # Vector matchers that fail
-not ok {test-number} - empty, Contains(v) for: {  } Contains: { 1, 2, 3 }
+not ok {test-number} - empty, Contains( v ) for: {  } Contains: { 1, 2, 3 }
 # Vector matchers that fail
-not ok {test-number} - v, Contains(v2) for: { 1, 2, 3 } Contains: { 1, 2, 4 }
+not ok {test-number} - v, Contains( v2 ) for: { 1, 2, 3 } Contains: { 1, 2, 4 }
 # Vector matchers that fail
-not ok {test-number} - v, Equals(v2) for: { 1, 2, 3 } Equals: { 1, 2 }
+not ok {test-number} - v, Equals( v2 ) for: { 1, 2, 3 } Equals: { 1, 2 }
 # Vector matchers that fail
-not ok {test-number} - v2, Equals(v) for: { 1, 2 } Equals: { 1, 2, 3 }
+not ok {test-number} - v2, Equals( v ) for: { 1, 2 } Equals: { 1, 2, 3 }
 # Vector matchers that fail
-not ok {test-number} - empty, Equals(v) for: {  } Equals: { 1, 2, 3 }
+not ok {test-number} - empty, Equals( v ) for: {  } Equals: { 1, 2, 3 }
 # Vector matchers that fail
-not ok {test-number} - v, Equals(empty) for: { 1, 2, 3 } Equals: {  }
+not ok {test-number} - v, Equals( empty ) for: { 1, 2, 3 } Equals: {  }
 # Vector matchers that fail
-not ok {test-number} - v, UnorderedEquals(empty) for: { 1, 2, 3 } UnorderedEquals: {  }
+not ok {test-number} - v, UnorderedEquals( empty ) for: { 1, 2, 3 } UnorderedEquals: {  }
 # Vector matchers that fail
-not ok {test-number} - empty, UnorderedEquals(v) for: {  } UnorderedEquals: { 1, 2, 3 }
+not ok {test-number} - empty, UnorderedEquals( v ) for: {  } UnorderedEquals: { 1, 2, 3 }
 # Vector matchers that fail
-not ok {test-number} - permuted, UnorderedEquals(v) for: { 1, 3 } UnorderedEquals: { 1, 2, 3 }
+not ok {test-number} - permuted, UnorderedEquals( v ) for: { 1, 3 } UnorderedEquals: { 1, 2, 3 }
 # Vector matchers that fail
-not ok {test-number} - permuted, UnorderedEquals(v) for: { 3, 1 } UnorderedEquals: { 1, 2, 3 }
+not ok {test-number} - permuted, UnorderedEquals( v ) for: { 3, 1 } UnorderedEquals: { 1, 2, 3 }
 # When checked exceptions are thrown they can be expected or unexpected
 ok {test-number} - thisThrows(), std::domain_error
 # When checked exceptions are thrown they can be expected or unexpected
@@ -3531,6 +3551,8 @@ ok {test-number} - encode( stringWithQuotes, Catch::XmlEncode::ForAttributes ) =
 ok {test-number} - encode( "[\x01]" ) == "[\\x01]" for: "[\x01]" == "[\x01]"
 # XmlEncode
 ok {test-number} - encode( "[\x7F]" ) == "[\\x7F]" for: "[\x7F]" == "[\x7F]"
+# XmlWriter writes boolean attributes as true/false
+ok {test-number} - stream.str(), Contains(R"(attr1="true")") && Contains(R"(attr2="false")") for: "<?xml version="1.0" encoding="UTF-8"?> <Element1 attr1="true" attr2="false"/> " ( contains: "attr1="true"" and contains: "attr2="false"" )
 # analyse no analysis
 ok {test-number} - analysis.mean.point.count() == 23 for: 23.0 == 23
 # analyse no analysis
@@ -3592,7 +3614,7 @@ ok {test-number} - flag for: true
 # checkedElse
 ok {test-number} - testCheckedElse( true ) for: true
 # checkedElse, failing
-not ok {test-number} - flag for: false
+ok {test-number} - flag for: false  # TODO
 # checkedElse, failing
 not ok {test-number} - testCheckedElse( false ) for: false
 # checkedIf
@@ -3600,7 +3622,7 @@ ok {test-number} - flag for: true
 # checkedIf
 ok {test-number} - testCheckedIf( true ) for: true
 # checkedIf, failing
-not ok {test-number} - flag for: false
+ok {test-number} - flag for: false  # TODO
 # checkedIf, failing
 not ok {test-number} - testCheckedIf( false ) for: false
 # classify_outliers
@@ -3691,6 +3713,20 @@ ok {test-number} - long_var == unsigned_short_var for: 1 == 1
 ok {test-number} - long_var == unsigned_int_var for: 1 == 1
 # comparisons between int variables
 ok {test-number} - long_var == unsigned_long_var for: 1 == 1
+# convertToBits
+ok {test-number} - convertToBits( 0.f ) == 0 for: 0 == 0
+# convertToBits
+ok {test-number} - convertToBits( -0.f ) == ( 1ULL << 31 ) for: 2147483648 (0x<hex digits>) == 2147483648 (0x<hex digits>)
+# convertToBits
+ok {test-number} - convertToBits( 0. ) == 0 for: 0 == 0
+# convertToBits
+ok {test-number} - convertToBits( -0. ) == ( 1ULL << 63 ) for: 9223372036854775808 (0x<hex digits>) == 9223372036854775808 (0x<hex digits>)
+# convertToBits
+ok {test-number} - convertToBits( std::numeric_limits<float>::denorm_min() ) == 1 for: 1 == 1
+# convertToBits
+ok {test-number} - convertToBits( std::numeric_limits<double>::denorm_min() ) == 1 for: 1 == 1
+# empty tags are not allowed
+ok {test-number} - Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo)
 # erfc_inv
 ok {test-number} - erfc_inv(1.103560) == Approx(-0.09203687623843015) for: -0.0920368762 == Approx( -0.0920368762 )
 # erfc_inv
@@ -4028,6 +4064,10 @@ ok {test-number} - strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(
 ok {test-number} - strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 5 == 5
 # tables
 ok {test-number} - strlen(std::get<0>(data)) == static_cast<size_t>(std::get<1>(data)) for: 6 == 6
+# tags with dots in later positions are not parsed as hidden
+ok {test-number} - testcase.tags.size() == 1 for: 1 == 1
+# tags with dots in later positions are not parsed as hidden
+ok {test-number} - testcase.tags[0].original == "magic.tag"_catch_sr for: magic.tag == magic.tag
 # thrown std::strings are translated
 not ok {test-number} - unexpected exception with message: 'Why would you throw a std::string?'
 # toString on const wchar_t const pointer returns the string contents
@@ -4202,5 +4242,5 @@ ok {test-number} - q3 == 23. for: 23.0 == 23.0
 ok {test-number} -
 # xmlentitycheck
 ok {test-number} -
-1..2105
+1..2120
 
diff --git a/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt
index e64c984fb111c5e359fee2e29d9f93347fa02284..5291c85d220785e193c9451d31af23074960e59d 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/teamcity.sw.approved.txt
@@ -50,6 +50,10 @@ Tricky.tests.cpp:<line number>|nexplicit failure with message:|n  "1514"']
 ##teamcity[testFinished name='#1954 - 7 arg template test case sig compiles - 5, 1, 1, 1, 1, 0, 0' duration="{duration}"]
 ##teamcity[testStarted name='#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0']
 ##teamcity[testFinished name='#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0' duration="{duration}"]
+##teamcity[testStarted name='#2152 - ULP checks between differently signed values were wrong - double']
+##teamcity[testFinished name='#2152 - ULP checks between differently signed values were wrong - double' duration="{duration}"]
+##teamcity[testStarted name='#2152 - ULP checks between differently signed values were wrong - float']
+##teamcity[testFinished name='#2152 - ULP checks between differently signed values were wrong - float' duration="{duration}"]
 ##teamcity[testStarted name='#748 - captures with unexpected exceptions']
 Exception.tests.cpp:<line number>|nunexpected exception with messages:|n  "answer := 42"|n  "expected exception"- failure ignore as test marked as |'ok to fail|'|n']
 Exception.tests.cpp:<line number>|nunexpected exception with messages:|n  "answer := 42"|n  "expected exception"|n  REQUIRE_NOTHROW( thisThrows() )|nwith expansion:|n  thisThrows()|n- failure ignore as test marked as |'ok to fail|'|n']
@@ -266,8 +270,8 @@ Exception.tests.cpp:<line number>|nunexpected exception with message:|n  "unexpe
 ##teamcity[testStarted name='Composed matchers shortcircuit']
 ##teamcity[testFinished name='Composed matchers shortcircuit' duration="{duration}"]
 ##teamcity[testStarted name='Contains string matcher']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Contains("not there", Catch::CaseSensitive::No) )|nwith expansion:|n  "this string contains |'abc|' as a substring" contains: "not there" (case insensitive)|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Contains("STRING") )|nwith expansion:|n  "this string contains |'abc|' as a substring" contains: "STRING"|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Contains( "not there", Catch::CaseSensitive::No ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" contains: "not there" (case insensitive)|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Contains( "STRING" ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" contains: "STRING"|n']
 ##teamcity[testFinished name='Contains string matcher' duration="{duration}"]
 ##teamcity[testStarted name='Copy and then generate a range']
 ##teamcity[testFinished name='Copy and then generate a range' duration="{duration}"]
@@ -285,8 +289,8 @@ Exception.tests.cpp:<line number>|nunexpected exception with message:|n  "custom
 ##teamcity[testStarted name='Directly creating an EnumInfo']
 ##teamcity[testFinished name='Directly creating an EnumInfo' duration="{duration}"]
 ##teamcity[testStarted name='EndsWith string matcher']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), EndsWith("Substring") )|nwith expansion:|n  "this string contains |'abc|' as a substring" ends with: "Substring"|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No) )|nwith expansion:|n  "this string contains |'abc|' as a substring" ends with: "this" (case insensitive)|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), EndsWith( "Substring" ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" ends with: "Substring"|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" ends with: "this" (case insensitive)|n']
 ##teamcity[testFinished name='EndsWith string matcher' duration="{duration}"]
 ##teamcity[testStarted name='Enums can quickly have stringification enabled using REGISTER_ENUM']
 ##teamcity[testFinished name='Enums can quickly have stringification enabled using REGISTER_ENUM' duration="{duration}"]
@@ -314,18 +318,18 @@ Condition.tests.cpp:<line number>|nexpression failed|n  CHECK( x == Approx( 1.30
 ##teamcity[testStarted name='Equals']
 ##teamcity[testFinished name='Equals' duration="{duration}"]
 ##teamcity[testStarted name='Equals string matcher']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Equals("this string contains |'ABC|' as a substring") )|nwith expansion:|n  "this string contains |'abc|' as a substring" equals: "this string contains |'ABC|' as a substring"|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Equals("something else", Catch::CaseSensitive::No) )|nwith expansion:|n  "this string contains |'abc|' as a substring" equals: "something else" (case insensitive)|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Equals( "this string contains |'ABC|' as a substring" ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" equals: "this string contains |'ABC|' as a substring"|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" equals: "something else" (case insensitive)|n']
 ##teamcity[testFinished name='Equals string matcher' duration="{duration}"]
 ##teamcity[testStarted name='Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified']
 ##teamcity[testFinished name='Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified' duration="{duration}"]
 ##teamcity[testStarted name='Exception matchers that fail']
-Matchers.tests.cpp:<line number>|nno exception was thrown where one was expected|n  CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )|nwith expansion:|n  doesNotThrow(), SpecialException, ExceptionMatcher{1}|n']
-Matchers.tests.cpp:<line number>|nno exception was thrown where one was expected|n  REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{1} )|nwith expansion:|n  doesNotThrow(), SpecialException, ExceptionMatcher{1}|n']
-Matchers.tests.cpp:<line number>|nunexpected exception with message:|n  "Unknown exception"|n  CHECK_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )|nwith expansion:|n  throwsAsInt(1), SpecialException, ExceptionMatcher{1}|n']
-Matchers.tests.cpp:<line number>|nunexpected exception with message:|n  "Unknown exception"|n  REQUIRE_THROWS_MATCHES( throwsAsInt(1), SpecialException, ExceptionMatcher{1} )|nwith expansion:|n  throwsAsInt(1), SpecialException, ExceptionMatcher{1}|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THROWS_MATCHES( throwsSpecialException(3), SpecialException, ExceptionMatcher{1} )|nwith expansion:|n  SpecialException::what special exception has value of 1|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  REQUIRE_THROWS_MATCHES( throwsSpecialException(4), SpecialException, ExceptionMatcher{1} )|nwith expansion:|n  SpecialException::what special exception has value of 1|n']
+Matchers.tests.cpp:<line number>|nno exception was thrown where one was expected|n  CHECK_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )|nwith expansion:|n  doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }|n']
+Matchers.tests.cpp:<line number>|nno exception was thrown where one was expected|n  REQUIRE_THROWS_MATCHES( doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } )|nwith expansion:|n  doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }|n']
+Matchers.tests.cpp:<line number>|nunexpected exception with message:|n  "Unknown exception"|n  CHECK_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )|nwith expansion:|n  throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }|n']
+Matchers.tests.cpp:<line number>|nunexpected exception with message:|n  "Unknown exception"|n  REQUIRE_THROWS_MATCHES( throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } )|nwith expansion:|n  throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THROWS_MATCHES( throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 } )|nwith expansion:|n  SpecialException::what special exception has value of 1|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  REQUIRE_THROWS_MATCHES( throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 } )|nwith expansion:|n  SpecialException::what special exception has value of 1|n']
 ##teamcity[testFinished name='Exception matchers that fail' duration="{duration}"]
 ##teamcity[testStarted name='Exception matchers that succeed']
 ##teamcity[testFinished name='Exception matchers that succeed' duration="{duration}"]
@@ -395,12 +399,12 @@ Condition.tests.cpp:<line number>|nexpression failed|n  CHECK( data.str_hello.si
 ##teamcity[testStarted name='Matchers can be composed with both && and ||||']
 ##teamcity[testFinished name='Matchers can be composed with both && and ||||' duration="{duration}"]
 ##teamcity[testStarted name='Matchers can be composed with both && and |||| - failing']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), (Contains("string") |||| Contains("different")) && Contains("random") )|nwith expansion:|n  "this string contains |'abc|' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), ( Contains( "string" ) |||| Contains( "different" ) ) && Contains( "random" ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )|n']
 ##teamcity[testFinished name='Matchers can be composed with both && and |||| - failing' duration="{duration}"]
 ##teamcity[testStarted name='Matchers can be negated (Not) with the ! operator']
 ##teamcity[testFinished name='Matchers can be negated (Not) with the ! operator' duration="{duration}"]
 ##teamcity[testStarted name='Matchers can be negated (Not) with the ! operator - failing']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), !Contains("substring") )|nwith expansion:|n  "this string contains |'abc|' as a substring" not contains: "substring"|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), !Contains( "substring" ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" not contains: "substring"|n']
 ##teamcity[testFinished name='Matchers can be negated (Not) with the ! operator - failing' duration="{duration}"]
 ##teamcity[testStarted name='Mismatching exception messages failing the test']
 Exception.tests.cpp:<line number>|nexpression failed|n  REQUIRE_THROWS_WITH( thisThrows(), "should fail" )|nwith expansion:|n  "expected exception" equals: "should fail"|n']
@@ -469,9 +473,9 @@ Message.tests.cpp:<line number>|nexplicit failure with message:|n  "Message from
 Decomposition.tests.cpp:<line number>|nexpression failed|n  CHECK( truthy(false) )|nwith expansion:|n  Hey, its truthy!|n']
 ##teamcity[testFinished name='Reconstruction should be based on stringification: #914' duration="{duration}"]
 ##teamcity[testStarted name='Regex string matcher']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Matches("this STRING contains |'abc|' as a substring") )|nwith expansion:|n  "this string contains |'abc|' as a substring" matches "this STRING contains |'abc|' as a substring" case sensitively|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Matches("contains |'abc|' as a substring") )|nwith expansion:|n  "this string contains |'abc|' as a substring" matches "contains |'abc|' as a substring" case sensitively|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Matches("this string contains |'abc|' as a") )|nwith expansion:|n  "this string contains |'abc|' as a substring" matches "this string contains |'abc|' as a" case sensitively|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Matches( "this STRING contains |'abc|' as a substring" ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" matches "this STRING contains |'abc|' as a substring" case sensitively|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Matches( "contains |'abc|' as a substring" ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" matches "contains |'abc|' as a substring" case sensitively|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), Matches( "this string contains |'abc|' as a" ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" matches "this string contains |'abc|' as a" case sensitively|n']
 ##teamcity[testFinished name='Regex string matcher' duration="{duration}"]
 ##teamcity[testStarted name='Regression test #1']
 ##teamcity[testFinished name='Regression test #1' duration="{duration}"]
@@ -499,8 +503,8 @@ Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringFor
 ##teamcity[testStdOut name='Standard output from all sections is reported' out='Message from section one|nMessage from section two|n']
 ##teamcity[testFinished name='Standard output from all sections is reported' duration="{duration}"]
 ##teamcity[testStarted name='StartsWith string matcher']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), StartsWith("This String") )|nwith expansion:|n  "this string contains |'abc|' as a substring" starts with: "This String"|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No) )|nwith expansion:|n  "this string contains |'abc|' as a substring" starts with: "string" (case insensitive)|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), StartsWith( "This String" ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" starts with: "This String"|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No ) )|nwith expansion:|n  "this string contains |'abc|' as a substring" starts with: "string" (case insensitive)|n']
 ##teamcity[testFinished name='StartsWith string matcher' duration="{duration}"]
 ##teamcity[testStarted name='Static arrays are convertible to string']
 ##teamcity[testFinished name='Static arrays are convertible to string' duration="{duration}"]
@@ -510,6 +514,12 @@ Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( testStringFor
 ##teamcity[testFinished name='StringRef' duration="{duration}"]
 ##teamcity[testStarted name='StringRef at compilation time']
 ##teamcity[testFinished name='StringRef at compilation time' duration="{duration}"]
+##teamcity[testStarted name='Stringifying char arrays with statically known sizes - char']
+##teamcity[testFinished name='Stringifying char arrays with statically known sizes - char' duration="{duration}"]
+##teamcity[testStarted name='Stringifying char arrays with statically known sizes - signed char']
+##teamcity[testFinished name='Stringifying char arrays with statically known sizes - signed char' duration="{duration}"]
+##teamcity[testStarted name='Stringifying char arrays with statically known sizes - unsigned char']
+##teamcity[testFinished name='Stringifying char arrays with statically known sizes - unsigned char' duration="{duration}"]
 ##teamcity[testStarted name='Stringifying std::chrono::duration helpers']
 ##teamcity[testFinished name='Stringifying std::chrono::duration helpers' duration="{duration}"]
 ##teamcity[testStarted name='Stringifying std::chrono::duration with weird ratios']
@@ -563,6 +573,14 @@ Misc.tests.cpp:<line number>|nexpression failed|n  CHECK( s1 == s2 )|nwith expan
 ##teamcity[testFinished name='Test enum bit values' duration="{duration}"]
 ##teamcity[testStarted name='Test with special, characters "in name']
 ##teamcity[testFinished name='Test with special, characters "in name' duration="{duration}"]
+##teamcity[testStarted name='Testing checked-if']
+##teamcity[testFinished name='Testing checked-if' duration="{duration}"]
+##teamcity[testStarted name='Testing checked-if 2']
+Misc.tests.cpp:<line number>|nexplicit failure- failure ignore as test marked as |'ok to fail|'|n']
+##teamcity[testFinished name='Testing checked-if 2' duration="{duration}"]
+##teamcity[testStarted name='Testing checked-if 3']
+Misc.tests.cpp:<line number>|nexplicit failure- failure ignore as test marked as |'ok to fail|'|n']
+##teamcity[testFinished name='Testing checked-if 3' duration="{duration}"]
 ##teamcity[testStarted name='The NO_FAIL macro reports a failure but does not fail the test']
 ##teamcity[testFinished name='The NO_FAIL macro reports a failure but does not fail the test' duration="{duration}"]
 ##teamcity[testStarted name='The default listing implementation write to provided stream']
@@ -596,24 +614,24 @@ Exception.tests.cpp:<line number>|nunexpected exception with message:|n  "3.14"'
 ##teamcity[testStarted name='Vector Approx matcher']
 ##teamcity[testFinished name='Vector Approx matcher' duration="{duration}"]
 ##teamcity[testStarted name='Vector Approx matcher -- failing']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, Approx(t1) )|nwith expansion:|n  {  } is approx: { 1.0, 2.0 }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v1, Approx(v2) )|nwith expansion:|n  { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, Approx( t1 ) )|nwith expansion:|n  {  } is approx: { 1.0, 2.0 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v1, Approx( v2 ) )|nwith expansion:|n  { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }|n']
 ##teamcity[testFinished name='Vector Approx matcher -- failing' duration="{duration}"]
 ##teamcity[testStarted name='Vector matchers']
 ##teamcity[testFinished name='Vector matchers' duration="{duration}"]
 ##teamcity[testStarted name='Vector matchers that fail']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, VectorContains(-1) )|nwith expansion:|n  { 1, 2, 3 } Contains: -1|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, VectorContains(1) )|nwith expansion:|n  {  } Contains: 1|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, Contains(v) )|nwith expansion:|n  {  } Contains: { 1, 2, 3 }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, Contains(v2) )|nwith expansion:|n  { 1, 2, 3 } Contains: { 1, 2, 4 }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, Equals(v2) )|nwith expansion:|n  { 1, 2, 3 } Equals: { 1, 2 }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v2, Equals(v) )|nwith expansion:|n  { 1, 2 } Equals: { 1, 2, 3 }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, Equals(v) )|nwith expansion:|n  {  } Equals: { 1, 2, 3 }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, Equals(empty) )|nwith expansion:|n  { 1, 2, 3 } Equals: {  }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, UnorderedEquals(empty) )|nwith expansion:|n  { 1, 2, 3 } UnorderedEquals: {  }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, UnorderedEquals(v) )|nwith expansion:|n  {  } UnorderedEquals: { 1, 2, 3 }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( permuted, UnorderedEquals(v) )|nwith expansion:|n  { 1, 3 } UnorderedEquals: { 1, 2, 3 }|n']
-Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( permuted, UnorderedEquals(v) )|nwith expansion:|n  { 3, 1 } UnorderedEquals: { 1, 2, 3 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, VectorContains( -1 ) )|nwith expansion:|n  { 1, 2, 3 } Contains: -1|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, VectorContains( 1 ) )|nwith expansion:|n  {  } Contains: 1|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, Contains( v ) )|nwith expansion:|n  {  } Contains: { 1, 2, 3 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, Contains( v2 ) )|nwith expansion:|n  { 1, 2, 3 } Contains: { 1, 2, 4 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, Equals( v2 ) )|nwith expansion:|n  { 1, 2, 3 } Equals: { 1, 2 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v2, Equals( v ) )|nwith expansion:|n  { 1, 2 } Equals: { 1, 2, 3 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, Equals( v ) )|nwith expansion:|n  {  } Equals: { 1, 2, 3 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, Equals( empty ) )|nwith expansion:|n  { 1, 2, 3 } Equals: {  }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( v, UnorderedEquals( empty ) )|nwith expansion:|n  { 1, 2, 3 } UnorderedEquals: {  }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( empty, UnorderedEquals( v ) )|nwith expansion:|n  {  } UnorderedEquals: { 1, 2, 3 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( permuted, UnorderedEquals( v ) )|nwith expansion:|n  { 1, 3 } UnorderedEquals: { 1, 2, 3 }|n']
+Matchers.tests.cpp:<line number>|nexpression failed|n  CHECK_THAT( permuted, UnorderedEquals( v ) )|nwith expansion:|n  { 3, 1 } UnorderedEquals: { 1, 2, 3 }|n']
 ##teamcity[testFinished name='Vector matchers that fail' duration="{duration}"]
 ##teamcity[testStarted name='When checked exceptions are thrown they can be expected or unexpected']
 ##teamcity[testFinished name='When checked exceptions are thrown they can be expected or unexpected' duration="{duration}"]
@@ -648,6 +666,8 @@ Exception.tests.cpp:<line number>|nunexpected exception with message:|n  "unexpe
 ##teamcity[testFinished name='X/level/1/b' duration="{duration}"]
 ##teamcity[testStarted name='XmlEncode']
 ##teamcity[testFinished name='XmlEncode' duration="{duration}"]
+##teamcity[testStarted name='XmlWriter writes boolean attributes as true/false']
+##teamcity[testFinished name='XmlWriter writes boolean attributes as true/false' duration="{duration}"]
 ##teamcity[testStarted name='analyse no analysis']
 ##teamcity[testFinished name='analyse no analysis' duration="{duration}"]
 ##teamcity[testStarted name='array<int, N> -> toString']
@@ -661,13 +681,11 @@ Exception.tests.cpp:<line number>|nunexpected exception with message:|n  "unexpe
 ##teamcity[testStarted name='checkedElse']
 ##teamcity[testFinished name='checkedElse' duration="{duration}"]
 ##teamcity[testStarted name='checkedElse, failing']
-Misc.tests.cpp:<line number>|nexpression failed|n  CHECKED_ELSE( flag )|nwith expansion:|n  false|n']
 Misc.tests.cpp:<line number>|nexpression failed|n  REQUIRE( testCheckedElse( false ) )|nwith expansion:|n  false|n']
 ##teamcity[testFinished name='checkedElse, failing' duration="{duration}"]
 ##teamcity[testStarted name='checkedIf']
 ##teamcity[testFinished name='checkedIf' duration="{duration}"]
 ##teamcity[testStarted name='checkedIf, failing']
-Misc.tests.cpp:<line number>|nexpression failed|n  CHECKED_IF( flag )|nwith expansion:|n  false|n']
 Misc.tests.cpp:<line number>|nexpression failed|n  REQUIRE( testCheckedIf( false ) )|nwith expansion:|n  false|n']
 ##teamcity[testFinished name='checkedIf, failing' duration="{duration}"]
 ##teamcity[testStarted name='classify_outliers']
@@ -676,6 +694,10 @@ Misc.tests.cpp:<line number>|nexpression failed|n  REQUIRE( testCheckedIf( false
 ##teamcity[testFinished name='comparisons between const int variables' duration="{duration}"]
 ##teamcity[testStarted name='comparisons between int variables']
 ##teamcity[testFinished name='comparisons between int variables' duration="{duration}"]
+##teamcity[testStarted name='convertToBits']
+##teamcity[testFinished name='convertToBits' duration="{duration}"]
+##teamcity[testStarted name='empty tags are not allowed']
+##teamcity[testFinished name='empty tags are not allowed' duration="{duration}"]
 ##teamcity[testStarted name='erfc_inv']
 ##teamcity[testFinished name='erfc_inv' duration="{duration}"]
 ##teamcity[testStarted name='estimate_clock_resolution']
@@ -816,6 +838,8 @@ Tricky.tests.cpp:<line number>|nexpression failed|n  REQUIRE( std::string( "firs
 ##teamcity[testFinished name='strlen3' duration="{duration}"]
 ##teamcity[testStarted name='tables']
 ##teamcity[testFinished name='tables' duration="{duration}"]
+##teamcity[testStarted name='tags with dots in later positions are not parsed as hidden']
+##teamcity[testFinished name='tags with dots in later positions are not parsed as hidden' duration="{duration}"]
 ##teamcity[testStarted name='thrown std::strings are translated']
 Exception.tests.cpp:<line number>|nunexpected exception with message:|n  "Why would you throw a std::string?"']
 ##teamcity[testFinished name='thrown std::strings are translated' duration="{duration}"]
diff --git a/packages/Catch2/tests/SelfTest/Baselines/xml.sw.approved.txt b/packages/Catch2/tests/SelfTest/Baselines/xml.sw.approved.txt
index ceaa32bcb8df86d901dbe890b1f283e5ad962dcc..bd12e70a30d509245ac9648840c46028867f74ee 100644
--- a/packages/Catch2/tests/SelfTest/Baselines/xml.sw.approved.txt
+++ b/packages/Catch2/tests/SelfTest/Baselines/xml.sw.approved.txt
@@ -1,247 +1,2700 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<Catch name="<exe-name>" filters="~[!nonportable]~[!benchmark]~[approvals] *">
+<Catch2TestRun name="<exe-name>" filters="~[!nonportable]~[!benchmark]~[approvals] *">
   <Randomness seed="1"/>
-  <Group name="<exe-name>">
-    <TestCase name="# A test name that starts with a #" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" tags="[Decomposition]" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
+  <TestCase name="# A test name that starts with a #" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1005: Comparing pointer to int and long (NULL can be either on various systems)" tags="[Decomposition]" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
+      <Original>
+        fptr == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
+      <Original>
+        fptr == 0l
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1027: Bitfields can be captured" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        y.v == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        0 == y.v
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1147" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        t1 == t2
+      </Original>
+      <Expanded>
+        {?} == {?}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        t1 != t2
+      </Original>
+      <Expanded>
+        {?} != {?}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        t1 &lt; t2
+      </Original>
+      <Expanded>
+        {?} &lt; {?}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        t1 > t2
+      </Original>
+      <Expanded>
+        {?} > {?}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        t1 &lt;= t2
+      </Original>
+      <Expanded>
+        {?} &lt;= {?}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        t1 >= t2
+      </Original>
+      <Expanded>
+        {?} >= {?}
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1175 - Hidden Test" tags="[.]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1238" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Info>
+      uarr := "123"
+    </Info>
+    <Info>
+      sarr := "456"
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        std::memcmp(uarr, "123", sizeof(uarr)) == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Info>
+      uarr := "123"
+    </Info>
+    <Info>
+      sarr := "456"
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        std::memcmp(sarr, "456", sizeof(sarr)) == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1245" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1319: Sections can have description (even if it is not saved" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Section name="SectionName" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1403" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        h1 == h2
+      </Original>
+      <Expanded>
+        [1403 helper] == [1403 helper]
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1455 - INFO and WARN can start with a linebreak" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+
+This info message starts with a linebreak
+    </Info>
+    <Warning>
+
+This warning message starts with a linebreak
+    </Warning>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="#1514: stderr/stdout is not captured in tests aborted by an exception" tags="[.][output-capture][regression]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Failure filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      1514
+    </Failure>
+    <OverallResult success="false">
+      <StdOut>
+This would not be caught previously
+      </StdOut>
+      <StdErr>
+Nor would this
+      </StdErr>
+    </OverallResult>
+  </TestCase>
+  <TestCase name="#1548" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        std::is_same&lt;TypeList&lt;int>, TypeList&lt;int>>::value
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1905 -- test spec parser properly clears internal state between compound tests" tags="[command-line][test-spec]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Original>
+        spec.matches(*fakeTestCase("spec . char"))
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Original>
+        spec.matches(*fakeTestCase("spec , char"))
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Original>
+        !(spec.matches(*fakeTestCase(R"(spec \, char)")))
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1912 -- test spec parser handles escaping" tags="[command-line][test-spec]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+    <Section name="Various parentheses" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          fptr == 0
+          spec.matches(*fakeTestCase(R"(spec {a} char)"))
         </Original>
         <Expanded>
-          0 == 0
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          fptr == 0l
+          spec.matches(*fakeTestCase(R"(spec [a] char)"))
         </Original>
         <Expanded>
-          0 == 0
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1027: Bitfields can be captured" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          y.v == 0
+          !(spec.matches(*fakeTestCase("differs but has similar tag", "[a]")))
         </Original>
         <Expanded>
-          0 == 0
+          !false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="backslash in test name" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches(*fakeTestCase(R"(spec \ char)"))
+        </Original>
+        <Expanded>
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1913 - GENERATE inside a for loop should not keep recreating the generator" tags="[generators][regression]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        counter &lt; 7
+      </Original>
+      <Expanded>
+        3 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        counter &lt; 7
+      </Original>
+      <Expanded>
+        6 &lt; 7
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1913 - GENERATEs can share a line" tags="[generators][regression]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        i != j
+      </Original>
+      <Expanded>
+        1 != 3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        i != j
+      </Original>
+      <Expanded>
+        1 != 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        i != j
+      </Original>
+      <Expanded>
+        2 != 3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        i != j
+      </Original>
+      <Expanded>
+        2 != 4
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1938 - GENERATE after a section" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+    <Section name="A" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          0 == y.v
+          m
         </Original>
         <Expanded>
-          0 == 0
+          1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1147" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          t1 == t2
+          m
         </Original>
         <Expanded>
-          {?} == {?}
+          2
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          t1 != t2
+          m
         </Original>
         <Expanded>
-          {?} != {?}
+          3
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1938 - Section followed by flat generate" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+    <Section name="A" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          t1 &lt; t2
+          1
         </Original>
         <Expanded>
-          {?} &lt; {?}
+          1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        3
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1938 - flat generate" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        3
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1938 - mixed sections and generates" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+    <Section name="A" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Info>
+      i := 1
+    </Info>
+    <Info>
+      j := 3
+    </Info>
+    <Info>
+      k := 5
+    </Info>
+    <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Info>
+      i := 1
+    </Info>
+    <Info>
+      j := 3
+    </Info>
+    <Info>
+      k := 6
+    </Info>
+    <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Info>
+      i := 1
+    </Info>
+    <Info>
+      j := 4
+    </Info>
+    <Info>
+      k := 5
+    </Info>
+    <Info>
+      i := 1
+    </Info>
+    <Info>
+      j := 4
+    </Info>
+    <Info>
+      k := 6
+    </Info>
+    <Section name="A" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Info>
+      i := 2
+    </Info>
+    <Info>
+      j := 3
+    </Info>
+    <Info>
+      k := 5
+    </Info>
+    <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Info>
+      i := 2
+    </Info>
+    <Info>
+      j := 3
+    </Info>
+    <Info>
+      k := 6
+    </Info>
+    <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Info>
+      i := 2
+    </Info>
+    <Info>
+      j := 4
+    </Info>
+    <Info>
+      k := 5
+    </Info>
+    <Info>
+      i := 2
+    </Info>
+    <Info>
+      j := 4
+    </Info>
+    <Info>
+      k := 6
+    </Info>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1938 - nested generate" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        n
+      </Original>
+      <Expanded>
+        1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        n
+      </Original>
+      <Expanded>
+        2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        n
+      </Original>
+      <Expanded>
+        3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        n
+      </Original>
+      <Expanded>
+        1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        n
+      </Original>
+      <Expanded>
+        2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        n
+      </Original>
+      <Expanded>
+        3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        n
+      </Original>
+      <Expanded>
+        1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        n
+      </Original>
+      <Expanded>
+        2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        m
+      </Original>
+      <Expanded>
+        3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        n
+      </Original>
+      <Expanded>
+        3
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1954 - 7 arg template test case sig compiles - 1, 1, 1, 1, 1, 0, 0" tags="[.][compilation][regression]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1954 - 7 arg template test case sig compiles - 5, 1, 1, 1, 1, 0, 0" tags="[.][compilation][regression]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0" tags="[.][compilation][regression]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#2152 - ULP checks between differently signed values were wrong - double" tags="[floating-point][matchers][ulp]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        smallest_non_zero, WithinULP( -smallest_non_zero, 2 )
+      </Original>
+      <Expanded>
+        0.0 is within 2 ULPs of -4.9406564584124654e-324 ([-1.4821969375237396e-323, 4.9406564584124654e-324])
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        smallest_non_zero, !WithinULP( -smallest_non_zero, 1 )
+      </Original>
+      <Expanded>
+        0.0 not is within 1 ULPs of -4.9406564584124654e-324 ([-9.8813129168249309e-324, -0.0000000000000000e+00])
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#2152 - ULP checks between differently signed values were wrong - float" tags="[floating-point][matchers][ulp]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        smallest_non_zero, WithinULP( -smallest_non_zero, 2 )
+      </Original>
+      <Expanded>
+        0.0f is within 2 ULPs of -1.40129846e-45f ([-4.20389539e-45, 1.40129846e-45])
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        smallest_non_zero, !WithinULP( -smallest_non_zero, 1 )
+      </Original>
+      <Expanded>
+        0.0f not is within 1 ULPs of -1.40129846e-45f ([-2.80259693e-45, -0.00000000e+00])
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#748 - captures with unexpected exceptions" tags="[!shouldfail][!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Section name="outside assertions" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Info>
+        answer := 42
+      </Info>
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        expected exception
+      </Exception>
+      <OverallResults successes="0" failures="0" expectedFailures="1"/>
+    </Section>
+    <Section name="inside REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Info>
+        answer := 42
+      </Info>
+      <Expression success="false" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
         <Original>
-          t1 > t2
+          thisThrows()
         </Original>
         <Expanded>
-          {?} > {?}
+          thisThrows()
         </Expanded>
+        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+          expected exception
+        </Exception>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="0" failures="0" expectedFailures="1"/>
+    </Section>
+    <Section name="inside REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Info>
+        answer := 42
+      </Info>
+      <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
         <Original>
-          t1 &lt;= t2
+          thisThrows()
         </Original>
         <Expanded>
-          {?} &lt;= {?}
+          thisThrows()
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#809" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        42 == f
+      </Original>
+      <Expanded>
+        42 == {?}
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#833" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        a == t
+      </Original>
+      <Expanded>
+        3 == 3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        a == t
+      </Original>
+      <Expanded>
+        3 == 3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        throws_int(true)
+      </Original>
+      <Expanded>
+        throws_int(true)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THROWS_AS" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        throws_int(true), int
+      </Original>
+      <Expanded>
+        throws_int(true), int
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        throws_int(false)
+      </Original>
+      <Expanded>
+        throws_int(false)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        "aaa", Catch::Matchers::EndsWith("aaa")
+      </Original>
+      <Expanded>
+        "aaa" ends with: "aaa"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        templated_tests&lt;int>(3)
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#835 -- errno should not be touched by Catch" tags="[!shouldfail][.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        f() == 0
+      </Original>
+      <Expanded>
+        1 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        errno == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#872" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Info>
+      dummy := 0
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        x == 4
+      </Original>
+      <Expanded>
+        {?} == 4
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="#961 -- Dynamically created sections should all be reported" tags="[.]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Section name="Looped section 0" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Looped section 1" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Looped section 2" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Looped section 3" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Looped section 4" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="'Not' checks that should fail" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        false != false
+      </Original>
+      <Expanded>
+        false != false
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        true != true
+      </Original>
+      <Expanded>
+        true != true
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !true
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !(true)
+      </Original>
+      <Expanded>
+        !true
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !trueValue
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !(trueValue)
+      </Original>
+      <Expanded>
+        !true
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !(1 == 1)
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !(1 == 1)
+      </Original>
+      <Expanded>
+        !(1 == 1)
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="'Not' checks that should succeed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        false == false
+      </Original>
+      <Expanded>
+        false == false
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        true == true
+      </Original>
+      <Expanded>
+        true == true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !false
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !(false)
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !falseValue
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !(falseValue)
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !(1 == 2)
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        !(1 == 2)
+      </Original>
+      <Expanded>
+        !(1 == 2)
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="(unimplemented) static bools can be evaluated" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Section name="compare to true" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
         <Original>
-          t1 >= t2
+          is_true&lt;true>::value == true
         </Original>
         <Expanded>
-          {?} >= {?}
+          true == true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1175 - Hidden Test" tags="[.]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1238" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Info>
-        uarr := "123"
-      </Info>
-      <Info>
-        sarr := "456"
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
         <Original>
-          std::memcmp(uarr, "123", sizeof(uarr)) == 0
+          true == is_true&lt;true>::value
         </Original>
         <Expanded>
-          0 == 0
+          true == true
         </Expanded>
       </Expression>
-      <Info>
-        uarr := "123"
-      </Info>
-      <Info>
-        sarr := "456"
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="compare to false" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
         <Original>
-          std::memcmp(sarr, "456", sizeof(sarr)) == 0
+          is_true&lt;false>::value == false
         </Original>
         <Expanded>
-          0 == 0
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1245" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1319: Sections can have description (even if it is not saved" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Section name="SectionName" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1403" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
         <Original>
-          h1 == h2
+          false == is_true&lt;false>::value
         </Original>
         <Expanded>
-          [1403 helper] == [1403 helper]
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1455 - INFO and WARN can start with a linebreak" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-
-This info message starts with a linebreak
-      </Info>
-      <Warning>
-
-This warning message starts with a linebreak
-      </Warning>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="#1514: stderr/stdout is not captured in tests aborted by an exception" tags="[.][output-capture][regression]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Failure filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        1514
-      </Failure>
-      <OverallResult success="false">
-        <StdOut>
-This would not be caught previously
-        </StdOut>
-        <StdErr>
-Nor would this
-        </StdErr>
-      </OverallResult>
-    </TestCase>
-    <TestCase name="#1548" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="negation" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
         <Original>
-          std::is_same&lt;TypeList&lt;int>, TypeList&lt;int>>::value
+          !is_true&lt;false>::value
         </Original>
         <Expanded>
           true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1905 -- test spec parser properly clears internal state between compound tests" tags="[command-line][test-spec]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="double negation" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
         <Original>
-          spec.matches(*fakeTestCase("spec . char"))
+          !!is_true&lt;true>::value
         </Original>
         <Expanded>
           true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="direct" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
         <Original>
-          spec.matches(*fakeTestCase("spec , char"))
+          is_true&lt;true>::value
         </Original>
         <Expanded>
           true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
         <Original>
-          !(spec.matches(*fakeTestCase(R"(spec \, char)")))
+          !(is_true&lt;false>::value)
         </Original>
         <Expanded>
           !false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1912 -- test spec parser handles escaping" tags="[command-line][test-spec]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-      <Section name="Various parentheses" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="3x3x3 ints" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        1 &lt; 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        4 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        1 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        1 &lt; 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        4 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        1 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        1 &lt; 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        4 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        1 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        1 &lt; 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        5 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        1 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        1 &lt; 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        5 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        1 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        1 &lt; 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        5 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        1 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        1 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        6 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        1 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        1 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        6 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        1 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        1 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        6 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        1 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        2 &lt; 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        4 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        2 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        2 &lt; 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        4 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        2 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        2 &lt; 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        4 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        2 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        2 &lt; 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        5 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        2 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        2 &lt; 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        5 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        2 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        2 &lt; 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        5 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        2 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        2 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        6 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        2 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        2 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        6 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        2 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        2 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        6 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        2 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        3 &lt; 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        4 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        3 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        3 &lt; 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        4 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        3 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        3 &lt; 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        4 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        3 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        3 &lt; 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        5 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        3 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        3 &lt; 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        5 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        3 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        3 &lt; 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        5 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        3 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        3 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        6 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        3 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        3 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        6 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        3 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; y
+      </Original>
+      <Expanded>
+        3 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        y &lt; z
+      </Original>
+      <Expanded>
+        6 &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        x &lt; z
+      </Original>
+      <Expanded>
+        3 &lt; 9
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A METHOD_AS_TEST_CASE based test run that fails" tags="[.][class][failing]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        s == "world"
+      </Original>
+      <Expanded>
+        "hello" == "world"
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A METHOD_AS_TEST_CASE based test run that succeeds" tags="[class]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        s == "hello"
+      </Original>
+      <Expanded>
+        "hello" == "hello"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that fails - Template_Foo&lt;float>" tags="[.][class][failing][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>::m_a.size() == 1
+      </Original>
+      <Expanded>
+        0 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that fails - Template_Foo&lt;int>" tags="[.][class][failing][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>::m_a.size() == 1
+      </Original>
+      <Expanded>
+        0 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that fails - std::vector&lt;float>" tags="[.][class][failing][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>::m_a.size() == 1
+      </Original>
+      <Expanded>
+        0 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that fails - std::vector&lt;int>" tags="[.][class][failing][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>::m_a.size() == 1
+      </Original>
+      <Expanded>
+        0 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that succeeds - Template_Foo&lt;float>" tags="[class][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>::m_a.size() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that succeeds - Template_Foo&lt;int>" tags="[class][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>::m_a.size() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that succeeds - std::vector&lt;float>" tags="[class][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>::m_a.size() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that succeeds - std::vector&lt;int>" tags="[class][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>::m_a.size() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that fails - Template_Foo_2&lt;float, 6>" tags="[.][class][failing][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>{}.m_a.size() &lt; 2
+      </Original>
+      <Expanded>
+        6 &lt; 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that fails - Template_Foo_2&lt;int, 2>" tags="[.][class][failing][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>{}.m_a.size() &lt; 2
+      </Original>
+      <Expanded>
+        2 &lt; 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that fails - std::array&lt;float, 6>" tags="[.][class][failing][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>{}.m_a.size() &lt; 2
+      </Original>
+      <Expanded>
+        6 &lt; 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that fails - std::array&lt;int, 2>" tags="[.][class][failing][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>{}.m_a.size() &lt; 2
+      </Original>
+      <Expanded>
+        2 &lt; 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds - Template_Foo_2&lt;float,6>" tags="[class][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>{}.m_a.size() >= 2
+      </Original>
+      <Expanded>
+        6 >= 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds - Template_Foo_2&lt;int,2>" tags="[class][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>{}.m_a.size() >= 2
+      </Original>
+      <Expanded>
+        2 >= 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds - std::array&lt;float,6>" tags="[class][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>{}.m_a.size() >= 2
+      </Original>
+      <Expanded>
+        6 >= 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds - std::array&lt;int,2>" tags="[class][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture_2&lt;TestType>{}.m_a.size() >= 2
+      </Original>
+      <Expanded>
+        2 >= 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that fails - double" tags="[.][class][failing][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture&lt;TestType>::m_a == 2
+      </Original>
+      <Expanded>
+        1.0 == 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that fails - float" tags="[.][class][failing][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture&lt;TestType>::m_a == 2
+      </Original>
+      <Expanded>
+        1.0f == 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that fails - int" tags="[.][class][failing][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture&lt;TestType>::m_a == 2
+      </Original>
+      <Expanded>
+        1 == 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that succeeds - double" tags="[class][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture&lt;TestType>::m_a == 1
+      </Original>
+      <Expanded>
+        1.0 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that succeeds - float" tags="[class][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture&lt;TestType>::m_a == 1
+      </Original>
+      <Expanded>
+        1.0f == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that succeeds - int" tags="[class][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture&lt;TestType>::m_a == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that fails - 1" tags="[.][class][failing][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Nttp_Fixture&lt;V>::value == 0
+      </Original>
+      <Expanded>
+        1 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that fails - 3" tags="[.][class][failing][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Nttp_Fixture&lt;V>::value == 0
+      </Original>
+      <Expanded>
+        3 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that fails - 6" tags="[.][class][failing][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Nttp_Fixture&lt;V>::value == 0
+      </Original>
+      <Expanded>
+        6 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds - 1" tags="[class][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Nttp_Fixture&lt;V>::value > 0
+      </Original>
+      <Expanded>
+        1 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds - 3" tags="[class][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Nttp_Fixture&lt;V>::value > 0
+      </Original>
+      <Expanded>
+        3 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds - 6" tags="[class][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Nttp_Fixture&lt;V>::value > 0
+      </Original>
+      <Expanded>
+        6 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A TEST_CASE_METHOD based test run that fails" tags="[.][class][failing]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        m_a == 2
+      </Original>
+      <Expanded>
+        1 == 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A TEST_CASE_METHOD based test run that succeeds" tags="[class]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        m_a == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A Template product test case - Foo&lt;float>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        x.size() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A Template product test case - Foo&lt;int>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        x.size() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A Template product test case - std::vector&lt;float>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        x.size() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A Template product test case - std::vector&lt;int>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        x.size() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A Template product test case with array signature - Bar&lt;float, 42>" tags="[nttp][product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        x.size() > 0
+      </Original>
+      <Expanded>
+        42 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A Template product test case with array signature - Bar&lt;int, 9>" tags="[nttp][product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        x.size() > 0
+      </Original>
+      <Expanded>
+        9 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A Template product test case with array signature - std::array&lt;float, 42>" tags="[nttp][product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        x.size() > 0
+      </Original>
+      <Expanded>
+        42 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A Template product test case with array signature - std::array&lt;int, 9>" tags="[nttp][product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        x.size() > 0
+      </Original>
+      <Expanded>
+        9 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A comparison that uses literals instead of the normal constructor" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d == 1.23_a
+      </Original>
+      <Expanded>
+        1.23 == Approx( 1.23 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d != 1.22_a
+      </Original>
+      <Expanded>
+        1.23 != Approx( 1.22 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        -d == -1.23_a
+      </Original>
+      <Expanded>
+        -1.23 == Approx( -1.23 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d == 1.2_a .epsilon(.1)
+      </Original>
+      <Expanded>
+        1.23 == Approx( 1.2 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d != 1.2_a .epsilon(.001)
+      </Original>
+      <Expanded>
+        1.23 != Approx( 1.2 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d == 1_a .epsilon(.3)
+      </Original>
+      <Expanded>
+        1.23 == Approx( 1.0 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="A couple of nested sections followed by a failure" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Section name="Outer" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Section name="Inner" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Failure filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      to infinity and beyond
+    </Failure>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="A failing expression with a non streamable type is still captured" tags="[.][failing][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        &amp;o1 == &amp;o2
+      </Original>
+      <Expanded>
+        0x<hex digits> == 0x<hex digits>
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        o1 == o2
+      </Original>
+      <Expanded>
+        {?} == {?}
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Absolute margin" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        104.0 != Approx(100.0)
+      </Original>
+      <Expanded>
+        104.0 != Approx( 100.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        104.0 == Approx(100.0).margin(5)
+      </Original>
+      <Expanded>
+        104.0 == Approx( 100.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        104.0 == Approx(100.0).margin(4)
+      </Original>
+      <Expanded>
+        104.0 == Approx( 100.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        104.0 != Approx(100.0).margin(3)
+      </Original>
+      <Expanded>
+        104.0 != Approx( 100.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        100.3 != Approx(100.0)
+      </Original>
+      <Expanded>
+        100.3 != Approx( 100.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        100.3 == Approx(100.0).margin(0.5)
+      </Original>
+      <Expanded>
+        100.3 == Approx( 100.0 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="An empty test with no assertions" tags="[empty]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="An expression with side-effects should only be evaluated once" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        i++ == 7
+      </Original>
+      <Expanded>
+        7 == 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        i++ == 8
+      </Original>
+      <Expanded>
+        8 == 8
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="An unchecked exception reports the line of the last assertion" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        1 == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="false" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        {Unknown expression after the reported line}
+      </Original>
+      <Expanded>
+        {Unknown expression after the reported line}
+      </Expanded>
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        unexpected exception
+      </Exception>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Anonymous test case 1" filename="tests/<exe-name>/UsageTests/VariadicMacros.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Approx setters validate their arguments" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(0).margin(0)
+      </Original>
+      <Expanded>
+        Approx(0).margin(0)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(0).margin(1234656)
+      </Original>
+      <Expanded>
+        Approx(0).margin(1234656)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(0).margin(-2), std::domain_error
+      </Original>
+      <Expanded>
+        Approx(0).margin(-2), std::domain_error
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(0).epsilon(0)
+      </Original>
+      <Expanded>
+        Approx(0).epsilon(0)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(0).epsilon(1)
+      </Original>
+      <Expanded>
+        Approx(0).epsilon(1)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(0).epsilon(-0.001), std::domain_error
+      </Original>
+      <Expanded>
+        Approx(0).epsilon(-0.001), std::domain_error
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(0).epsilon(1.0001), std::domain_error
+      </Original>
+      <Expanded>
+        Approx(0).epsilon(1.0001), std::domain_error
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Approx with exactly-representable margin" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        0.25f == Approx(0.0f).margin(0.25f)
+      </Original>
+      <Expanded>
+        0.25f == Approx( 0.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        0.0f == Approx(0.25f).margin(0.25f)
+      </Original>
+      <Expanded>
+        0.0f == Approx( 0.25 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        0.5f == Approx(0.25f).margin(0.25f)
+      </Original>
+      <Expanded>
+        0.5f == Approx( 0.25 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        245.0f == Approx(245.25f).margin(0.25f)
+      </Original>
+      <Expanded>
+        245.0f == Approx( 245.25 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        245.5f == Approx(245.25f).margin(0.25f)
+      </Original>
+      <Expanded>
+        245.5f == Approx( 245.25 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Approximate PI" tags="[Approx][PI]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 )
+      </Original>
+      <Expanded>
+        3.1428571429 == Approx( 3.141 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 )
+      </Original>
+      <Expanded>
+        3.1428571429 != Approx( 3.141 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Approximate comparisons with different epsilons" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d != Approx( 1.231 )
+      </Original>
+      <Expanded>
+        1.23 != Approx( 1.231 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d == Approx( 1.231 ).epsilon( 0.1 )
+      </Original>
+      <Expanded>
+        1.23 == Approx( 1.231 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Approximate comparisons with floats" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        1.23f == Approx( 1.23f )
+      </Original>
+      <Expanded>
+        1.23f == Approx( 1.2300000191 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        0.0f == Approx( 0.0f )
+      </Original>
+      <Expanded>
+        0.0f == Approx( 0.0 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Approximate comparisons with ints" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        1 == Approx( 1 )
+      </Original>
+      <Expanded>
+        1 == Approx( 1.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        0 == Approx( 0 )
+      </Original>
+      <Expanded>
+        0 == Approx( 0.0 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Approximate comparisons with mixed numeric types" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        1.0f == Approx( 1 )
+      </Original>
+      <Expanded>
+        1.0f == Approx( 1.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        0 == Approx( dZero)
+      </Original>
+      <Expanded>
+        0 == Approx( 0.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        0 == Approx( dSmall ).margin( 0.001 )
+      </Original>
+      <Expanded>
+        0 == Approx( 0.00001 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        1.234f == Approx( dMedium )
+      </Original>
+      <Expanded>
+        1.234f == Approx( 1.234 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        dMedium == Approx( 1.234f )
+      </Original>
+      <Expanded>
+        1.234 == Approx( 1.2339999676 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Arbitrary predicate matcher" tags="[generic][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="Function pointer" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Original>
+          1, Predicate&lt;int>( alwaysTrue, "always true" )
+        </Original>
+        <Expanded>
+          1 matches predicate: "always true"
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Original>
+          1, !Predicate&lt;int>( alwaysFalse, "always false" )
+        </Original>
+        <Expanded>
+          1 not matches predicate: "always false"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Lambdas + different type" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Original>
+          "Hello olleH", Predicate&lt;std::string>( []( std::string const&amp; str ) -> bool { return str.front() == str.back(); }, "First and last character should be equal" )
+        </Original>
+        <Expanded>
+          "Hello olleH" matches predicate: "First and last character should be equal"
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Original>
+          "This wouldn't pass", !Predicate&lt;std::string>( []( std::string const&amp; str ) -> bool { return str.front() == str.back(); } )
+        </Original>
+        <Expanded>
+          "This wouldn't pass" not matches undescribed predicate
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Assertion macros support bit operators and bool conversions" tags="[bitops][compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        lhs | rhs
+      </Original>
+      <Expanded>
+        Val: 1 | Val: 2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        !(lhs &amp; rhs)
+      </Original>
+      <Expanded>
+        !(Val: 1 &amp; Val: 2)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        HasBitOperators{ 1 } &amp; HasBitOperators{ 1 }
+      </Original>
+      <Expanded>
+        Val: 1 &amp; Val: 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        lhs ^ rhs
+      </Original>
+      <Expanded>
+        Val: 1 ^ Val: 2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        !(lhs ^ lhs)
+      </Original>
+      <Expanded>
+        !(Val: 1 ^ Val: 1)
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Assertions then sections" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Section name="A section" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+        <Original>
+          true
+        </Original>
+        <Expanded>
+          true
+        </Expanded>
+      </Expression>
+      <Section name="Another section" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
           <Original>
-            spec.matches(*fakeTestCase(R"(spec {a} char)"))
-          </Original>
-          <Expanded>
             true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches(*fakeTestCase(R"(spec [a] char)"))
           </Original>
           <Expanded>
             true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            !(spec.matches(*fakeTestCase("differs but has similar tag", "[a]")))
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="backslash in test name" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Section name="A section" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+        <Original>
+          true
+        </Original>
+        <Expanded>
+          true
+        </Expanded>
+      </Expression>
+      <Section name="Another other section" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
           <Original>
-            spec.matches(*fakeTestCase(R"(spec \ char)"))
+            true
           </Original>
           <Expanded>
             true
@@ -249,19386 +2702,17080 @@ Nor would this
         </Expression>
         <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1913 - GENERATE inside a for loop should not keep recreating the generator" tags="[generators][regression]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Basic use of the Contains range matcher" tags="[contains][matchers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+    <Section name="Different argument ranges, same element type, default comparison" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          counter &lt; 7
+          a, Contains(1)
         </Original>
         <Expanded>
-          3 &lt; 7
+          { 1, 2, 3 } contains element 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          counter &lt; 7
+          b, Contains(1)
         </Original>
         <Expanded>
-          6 &lt; 7
+          { 0, 1, 2 } contains element 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1913 - GENERATEs can share a line" tags="[generators][regression]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          i != j
+          c, !Contains(1)
         </Original>
         <Expanded>
-          1 != 3
+          { 4, 5, 6 } not contains element 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Different argument ranges, same element type, custom comparison" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          i != j
+          a, Contains(0, close_enough)
         </Original>
         <Expanded>
-          1 != 4
+          { 1, 2, 3 } contains element 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          i != j
+          b, Contains(0, close_enough)
         </Original>
         <Expanded>
-          2 != 3
+          { 0, 1, 2 } contains element 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          i != j
+          c, !Contains(0, close_enough)
         </Original>
         <Expanded>
-          2 != 4
+          { 4, 5, 6 } not contains element 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1938 - GENERATE after a section" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-      <Section name="A" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-          <Original>
-            m
-          </Original>
-          <Expanded>
-            1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-          <Original>
-            m
-          </Original>
-          <Expanded>
-            2
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-          <Original>
-            m
-          </Original>
-          <Expanded>
-            3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1938 - Section followed by flat generate" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-      <Section name="A" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-          <Original>
-            1
-          </Original>
-          <Expanded>
-            1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Different element type, custom comparisons" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          a, Contains(4, [](auto&amp;&amp; lhs, size_t sz) { return lhs.size() == sz; })
         </Original>
         <Expanded>
-          2
+          { "abc", "abcd", "abcde" } contains element 4
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Can handle type that requires ADL-found free function begin and end" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          in, Contains(1)
         </Original>
         <Expanded>
-          3
+          { 1, 2, 3, 4, 5 } contains element 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1938 - flat generate" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          in, !Contains(8)
         </Original>
         <Expanded>
-          1
+          { 1, 2, 3, 4, 5 } not contains element 8
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Initialization with move only types" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          in, Contains(MoveOnlyTestElement{ 2 })
         </Original>
         <Expanded>
-          2
+          { 1, 2, 3 } contains element 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          in, !Contains(MoveOnlyTestElement{ 9 })
         </Original>
         <Expanded>
-          3
+          { 1, 2, 3 } not contains element 9
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1938 - mixed sections and generates" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-      <Section name="A" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Info>
-        i := 1
-      </Info>
-      <Info>
-        j := 3
-      </Info>
-      <Info>
-        k := 5
-      </Info>
-      <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Info>
-        i := 1
-      </Info>
-      <Info>
-        j := 3
-      </Info>
-      <Info>
-        k := 6
-      </Info>
-      <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Info>
-        i := 1
-      </Info>
-      <Info>
-        j := 4
-      </Info>
-      <Info>
-        k := 5
-      </Info>
-      <Info>
-        i := 1
-      </Info>
-      <Info>
-        j := 4
-      </Info>
-      <Info>
-        k := 6
-      </Info>
-      <Section name="A" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Info>
-        i := 2
-      </Info>
-      <Info>
-        j := 3
-      </Info>
-      <Info>
-        k := 5
-      </Info>
-      <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Info>
-        i := 2
-      </Info>
-      <Info>
-        j := 3
-      </Info>
-      <Info>
-        k := 6
-      </Info>
-      <Section name="B" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Info>
-        i := 2
-      </Info>
-      <Info>
-        j := 4
-      </Info>
-      <Info>
-        k := 5
-      </Info>
-      <Info>
-        i := 2
-      </Info>
-      <Info>
-        j := 4
-      </Info>
-      <Info>
-        k := 6
-      </Info>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1938 - nested generate" tags="[.][generators][regression]" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Matching using matcher" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          in, Contains(Catch::Matchers::WithinAbs(0.5, 0.5))
         </Original>
         <Expanded>
-          1
+          { 1.0, 2.0, 3.0, 0.0 } contains element matching is within 0.5 of 0.5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Basic use of the Empty range matcher" tags="[empty][matchers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+    <Section name="Simple, std-provided containers" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          n
+          empty_array, IsEmpty()
         </Original>
         <Expanded>
-          1
+          {  } is empty
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          non_empty_array, !IsEmpty()
         </Original>
         <Expanded>
-          1
+          { 0.0 } not is empty
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          n
+          empty_vec, IsEmpty()
         </Original>
         <Expanded>
-          2
+          {  } is empty
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          non_empty_vec, !IsEmpty()
         </Original>
         <Expanded>
-          1
+          { 'a', 'b', 'c' } not is empty
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          n
+          inner_lists_are_empty, !IsEmpty()
         </Original>
         <Expanded>
-          3
+          { {  } } not is empty
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          inner_lists_are_empty.front(), IsEmpty()
         </Original>
         <Expanded>
-          2
+          {  } is empty
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Type with empty" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          n
+          has_empty{}, !IsEmpty()
         </Original>
         <Expanded>
-          1
+          {?} not is empty
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Type requires ADL found empty free function" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m
+          unrelated::ADL_empty{}, IsEmpty()
         </Original>
         <Expanded>
-          2
+          {?} is empty
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="CAPTURE can deal with complex expressions" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      a := 1
+    </Info>
+    <Info>
+      b := 2
+    </Info>
+    <Info>
+      c := 3
+    </Info>
+    <Info>
+      a + b := 3
+    </Info>
+    <Info>
+      a+b := 3
+    </Info>
+    <Info>
+      c > b := true
+    </Info>
+    <Info>
+      a == 1 := true
+    </Info>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="CAPTURE can deal with complex expressions involving commas" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      std::vector&lt;int>{1, 2, 3}[0, 1, 2] := 3
+    </Info>
+    <Info>
+      std::vector&lt;int>{1, 2, 3}[(0, 1)] := 2
+    </Info>
+    <Info>
+      std::vector&lt;int>{1, 2, 3}[0] := 1
+    </Info>
+    <Info>
+      (helper_1436&lt;int, int>{12, -12}) := { 12, -12 }
+    </Info>
+    <Info>
+      (helper_1436&lt;int, int>(-12, 12)) := { -12, 12 }
+    </Info>
+    <Info>
+      (1, 2) := 2
+    </Info>
+    <Info>
+      (2, 3) := 3
+    </Info>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="CAPTURE parses string and character constants" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      ("comma, in string", "escaped, \", ") := "escaped, ", "
+    </Info>
+    <Info>
+      "single quote in string,'," := "single quote in string,',"
+    </Info>
+    <Info>
+      "some escapes, \\,\\\\" := "some escapes, \,\\"
+    </Info>
+    <Info>
+      "some, ), unmatched, } prenheses {[&lt;" := "some, ), unmatched, } prenheses {[&lt;"
+    </Info>
+    <Info>
+      '"' := '"'
+    </Info>
+    <Info>
+      '\'' := '''
+    </Info>
+    <Info>
+      ',' := ','
+    </Info>
+    <Info>
+      '}' := '}'
+    </Info>
+    <Info>
+      ')' := ')'
+    </Info>
+    <Info>
+      '(' := '('
+    </Info>
+    <Info>
+      '{' := '{'
+    </Info>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Capture and info messages" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+    <Section name="Capture should stringify like assertions" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Info>
+        i := 2
+      </Info>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          n
+          true
         </Original>
         <Expanded>
-          2
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Info should NOT stringify the way assertions do" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Info>
+        3
+      </Info>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          m
+          true
         </Original>
         <Expanded>
-          2
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Character pretty printing" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+    <Section name="Specifically escaped" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          n
+          tab == '\t'
         </Original>
         <Expanded>
-          3
+          '\t' == '\t'
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          m
+          newline == '\n'
         </Original>
         <Expanded>
-          3
+          '\n' == '\n'
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          n
+          carr_return == '\r'
         </Original>
         <Expanded>
-          1
+          '\r' == '\r'
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          m
+          form_feed == '\f'
         </Original>
         <Expanded>
-          3
+          '\f' == '\f'
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="General chars" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          n
+          space == ' '
         </Original>
         <Expanded>
-          2
+          ' ' == ' '
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          m
+          c == chars[i]
         </Original>
         <Expanded>
-          3
+          'a' == 'a'
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          n
+          c == chars[i]
         </Original>
         <Expanded>
-          3
+          'z' == 'z'
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1954 - 7 arg template test case sig compiles - 1, 1, 1, 1, 1, 0, 0" tags="[.][compilation][regression]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1954 - 7 arg template test case sig compiles - 5, 1, 1, 1, 1, 0, 0" tags="[.][compilation][regression]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#1954 - 7 arg template test case sig compiles - 5, 3, 1, 1, 1, 0, 0" tags="[.][compilation][regression]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#748 - captures with unexpected exceptions" tags="[!shouldfail][!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Section name="outside assertions" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Info>
-          answer := 42
-        </Info>
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          expected exception
-        </Exception>
-        <OverallResults successes="0" failures="0" expectedFailures="1"/>
-      </Section>
-      <Section name="inside REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Info>
-          answer := 42
-        </Info>
-        <Expression success="false" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          <Original>
-            thisThrows()
-          </Original>
-          <Expanded>
-            thisThrows()
-          </Expanded>
-          <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-            expected exception
-          </Exception>
-        </Expression>
-        <OverallResults successes="0" failures="0" expectedFailures="1"/>
-      </Section>
-      <Section name="inside REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Info>
-          answer := 42
-        </Info>
-        <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          <Original>
-            thisThrows()
-          </Original>
-          <Expanded>
-            thisThrows()
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#809" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          42 == f
+          c == chars[i]
         </Original>
         <Expanded>
-          42 == {?}
+          'A' == 'A'
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#833" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          a == t
+          c == chars[i]
         </Original>
         <Expanded>
-          3 == 3
+          'Z' == 'Z'
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Low ASCII" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          a == t
+          null_terminator == '\0'
         </Original>
         <Expanded>
-          3 == 3
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          throws_int(true)
+          c == i
         </Original>
         <Expanded>
-          throws_int(true)
+          2 == 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK_THROWS_AS" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          throws_int(true), int
+          c == i
         </Original>
         <Expanded>
-          throws_int(true), int
+          3 == 3
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          throws_int(false)
+          c == i
         </Original>
         <Expanded>
-          throws_int(false)
+          4 == 4
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          "aaa", Catch::Matchers::EndsWith("aaa")
+          c == i
         </Original>
         <Expanded>
-          "aaa" ends with: "aaa"
+          5 == 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-        <Original>
-          templated_tests&lt;int>(3)
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Clara::Arg supports single-arg parse the way Opt does" tags="[arg][clara][compilation]" filename="tests/<exe-name>/IntrospectiveTests/Clara.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/Clara.tests.cpp" >
+      <Original>
+        name.empty()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Clara.tests.cpp" >
+      <Original>
+        name == "foo"
+      </Original>
+      <Expanded>
+        "foo" == "foo"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Combining MatchAllOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, ( MatcherA() &amp;&amp; MatcherB() ) &amp;&amp; MatcherC()
+      </Original>
+      <Expanded>
+        1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, MatcherA() &amp;&amp; ( MatcherB() &amp;&amp; MatcherC() )
+      </Original>
+      <Expanded>
+        1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, ( MatcherA() &amp;&amp; MatcherB() ) &amp;&amp; ( MatcherC() &amp;&amp; MatcherD() )
+      </Original>
+      <Expanded>
+        1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Combining MatchAnyOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, ( MatcherA() || MatcherB() ) || MatcherC()
+      </Original>
+      <Expanded>
+        1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, MatcherA() || ( MatcherB() || MatcherC() )
+      </Original>
+      <Expanded>
+        1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, ( MatcherA() || MatcherB() ) || ( MatcherC() || MatcherD() )
+      </Original>
+      <Expanded>
+        1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Combining MatchNotOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        0, !MatcherA()
+      </Original>
+      <Expanded>
+        0 not equals: (int) 1 or (float) 1.0f
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, !!MatcherA()
+      </Original>
+      <Expanded>
+        1 equals: (int) 1 or (float) 1.0f
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        0, !!!MatcherA()
+      </Original>
+      <Expanded>
+        0 not equals: (int) 1 or (float) 1.0f
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, !!!!MatcherA()
+      </Original>
+      <Expanded>
+        1 equals: (int) 1 or (float) 1.0f
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Combining concrete matchers does not use templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Combining only templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, MatcherA() || MatcherB()
+      </Original>
+      <Expanded>
+        1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, MatcherA() &amp;&amp; MatcherB()
+      </Original>
+      <Expanded>
+        1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        1, MatcherA() || !MatcherB()
+      </Original>
+      <Expanded>
+        1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Combining templated and concrete matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        vec, Predicate&lt;std::vector&lt;int>>( []( auto const&amp; v ) { return std::all_of( v.begin(), v.end(), []( int elem ) { return elem % 2 == 1; } ); }, "All elements are odd" ) &amp;&amp; !EqualsRange( a )
+      </Original>
+      <Expanded>
+        { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        str, StartsWith( "foo" ) &amp;&amp; EqualsRange( arr ) &amp;&amp; EndsWith( "bar" )
+      </Original>
+      <Expanded>
+        "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        str, StartsWith( "foo" ) &amp;&amp; !EqualsRange( bad_arr ) &amp;&amp; EndsWith( "bar" )
+      </Original>
+      <Expanded>
+        "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        str, EqualsRange( arr ) &amp;&amp; StartsWith( "foo" ) &amp;&amp; EndsWith( "bar" )
+      </Original>
+      <Expanded>
+        "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        str, !EqualsRange( bad_arr ) &amp;&amp; StartsWith( "foo" ) &amp;&amp; EndsWith( "bar" )
+      </Original>
+      <Expanded>
+        "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        str, EqualsRange( bad_arr ) || ( StartsWith( "foo" ) &amp;&amp; EndsWith( "bar" ) )
+      </Original>
+      <Expanded>
+        "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        str, ( StartsWith( "foo" ) &amp;&amp; EndsWith( "bar" ) ) || EqualsRange( bad_arr )
+      </Original>
+      <Expanded>
+        "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Combining templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        container, EqualsRange( a ) || EqualsRange( b ) || EqualsRange( c )
+      </Original>
+      <Expanded>
+        { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Commas in various macros are allowed" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::vector&lt;constructor_throws>{constructor_throws{}, constructor_throws{}}
+      </Original>
+      <Expanded>
+        std::vector&lt;constructor_throws>{constructor_throws{}, constructor_throws{}}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::vector&lt;constructor_throws>{constructor_throws{}, constructor_throws{}}
+      </Original>
+      <Expanded>
+        std::vector&lt;constructor_throws>{constructor_throws{}, constructor_throws{}}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::vector&lt;int>{1, 2, 3} == std::vector&lt;int>{1, 2, 3}
+      </Original>
+      <Expanded>
+        std::vector&lt;int>{1, 2, 3} == std::vector&lt;int>{1, 2, 3}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_NOTHROW" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::vector&lt;int>{1, 2, 3} == std::vector&lt;int>{1, 2, 3}
+      </Original>
+      <Expanded>
+        std::vector&lt;int>{1, 2, 3} == std::vector&lt;int>{1, 2, 3}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
+      </Original>
+      <Expanded>
+        { 1, 2 } == { 1, 2 }
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
+      </Original>
+      <Expanded>
+        { 1, 2 } == { 1, 2 }
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        !(std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2, 3})
+      </Original>
+      <Expanded>
+        !({ 1, 2 } == { 1, 2, 3 })
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        !(std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2, 3})
+      </Original>
+      <Expanded>
+        !({ 1, 2 } == { 1, 2, 3 })
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_NOFAIL" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
+      </Original>
+      <Expanded>
+        { 1, 2 } == { 1, 2 }
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECKED_IF" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
+      </Original>
+      <Expanded>
+        { 1, 2 } == { 1, 2 }
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
+      </Original>
+      <Expanded>
+        { 1, 2 } == { 1, 2 }
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Comparing function pointers" tags="[function pointer][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        a
+      </Original>
+      <Expanded>
+        0x<hex digits>
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        a == &amp;foo
+      </Original>
+      <Expanded>
+        0x<hex digits> == 0x<hex digits>
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Comparison ops" tags="[rng]" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+      <Original>
+        SimplePcg32{} == SimplePcg32{}
+      </Original>
+      <Expanded>
+        {?} == {?}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+      <Original>
+        SimplePcg32{ 0 } != SimplePcg32{}
+      </Original>
+      <Expanded>
+        {?} != {?}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+      <Original>
+        !(SimplePcg32{ 1 } == SimplePcg32{ 2 })
+      </Original>
+      <Expanded>
+        !({?} == {?})
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+      <Original>
+        !(SimplePcg32{ 1 } != SimplePcg32{ 1 })
+      </Original>
+      <Expanded>
+        !({?} != {?})
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Comparison with explicitly convertible types" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        td == Approx(10.0)
+      </Original>
+      <Expanded>
+        StrongDoubleTypedef(10) == Approx( 10.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(10.0) == td
+      </Original>
+      <Expanded>
+        Approx( 10.0 ) == StrongDoubleTypedef(10)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        td != Approx(11.0)
+      </Original>
+      <Expanded>
+        StrongDoubleTypedef(10) != Approx( 11.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(11.0) != td
+      </Original>
+      <Expanded>
+        Approx( 11.0 ) != StrongDoubleTypedef(10)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        td &lt;= Approx(10.0)
+      </Original>
+      <Expanded>
+        StrongDoubleTypedef(10) &lt;= Approx( 10.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        td &lt;= Approx(11.0)
+      </Original>
+      <Expanded>
+        StrongDoubleTypedef(10) &lt;= Approx( 11.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(10.0) &lt;= td
+      </Original>
+      <Expanded>
+        Approx( 10.0 ) &lt;= StrongDoubleTypedef(10)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(9.0) &lt;= td
+      </Original>
+      <Expanded>
+        Approx( 9.0 ) &lt;= StrongDoubleTypedef(10)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        td >= Approx(9.0)
+      </Original>
+      <Expanded>
+        StrongDoubleTypedef(10) >= Approx( 9.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        td >= Approx(td)
+      </Original>
+      <Expanded>
+        StrongDoubleTypedef(10) >= Approx( 10.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(td) >= td
+      </Original>
+      <Expanded>
+        Approx( 10.0 ) >= StrongDoubleTypedef(10)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx(11.0) >= td
+      </Original>
+      <Expanded>
+        Approx( 11.0 ) >= StrongDoubleTypedef(10)
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Comparisons between ints where one side is computed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        54 == 6*9
+      </Original>
+      <Expanded>
+        54 == 54
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Comparisons between unsigned ints and negative signed ints match c++ standard behaviour" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        ( -1 > 2u )
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        -1 > 2u
+      </Original>
+      <Expanded>
+        -1 > 2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        ( 2u &lt; -1 )
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        2u &lt; -1
+      </Original>
+      <Expanded>
+        2 &lt; -1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        ( minInt > 2u )
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        minInt > 2u
+      </Original>
+      <Expanded>
+        -2147483648 > 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Comparisons with int literals don't warn when mixing signed/ unsigned" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        i == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        ui == 2
+      </Original>
+      <Expanded>
+        2 == 2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        l == 3
+      </Original>
+      <Expanded>
+        3 == 3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        ul == 4
+      </Original>
+      <Expanded>
+        4 == 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        c == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        uc == 6
+      </Original>
+      <Expanded>
+        6 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        1 == i
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        2 == ui
+      </Original>
+      <Expanded>
+        2 == 2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        3 == l
+      </Original>
+      <Expanded>
+        3 == 3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        4 == ul
+      </Original>
+      <Expanded>
+        4 == 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        5 == c
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        6 == uc
+      </Original>
+      <Expanded>
+        6 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        (std::numeric_limits&lt;uint32_t>::max)() > ul
+      </Original>
+      <Expanded>
+        4294967295 (0x<hex digits>) > 4
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Composed generic matchers shortcircuit" tags="[composed][generic][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="MatchAllOf" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Original>
+          !(matcher.match( 1 ))
         </Original>
         <Expanded>
-          true
+          !false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#835 -- errno should not be touched by Catch" tags="[!shouldfail][.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          f() == 0
+          first.matchCalled
         </Original>
         <Expanded>
-          1 == 0
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          errno == 1
+          !second.matchCalled
         </Original>
         <Expanded>
-          1 == 1
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#872" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Info>
-        dummy := 0
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="MatchAnyOf" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x == 4
+          matcher.match( 1 )
         </Original>
         <Expanded>
-          {?} == 4
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="#961 -- Dynamically created sections should all be reported" tags="[.]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Section name="Looped section 0" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Looped section 1" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Looped section 2" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Looped section 3" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Looped section 4" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="'Not' checks that should fail" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          false != false
+          first.matchCalled
         </Original>
         <Expanded>
-          false != false
+          true
         </Expanded>
       </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          true != true
+          !second.matchCalled
         </Original>
         <Expanded>
-          true != true
+          true
         </Expanded>
       </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Composed matchers shortcircuit" tags="[composed][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="MatchAllOf" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          !true
+          !(matcher.match( 1 ))
         </Original>
         <Expanded>
-          false
+          !false
         </Expanded>
       </Expression>
-      <Expression success="false" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          !(true)
+          first.matchCalled
         </Original>
         <Expanded>
-          !true
+          true
         </Expanded>
       </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          !trueValue
+          !second.matchCalled
         </Original>
         <Expanded>
-          false
+          true
         </Expanded>
       </Expression>
-      <Expression success="false" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="MatchAnyOf" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          !(trueValue)
+          matcher.match( 1 )
         </Original>
         <Expanded>
-          !true
+          true
         </Expanded>
       </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          !(1 == 1)
+          first.matchCalled
         </Original>
         <Expanded>
-          false
+          true
         </Expanded>
       </Expression>
-      <Expression success="false" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          !(1 == 1)
+          !second.matchCalled
         </Original>
         <Expanded>
-          !(1 == 1)
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="'Not' checks that should succeed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Contains string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Contains( "not there", Catch::CaseSensitive::No )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" contains: "not there" (case insensitive)
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Contains( "STRING" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" contains: "STRING"
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Copy and then generate a range" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+    <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          false == false
+          elem % 2 == 1
         </Original>
         <Expanded>
-          false == false
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          true == true
+          elem % 2 == 1
         </Original>
         <Expanded>
-          true == true
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          !false
+          elem % 2 == 1
         </Original>
         <Expanded>
-          true
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          !(false)
+          elem % 2 == 1
         </Original>
         <Expanded>
-          !false
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          !falseValue
+          elem % 2 == 1
         </Original>
         <Expanded>
-          true
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          !(falseValue)
+          elem % 2 == 1
         </Original>
         <Expanded>
-          !false
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          !(1 == 2)
+          elem % 2 == 1
         </Original>
         <Expanded>
-          true
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          !(1 == 2)
+          elem % 2 == 1
         </Original>
         <Expanded>
-          !(1 == 2)
+          1 == 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="(unimplemented) static bools can be evaluated" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Section name="compare to true" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Original>
-            is_true&lt;true>::value == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Original>
-            true == is_true&lt;true>::value
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="compare to false" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Original>
-            is_true&lt;false>::value == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Original>
-            false == is_true&lt;false>::value
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="negation" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Original>
-            !is_true&lt;false>::value
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="double negation" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Original>
-            !!is_true&lt;true>::value
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="direct" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Original>
-            is_true&lt;true>::value
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Original>
-            !(is_true&lt;false>::value)
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="3x3x3 ints" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          x &lt; y
+          elem % 2 == 1
         </Original>
         <Expanded>
-          1 &lt; 4
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          y &lt; z
+          elem % 2 == 1
         </Original>
         <Expanded>
-          4 &lt; 7
+          1 == 1
         </Expanded>
       </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
       <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          x &lt; z
+          elem % 2 == 1
         </Original>
         <Expanded>
-          1 &lt; 7
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          x &lt; y
+          elem % 2 == 1
         </Original>
         <Expanded>
-          1 &lt; 4
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Final validation" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          y &lt; z
+          call_count == 1
         </Original>
         <Expanded>
-          4 &lt; 8
+          1 == 1
         </Expanded>
       </Expression>
       <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          x &lt; z
+          make_data().size() == test_count
         </Original>
         <Expanded>
-          1 &lt; 8
+          6 == 6
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Custom exceptions can be translated when testing for nothrow" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Expression success="false" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        throwCustom()
+      </Original>
+      <Expanded>
+        throwCustom()
+      </Expanded>
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        custom exception - not std
+      </Exception>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Custom exceptions can be translated when testing for throwing as something else" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Expression success="false" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        throwCustom(), std::exception
+      </Original>
+      <Expanded>
+        throwCustom(), std::exception
+      </Expanded>
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        custom exception - not std
+      </Exception>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Custom std-exceptions can be custom translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      custom std exception
+    </Exception>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Default scale is invisible to comparison" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        101.000001 != Approx(100).epsilon(0.01)
+      </Original>
+      <Expanded>
+        101.000001 != Approx( 100.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        std::pow(10, -5) != Approx(std::pow(10, -7))
+      </Original>
+      <Expanded>
+        0.00001 != Approx( 0.0000001 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Directly creating an EnumInfo" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        enumInfo->lookup(0) == "Value1"
+      </Original>
+      <Expanded>
+        Value1 == "Value1"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        enumInfo->lookup(1) == "Value2"
+      </Original>
+      <Expanded>
+        Value2 == "Value2"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        enumInfo->lookup(3) == "{** unexpected enum value **}"
+      </Original>
+      <Expanded>
+        {** unexpected enum value **}
+==
+"{** unexpected enum value **}"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="EndsWith string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), EndsWith( "Substring" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" ends with: "Substring"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), EndsWith( "this", Catch::CaseSensitive::No )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" ends with: "this" (case insensitive)
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Enums can quickly have stringification enabled using REGISTER_ENUM" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        stringify( EnumClass3::Value1 ) == "Value1"
+      </Original>
+      <Expanded>
+        "Value1" == "Value1"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        stringify( EnumClass3::Value2 ) == "Value2"
+      </Original>
+      <Expanded>
+        "Value2" == "Value2"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        stringify( EnumClass3::Value3 ) == "Value3"
+      </Original>
+      <Expanded>
+        "Value3" == "Value3"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        stringify( EnumClass3::Value4 ) == "{** unexpected enum value **}"
+      </Original>
+      <Expanded>
+        "{** unexpected enum value **}"
+==
+"{** unexpected enum value **}"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        stringify( ec3 ) == "Value2"
+      </Original>
+      <Expanded>
+        "Value2" == "Value2"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Enums in namespaces can quickly have stringification enabled using REGISTER_ENUM" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        stringify( Bikeshed::Colours::Red ) == "Red"
+      </Original>
+      <Expanded>
+        "Red" == "Red"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        stringify( Bikeshed::Colours::Blue ) == "Blue"
+      </Original>
+      <Expanded>
+        "Blue" == "Blue"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Epsilon only applies to Approx's value" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        101.01 != Approx(100).epsilon(0.01)
+      </Original>
+      <Expanded>
+        101.01 != Approx( 100.0 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Equality checks that should fail" tags="[!mayfail][.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven == 6
+      </Original>
+      <Expanded>
+        7 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven == 8
+      </Original>
+      <Expanded>
+        7 == 8
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven == 0
+      </Original>
+      <Expanded>
+        7 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one == Approx( 9.11f )
+      </Original>
+      <Expanded>
+        9.1f == Approx( 9.1099996567 )
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one == Approx( 9.0f )
+      </Original>
+      <Expanded>
+        9.1f == Approx( 9.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one == Approx( 1 )
+      </Original>
+      <Expanded>
+        9.1f == Approx( 1.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one == Approx( 0 )
+      </Original>
+      <Expanded>
+        9.1f == Approx( 0.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.double_pi == Approx( 3.1415 )
+      </Original>
+      <Expanded>
+        3.1415926535 == Approx( 3.1415 )
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello == "goodbye"
+      </Original>
+      <Expanded>
+        "hello" == "goodbye"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello == "hell"
+      </Original>
+      <Expanded>
+        "hello" == "hell"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello == "hello1"
+      </Original>
+      <Expanded>
+        "hello" == "hello1"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello.size() == 6
+      </Original>
+      <Expanded>
+        5 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        x == Approx( 1.301 )
+      </Original>
+      <Expanded>
+        1.3 == Approx( 1.301 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Equality checks that should succeed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven == 7
+      </Original>
+      <Expanded>
+        7 == 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one == Approx( 9.1f )
+      </Original>
+      <Expanded>
+        9.1f == Approx( 9.1000003815 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.double_pi == Approx( 3.1415926535 )
+      </Original>
+      <Expanded>
+        3.1415926535 == Approx( 3.1415926535 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello == "hello"
+      </Original>
+      <Expanded>
+        "hello" == "hello"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        "hello" == data.str_hello
+      </Original>
+      <Expanded>
+        "hello" == "hello"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        x == Approx( 1.3 )
+      </Original>
+      <Expanded>
+        1.3 == Approx( 1.3 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Equals" tags="[matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Equals( "this string contains 'abc' as a substring" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Equals( "this string contains 'ABC' as a substring", Catch::CaseSensitive::No )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Equals string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Equals( "this string contains 'ABC' as a substring" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Equals( "something else", Catch::CaseSensitive::No )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" equals: "something else" (case insensitive)
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified" tags="[exception][toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(WhatException{}) == "This exception has overridden what() method"
+      </Original>
+      <Expanded>
+        "This exception has overridden what() method"
+==
+"This exception has overridden what() method"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(OperatorException{}) == "OperatorException"
+      </Original>
+      <Expanded>
+        "OperatorException" == "OperatorException"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(StringMakerException{}) == "StringMakerException"
+      </Original>
+      <Expanded>
+        "StringMakerException"
+==
+"StringMakerException"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Exception matchers that fail" tags="[!throws][.][exceptions][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="No exception" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="false" type="CHECK_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }
         </Original>
         <Expanded>
-          1 &lt; 4
+          doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="false" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }
         </Original>
         <Expanded>
-          4 &lt; 9
+          doesNotThrow(), SpecialException, ExceptionMatcher{ 1 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="0" failures="2" expectedFailures="0"/>
+    </Section>
+    <Section name="Type mismatch" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="false" type="CHECK_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }
         </Original>
         <Expanded>
-          1 &lt; 9
+          throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }
         </Expanded>
+        <Exception filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+          Unknown exception
+        </Exception>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="false" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }
         </Original>
         <Expanded>
-          1 &lt; 5
+          throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 }
         </Expanded>
+        <Exception filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+          Unknown exception
+        </Exception>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="0" failures="2" expectedFailures="0"/>
+    </Section>
+    <Section name="Contents are wrong" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="false" type="CHECK_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          throwsSpecialException( 3 ), SpecialException, ExceptionMatcher{ 1 }
         </Original>
         <Expanded>
-          5 &lt; 7
+          SpecialException::what special exception has value of 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="false" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          throwsSpecialException( 4 ), SpecialException, ExceptionMatcher{ 1 }
         </Original>
         <Expanded>
-          1 &lt; 7
+          SpecialException::what special exception has value of 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="0" failures="2" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Exception matchers that succeed" tags="[!throws][exceptions][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="CHECK_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        throwsSpecialException( 1 ), SpecialException, ExceptionMatcher{ 1 }
+      </Original>
+      <Expanded>
+        SpecialException::what special exception has value of 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        throwsSpecialException( 2 ), SpecialException, ExceptionMatcher{ 2 }
+      </Original>
+      <Expanded>
+        SpecialException::what special exception has value of 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Exception messages can be tested for" tags="[!throws]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Section name="exact match" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
         <Original>
-          x &lt; y
+          thisThrows(), "expected exception"
         </Original>
         <Expanded>
-          1 &lt; 5
+          "expected exception" equals: "expected exception"
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="different case" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
         <Original>
-          y &lt; z
+          thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No )
         </Original>
         <Expanded>
-          5 &lt; 8
+          "expected exception" equals: "expected exception" (case insensitive)
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="wildcarded" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
         <Original>
-          x &lt; z
+          thisThrows(), StartsWith( "expected" )
         </Original>
         <Expanded>
-          1 &lt; 8
+          "expected exception" starts with: "expected"
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
         <Original>
-          x &lt; y
+          thisThrows(), EndsWith( "exception" )
         </Original>
         <Expanded>
-          1 &lt; 5
+          "expected exception" ends with: "exception"
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
         <Original>
-          y &lt; z
+          thisThrows(), Contains( "except" )
         </Original>
         <Expanded>
-          5 &lt; 9
+          "expected exception" contains: "except"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        <Original>
+          thisThrows(), Contains( "exCept", Catch::CaseSensitive::No )
+        </Original>
+        <Expanded>
+          "expected exception" contains: "except" (case insensitive)
+        </Expanded>
+      </Expression>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Exceptions matchers" tags="[!throws][exceptions][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        throwsDerivedException(), DerivedException, Message( "DerivedException::what" )
+      </Original>
+      <Expanded>
+        DerivedException::what exception message matches "DerivedException::what"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        throwsDerivedException(), DerivedException, !Message( "derivedexception::what" )
+      </Original>
+      <Expanded>
+        DerivedException::what not exception message matches "derivedexception::what"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        throwsSpecialException( 2 ), SpecialException, !Message( "DerivedException::what" )
+      </Original>
+      <Expanded>
+        SpecialException::what not exception message matches "DerivedException::what"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        throwsSpecialException( 2 ), SpecialException, Message( "SpecialException::what" )
+      </Original>
+      <Expanded>
+        SpecialException::what exception message matches "SpecialException::what"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Expected exceptions that don't throw or unexpected exceptions fail the test" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Expression success="false" type="CHECK_THROWS_AS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisThrows(), std::string
+      </Original>
+      <Expanded>
+        thisThrows(), std::string
+      </Expanded>
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        expected exception
+      </Exception>
+    </Expression>
+    <Expression success="false" type="CHECK_THROWS_AS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisDoesntThrow(), std::domain_error
+      </Original>
+      <Expanded>
+        thisDoesntThrow(), std::domain_error
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisThrows()
+      </Original>
+      <Expanded>
+        thisThrows()
+      </Expanded>
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        expected exception
+      </Exception>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="FAIL aborts the test" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      This is a failure
+    </Failure>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="FAIL does not require an argument" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" />
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="FAIL_CHECK does not abort the test" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      This is a failure
+    </Failure>
+    <Warning>
+      This message appears in the output
+    </Warning>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Factorials are computed" tags="[factorial]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        Factorial(0) == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        Factorial(1) == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        Factorial(2) == 2
+      </Original>
+      <Expanded>
+        2 == 2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        Factorial(3) == 6
+      </Original>
+      <Expanded>
+        6 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        Factorial(10) == 3628800
+      </Original>
+      <Expanded>
+        3628800 (0x<hex digits>) == 3628800 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Floating point matchers: double" tags="[floating-point][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="Relative" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          10., WithinRel( 11.1, 0.1 )
         </Original>
         <Expanded>
-          1 &lt; 9
+          10.0 and 11.1 are within 10% of each other
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          10., !WithinRel( 11.2, 0.1 )
         </Original>
         <Expanded>
-          1 &lt; 6
+          10.0 not and 11.2 are within 10% of each other
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          1., !WithinRel( 0., 0.99 )
         </Original>
         <Expanded>
-          6 &lt; 7
+          1.0 not and 0 are within 99% of each other
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          -0., WithinRel( 0. )
         </Original>
         <Expanded>
-          1 &lt; 7
+          -0.0 and 0 are within 2.22045e-12% of each other
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Some subnormal values" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+          <Original>
+            v1, WithinRel( v2 )
+          </Original>
+          <Expanded>
+            0.0 and 2.22507e-308 are within 2.22045e-12% of each other
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Margin" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          1., WithinAbs( 1., 0 )
         </Original>
         <Expanded>
-          1 &lt; 6
+          1.0 is within 0.0 of 1.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          0., WithinAbs( 1., 1 )
         </Original>
         <Expanded>
-          6 &lt; 8
+          0.0 is within 1.0 of 1.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          0., !WithinAbs( 1., 0.99 )
         </Original>
         <Expanded>
-          1 &lt; 8
+          0.0 not is within 0.99 of 1.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          0., !WithinAbs( 1., 0.99 )
         </Original>
         <Expanded>
-          1 &lt; 6
+          0.0 not is within 0.99 of 1.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          11., !WithinAbs( 10., 0.5 )
         </Original>
         <Expanded>
-          6 &lt; 9
+          11.0 not is within 0.5 of 10.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          10., !WithinAbs( 11., 0.5 )
         </Original>
         <Expanded>
-          1 &lt; 9
+          10.0 not is within 0.5 of 11.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          -10., WithinAbs( -10., 0.5 )
         </Original>
         <Expanded>
-          2 &lt; 4
+          -10.0 is within 0.5 of -10.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          -10., WithinAbs( -9.6, 0.5 )
         </Original>
         <Expanded>
-          4 &lt; 7
+          -10.0 is within 0.5 of -9.6
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="8" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="ULPs" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          1., WithinULP( 1., 0 )
         </Original>
         <Expanded>
-          2 &lt; 7
+          1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          nextafter( 1., 2. ), WithinULP( 1., 1 )
         </Original>
         <Expanded>
-          2 &lt; 4
+          1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          0., WithinULP( nextafter( 0., 1. ), 1 )
         </Original>
         <Expanded>
-          4 &lt; 8
+          0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324])
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          1., WithinULP( nextafter( 1., 0. ), 1 )
         </Original>
         <Expanded>
-          2 &lt; 8
+          1.0 is within 1 ULPs of 9.9999999999999989e-01 ([9.9999999999999978e-01, 1.0000000000000000e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          1., !WithinULP( nextafter( 1., 2. ), 0 )
         </Original>
         <Expanded>
-          2 &lt; 4
+          1.0 not is within 0 ULPs of 1.0000000000000002e+00 ([1.0000000000000002e+00, 1.0000000000000002e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          1., WithinULP( 1., 0 )
         </Original>
         <Expanded>
-          4 &lt; 9
+          1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          -0., WithinULP( 0., 0 )
         </Original>
         <Expanded>
-          2 &lt; 9
+          -0.0 is within 0 ULPs of 0.0000000000000000e+00 ([0.0000000000000000e+00, 0.0000000000000000e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="7" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Composed" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 )
         </Original>
         <Expanded>
-          2 &lt; 5
+          1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0000000000000000e+00 ([1.9999999999999998e+00, 2.0000000000000004e+00]) )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 )
         </Original>
         <Expanded>
-          5 &lt; 7
+          1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) )
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 )
         </Original>
         <Expanded>
-          2 &lt; 7
+          0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Constructor validation" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          WithinAbs( 1., 0. )
         </Original>
         <Expanded>
-          2 &lt; 5
+          WithinAbs( 1., 0. )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          WithinAbs( 1., -1. ), std::domain_error
         </Original>
         <Expanded>
-          5 &lt; 8
+          WithinAbs( 1., -1. ), std::domain_error
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          WithinULP( 1., 0 )
         </Original>
         <Expanded>
-          2 &lt; 8
+          WithinULP( 1., 0 )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          WithinRel( 1., 0. )
         </Original>
         <Expanded>
-          2 &lt; 5
+          WithinRel( 1., 0. )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          WithinRel( 1., -0.2 ), std::domain_error
         </Original>
         <Expanded>
-          5 &lt; 9
+          WithinRel( 1., -0.2 ), std::domain_error
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          WithinRel( 1., 1. ), std::domain_error
         </Original>
         <Expanded>
-          2 &lt; 9
+          WithinRel( 1., 1. ), std::domain_error
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Floating point matchers: float" tags="[floating-point][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="Relative" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          10.f, WithinRel( 11.1f, 0.1f )
         </Original>
         <Expanded>
-          2 &lt; 6
+          10.0f and 11.1 are within 10% of each other
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          10.f, !WithinRel( 11.2f, 0.1f )
         </Original>
         <Expanded>
-          6 &lt; 7
+          10.0f not and 11.2 are within 10% of each other
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          1.f, !WithinRel( 0.f, 0.99f )
         </Original>
         <Expanded>
-          2 &lt; 7
+          1.0f not and 0 are within 99% of each other
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          -0.f, WithinRel( 0.f )
         </Original>
         <Expanded>
-          2 &lt; 6
+          -0.0f and 0 are within 0.00119209% of each other
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Some subnormal values" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+          <Original>
+            v1, WithinRel( v2 )
+          </Original>
+          <Expanded>
+            0.0f and 1.17549e-38 are within 0.00119209% of each other
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Margin" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          1.f, WithinAbs( 1.f, 0 )
         </Original>
         <Expanded>
-          6 &lt; 8
+          1.0f is within 0.0 of 1.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          0.f, WithinAbs( 1.f, 1 )
         </Original>
         <Expanded>
-          2 &lt; 8
+          0.0f is within 1.0 of 1.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          0.f, !WithinAbs( 1.f, 0.99f )
         </Original>
         <Expanded>
-          2 &lt; 6
+          0.0f not is within 0.9900000095 of 1.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          0.f, !WithinAbs( 1.f, 0.99f )
         </Original>
         <Expanded>
-          6 &lt; 9
+          0.0f not is within 0.9900000095 of 1.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          0.f, WithinAbs( -0.f, 0 )
         </Original>
         <Expanded>
-          2 &lt; 9
+          0.0f is within 0.0 of -0.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          11.f, !WithinAbs( 10.f, 0.5f )
         </Original>
         <Expanded>
-          3 &lt; 4
+          11.0f not is within 0.5 of 10.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          10.f, !WithinAbs( 11.f, 0.5f )
         </Original>
         <Expanded>
-          4 &lt; 7
+          10.0f not is within 0.5 of 11.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          -10.f, WithinAbs( -10.f, 0.5f )
         </Original>
         <Expanded>
-          3 &lt; 7
+          -10.0f is within 0.5 of -10.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          -10.f, WithinAbs( -9.6f, 0.5f )
         </Original>
         <Expanded>
-          3 &lt; 4
+          -10.0f is within 0.5 of -9.6000003815
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="9" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="ULPs" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          1.f, WithinULP( 1.f, 0 )
         </Original>
         <Expanded>
-          4 &lt; 8
+          1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          -1.f, WithinULP( -1.f, 0 )
         </Original>
         <Expanded>
-          3 &lt; 8
+          -1.0f is within 0 ULPs of -1.00000000e+00f ([-1.00000000e+00, -1.00000000e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 )
         </Original>
         <Expanded>
-          3 &lt; 4
+          1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          0.f, WithinULP( nextafter( 0.f, 1.f ), 1 )
         </Original>
         <Expanded>
-          4 &lt; 9
+          0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45])
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          1.f, WithinULP( nextafter( 1.f, 0.f ), 1 )
         </Original>
         <Expanded>
-          3 &lt; 9
+          1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          1.f, !WithinULP( nextafter( 1.f, 2.f ), 0 )
         </Original>
         <Expanded>
-          3 &lt; 5
+          1.0f not is within 0 ULPs of 1.00000012e+00f ([1.00000012e+00, 1.00000012e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          1.f, WithinULP( 1.f, 0 )
         </Original>
         <Expanded>
-          5 &lt; 7
+          1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          -0.f, WithinULP( 0.f, 0 )
         </Original>
         <Expanded>
-          3 &lt; 7
+          -0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00])
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="8" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Composed" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 )
         </Original>
         <Expanded>
-          3 &lt; 5
+          1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 )
         </Original>
         <Expanded>
-          5 &lt; 8
+          1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) )
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          0.0001f, WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f )
         </Original>
         <Expanded>
-          3 &lt; 8
+          0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Constructor validation" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          WithinAbs( 1.f, 0.f )
         </Original>
         <Expanded>
-          3 &lt; 5
+          WithinAbs( 1.f, 0.f )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          WithinAbs( 1.f, -1.f ), std::domain_error
         </Original>
         <Expanded>
-          5 &lt; 9
+          WithinAbs( 1.f, -1.f ), std::domain_error
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          WithinULP( 1.f, 0 )
         </Original>
         <Expanded>
-          3 &lt; 9
+          WithinULP( 1.f, 0 )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          WithinULP( 1.f, static_cast&lt;uint64_t>( -1 ) ), std::domain_error
         </Original>
         <Expanded>
-          3 &lt; 6
+          WithinULP( 1.f, static_cast&lt;uint64_t>( -1 ) ), std::domain_error
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          y &lt; z
+          WithinRel( 1.f, 0.f )
         </Original>
         <Expanded>
-          6 &lt; 7
+          WithinRel( 1.f, 0.f )
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; z
+          WithinRel( 1.f, -0.2f ), std::domain_error
         </Original>
         <Expanded>
-          3 &lt; 7
+          WithinRel( 1.f, -0.2f ), std::domain_error
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          x &lt; y
+          WithinRel( 1.f, 1.f ), std::domain_error
         </Original>
         <Expanded>
-          3 &lt; 6
+          WithinRel( 1.f, 1.f ), std::domain_error
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="7" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Generators -- adapters" tags="[generators][generic]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+    <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i % 2 == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i % 2 == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i % 2 == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Throws if there are no matching values" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            filter([] (int) {return false; }, value(1)), Catch::GeneratorException
+          </Original>
+          <Expanded>
+            filter([] (int) {return false; }, value(1)), Catch::GeneratorException
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortening a range" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          y &lt; z
+          i &lt; 4
         </Original>
         <Expanded>
-          6 &lt; 8
+          1 &lt; 4
         </Expanded>
       </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortening a range" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
       <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          x &lt; z
+          i &lt; 4
         </Original>
         <Expanded>
-          3 &lt; 8
+          2 &lt; 4
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortening a range" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          x &lt; y
+          i &lt; 4
         </Original>
         <Expanded>
-          3 &lt; 6
+          3 &lt; 4
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Same type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i % 2 == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Same type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i % 2 == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Same type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i % 2 == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Different type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i.size() == 1
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Different type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i.size() == 1
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Different type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i.size() == 1
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Different deduced type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i.size() == 1
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Different deduced type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i.size() == 1
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Different deduced type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            i.size() == 1
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          y &lt; z
+          j > 0
         </Original>
         <Expanded>
-          6 &lt; 9
+          1 > 0
         </Expanded>
       </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
       <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          x &lt; z
-        </Original>
-        <Expanded>
-          3 &lt; 9
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A METHOD_AS_TEST_CASE based test run that fails" tags="[.][class][failing]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-        <Original>
-          s == "world"
-        </Original>
-        <Expanded>
-          "hello" == "world"
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A METHOD_AS_TEST_CASE based test run that succeeds" tags="[class]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-        <Original>
-          s == "hello"
-        </Original>
-        <Expanded>
-          "hello" == "hello"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that fails - Template_Foo&lt;float>" tags="[.][class][failing][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-        <Original>
-          Template_Fixture_2&lt;TestType>::m_a.size() == 1
-        </Original>
-        <Expanded>
-          0 == 1
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that fails - Template_Foo&lt;int>" tags="[.][class][failing][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-        <Original>
-          Template_Fixture_2&lt;TestType>::m_a.size() == 1
-        </Original>
-        <Expanded>
-          0 == 1
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that fails - std::vector&lt;float>" tags="[.][class][failing][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-        <Original>
-          Template_Fixture_2&lt;TestType>::m_a.size() == 1
-        </Original>
-        <Expanded>
-          0 == 1
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that fails - std::vector&lt;int>" tags="[.][class][failing][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-        <Original>
-          Template_Fixture_2&lt;TestType>::m_a.size() == 1
+          j > 0
         </Original>
         <Expanded>
-          0 == 1
+          2 > 0
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that succeeds - Template_Foo&lt;float>" tags="[class][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-        <Original>
-          Template_Fixture_2&lt;TestType>::m_a.size() == 0
-        </Original>
-        <Expanded>
-          0 == 0
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that succeeds - Template_Foo&lt;int>" tags="[class][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>::m_a.size() == 0
+          j > 0
         </Original>
         <Expanded>
-          0 == 0
+          3 > 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that succeeds - std::vector&lt;float>" tags="[class][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>::m_a.size() == 0
+          j > 0
         </Original>
         <Expanded>
-          0 == 0
+          1 > 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD based test run that succeeds - std::vector&lt;int>" tags="[class][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>::m_a.size() == 0
+          j > 0
         </Original>
         <Expanded>
-          0 == 0
+          2 > 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that fails - Template_Foo_2&lt;float, 6>" tags="[.][class][failing][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>{}.m_a.size() &lt; 2
+          j > 0
         </Original>
         <Expanded>
-          6 &lt; 2
+          3 > 0
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that fails - Template_Foo_2&lt;int, 2>" tags="[.][class][failing][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Number of elements in source is divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.size() == 2
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.front() == chunk2.back()
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Number of elements in source is divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.size() == 2
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.front() == chunk2.back()
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Number of elements in source is divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.size() == 2
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.front() == chunk2.back()
+          </Original>
+          <Expanded>
+            3 == 3
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Number of elements in source is not divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.size() == 2
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.front() == chunk2.back()
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.front() &lt; 3
+          </Original>
+          <Expanded>
+            1 &lt; 3
+          </Expanded>
+        </Expression>
+        <OverallResults successes="3" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Number of elements in source is not divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.size() == 2
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.front() == chunk2.back()
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.front() &lt; 3
+          </Original>
+          <Expanded>
+            2 &lt; 3
+          </Expanded>
+        </Expression>
+        <OverallResults successes="3" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Chunk size of zero" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.size() == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Chunk size of zero" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.size() == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Chunk size of zero" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk2.size() == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Section name="Throws on too small generators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+          <Original>
+            chunk(2, value(1)), Catch::GeneratorException
+          </Original>
+          <Expanded>
+            chunk(2, value(1)), Catch::GeneratorException
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Generators -- simple" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>{}.m_a.size() &lt; 2
+          j &lt; i
         </Original>
         <Expanded>
-          2 &lt; 2
+          -3 &lt; 1
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that fails - std::array&lt;float, 6>" tags="[.][class][failing][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>{}.m_a.size() &lt; 2
+          j &lt; i
         </Original>
         <Expanded>
-          6 &lt; 2
+          -2 &lt; 1
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that fails - std::array&lt;int, 2>" tags="[.][class][failing][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>{}.m_a.size() &lt; 2
+          j &lt; i
         </Original>
         <Expanded>
-          2 &lt; 2
+          -1 &lt; 1
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds - Template_Foo_2&lt;float,6>" tags="[class][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>{}.m_a.size() >= 2
+          4u * i > str.size()
         </Original>
         <Expanded>
-          6 >= 2
+          4 > 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds - Template_Foo_2&lt;int,2>" tags="[class][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>{}.m_a.size() >= 2
+          4u * i > str.size()
         </Original>
         <Expanded>
-          2 >= 2
+          4 > 2
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds - std::array&lt;float,6>" tags="[class][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>{}.m_a.size() >= 2
+          4u * i > str.size()
         </Original>
         <Expanded>
-          6 >= 2
+          4 > 3
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG based test run that succeeds - std::array&lt;int,2>" tags="[class][nttp][product][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture_2&lt;TestType>{}.m_a.size() >= 2
+          j &lt; i
         </Original>
         <Expanded>
-          2 >= 2
+          -3 &lt; 2
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that fails - double" tags="[.][class][failing][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture&lt;TestType>::m_a == 2
+          j &lt; i
         </Original>
         <Expanded>
-          1.0 == 2
+          -2 &lt; 2
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that fails - float" tags="[.][class][failing][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture&lt;TestType>::m_a == 2
+          j &lt; i
         </Original>
         <Expanded>
-          1.0f == 2
+          -1 &lt; 2
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that fails - int" tags="[.][class][failing][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture&lt;TestType>::m_a == 2
+          4u * i > str.size()
         </Original>
         <Expanded>
-          1 == 2
+          8 > 1
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that succeeds - double" tags="[class][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture&lt;TestType>::m_a == 1
+          4u * i > str.size()
         </Original>
         <Expanded>
-          1.0 == 1
+          8 > 2
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that succeeds - float" tags="[class][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture&lt;TestType>::m_a == 1
+          4u * i > str.size()
         </Original>
         <Expanded>
-          1.0f == 1
+          8 > 3
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD based test run that succeeds - int" tags="[class][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Template_Fixture&lt;TestType>::m_a == 1
+          j &lt; i
         </Original>
         <Expanded>
-          1 == 1
+          -3 &lt; 3
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that fails - 1" tags="[.][class][failing][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Nttp_Fixture&lt;V>::value == 0
+          j &lt; i
         </Original>
         <Expanded>
-          1 == 0
+          -2 &lt; 3
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that fails - 3" tags="[.][class][failing][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Nttp_Fixture&lt;V>::value == 0
+          j &lt; i
         </Original>
         <Expanded>
-          3 == 0
+          -1 &lt; 3
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that fails - 6" tags="[.][class][failing][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Nttp_Fixture&lt;V>::value == 0
+          4u * i > str.size()
         </Original>
         <Expanded>
-          6 == 0
+          12 > 1
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds - 1" tags="[class][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Nttp_Fixture&lt;V>::value > 0
+          4u * i > str.size()
         </Original>
         <Expanded>
-          1 > 0
+          12 > 2
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds - 3" tags="[class][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
         <Original>
-          Nttp_Fixture&lt;V>::value > 0
+          4u * i > str.size()
         </Original>
         <Expanded>
-          3 > 0
+          12 > 3
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEMPLATE_TEST_CASE_METHOD_SIG based test run that succeeds - 6" tags="[class][nttp][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Generators internals" tags="[generators][internals]" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+    <Section name="Single value" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          Nttp_Fixture&lt;V>::value > 0
+          gen.get() == 123
         </Original>
         <Expanded>
-          6 > 0
+          123 == 123
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A TEST_CASE_METHOD based test run that fails" tags="[.][class][failing]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          m_a == 2
+          !(gen.next())
         </Original>
         <Expanded>
-          1 == 2
+          !false
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A TEST_CASE_METHOD based test run that succeeds" tags="[class]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Preset values" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          m_a == 1
+          gen.get() == 1
         </Original>
         <Expanded>
           1 == 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A Template product test case - Foo&lt;float>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          x.size() == 0
-        </Original>
-        <Expanded>
-          0 == 0
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A Template product test case - Foo&lt;int>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          x.size() == 0
-        </Original>
-        <Expanded>
-          0 == 0
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A Template product test case - std::vector&lt;float>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          x.size() == 0
+          gen.next()
         </Original>
         <Expanded>
-          0 == 0
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A Template product test case - std::vector&lt;int>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          x.size() == 0
+          gen.get() == 3
         </Original>
         <Expanded>
-          0 == 0
+          3 == 3
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A Template product test case with array signature - Bar&lt;float, 42>" tags="[nttp][product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          x.size() > 0
+          gen.next()
         </Original>
         <Expanded>
-          42 > 0
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A Template product test case with array signature - Bar&lt;int, 9>" tags="[nttp][product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          x.size() > 0
+          gen.get() == 5
         </Original>
         <Expanded>
-          9 > 0
+          5 == 5
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A Template product test case with array signature - std::array&lt;float, 42>" tags="[nttp][product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          x.size() > 0
+          !(gen.next())
         </Original>
         <Expanded>
-          42 > 0
+          !false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A Template product test case with array signature - std::array&lt;int, 9>" tags="[nttp][product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Generator combinator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          x.size() > 0
+          gen.get() == 1
         </Original>
         <Expanded>
-          9 > 0
+          1 == 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A comparison that uses literals instead of the normal constructor" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          d == 1.23_a
+          gen.next()
         </Original>
         <Expanded>
-          1.23 == Approx( 1.23 )
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          d != 1.22_a
+          gen.get() == 5
         </Original>
         <Expanded>
-          1.23 != Approx( 1.22 )
+          5 == 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          -d == -1.23_a
+          gen.next()
         </Original>
         <Expanded>
-          -1.23 == Approx( -1.23 )
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          d == 1.2_a .epsilon(.1)
+          gen.get() == 2
         </Original>
         <Expanded>
-          1.23 == Approx( 1.2 )
+          2 == 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          d != 1.2_a .epsilon(.001)
+          gen.next()
         </Original>
         <Expanded>
-          1.23 != Approx( 1.2 )
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          d == 1_a .epsilon(.3)
+          gen.get() == 4
         </Original>
         <Expanded>
-          1.23 == Approx( 1.0 )
+          4 == 4
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="A couple of nested sections followed by a failure" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Section name="Outer" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Section name="Inner" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Failure filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        to infinity and beyond
-      </Failure>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="A failing expression with a non streamable type is still captured" tags="[.][failing][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          &amp;o1 == &amp;o2
+          gen.next()
         </Original>
         <Expanded>
-          0x<hex digits> == 0x<hex digits>
+          true
         </Expanded>
       </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          o1 == o2
+          gen.get() == 0
         </Original>
         <Expanded>
-          {?} == {?}
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Absolute margin" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          104.0 != Approx(100.0)
+          !(gen.next())
         </Original>
         <Expanded>
-          104.0 != Approx( 100.0 )
+          !false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <OverallResults successes="10" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Explicitly typed generator sequence" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          104.0 == Approx(100.0).margin(5)
+          gen.get().size() == 2
         </Original>
         <Expanded>
-          104.0 == Approx( 100.0 )
+          2 == 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          104.0 == Approx(100.0).margin(4)
+          gen.get() == "aa"
         </Original>
         <Expanded>
-          104.0 == Approx( 100.0 )
+          "aa" == "aa"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          104.0 != Approx(100.0).margin(3)
+          gen.next()
         </Original>
         <Expanded>
-          104.0 != Approx( 100.0 )
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          100.3 != Approx(100.0)
+          gen.get() == "bb"
         </Original>
         <Expanded>
-          100.3 != Approx( 100.0 )
+          "bb" == "bb"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          100.3 == Approx(100.0).margin(0.5)
+          gen.next()
         </Original>
         <Expanded>
-          100.3 == Approx( 100.0 )
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="An empty test with no assertions" tags="[empty]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="An expression with side-effects should only be evaluated once" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          i++ == 7
+          gen.get() == "cc"
         </Original>
         <Expanded>
-          7 == 7
+          "cc" == "cc"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          i++ == 8
+          !(gen.next())
         </Original>
         <Expanded>
-          8 == 8
+          !false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="An unchecked exception reports the line of the last assertion" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <OverallResults successes="7" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Filter generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          1 == 1
+          gen.get() == 1
         </Original>
         <Expanded>
           1 == 1
         </Expanded>
       </Expression>
-      <Expression success="false" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          {Unknown expression after the reported line}
+          gen.next()
         </Original>
         <Expanded>
-          {Unknown expression after the reported line}
+          true
         </Expanded>
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          unexpected exception
-        </Exception>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Anonymous test case 1" filename="tests/<exe-name>/UsageTests/VariadicMacros.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Approx setters validate their arguments" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          Approx(0).margin(0)
+          gen.get() == 3
         </Original>
         <Expanded>
-          Approx(0).margin(0)
+          3 == 3
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          Approx(0).margin(1234656)
+          !(gen.next())
         </Original>
         <Expanded>
-          Approx(0).margin(1234656)
+          !false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          Approx(0).margin(-2), std::domain_error
+          filter([] (int) { return false; }, value(1)), Catch::GeneratorException
         </Original>
         <Expanded>
-          Approx(0).margin(-2), std::domain_error
+          filter([] (int) { return false; }, value(1)), Catch::GeneratorException
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(0).epsilon(0)
-        </Original>
-        <Expanded>
-          Approx(0).epsilon(0)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(0).epsilon(1)
-        </Original>
-        <Expanded>
-          Approx(0).epsilon(1)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(0).epsilon(-0.001), std::domain_error
-        </Original>
-        <Expanded>
-          Approx(0).epsilon(-0.001), std::domain_error
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(0).epsilon(1.0001), std::domain_error
-        </Original>
-        <Expanded>
-          Approx(0).epsilon(1.0001), std::domain_error
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Approx with exactly-representable margin" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          0.25f == Approx(0.0f).margin(0.25f)
-        </Original>
-        <Expanded>
-          0.25f == Approx( 0.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          0.0f == Approx(0.25f).margin(0.25f)
-        </Original>
-        <Expanded>
-          0.0f == Approx( 0.25 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          0.5f == Approx(0.25f).margin(0.25f)
-        </Original>
-        <Expanded>
-          0.5f == Approx( 0.25 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          245.0f == Approx(245.25f).margin(0.25f)
-        </Original>
-        <Expanded>
-          245.0f == Approx( 245.25 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          245.5f == Approx(245.25f).margin(0.25f)
-        </Original>
-        <Expanded>
-          245.5f == Approx( 245.25 )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Approximate PI" tags="[Approx][PI]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          divide( 22, 7 ) == Approx( 3.141 ).epsilon( 0.001 )
-        </Original>
-        <Expanded>
-          3.1428571429 == Approx( 3.141 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Take generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Take less" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Original>
+            gen.get() == 1
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Original>
+            gen.next()
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Original>
+            gen.get() == 2
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Original>
+            !(gen.next())
+          </Original>
+          <Expanded>
+            !false
+          </Expanded>
+        </Expression>
+        <OverallResults successes="4" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Take generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Take more" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Original>
+            gen.get() == 1
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Original>
+            !(gen.next())
+          </Original>
+          <Expanded>
+            !false
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Map with explicit return type" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          divide( 22, 7 ) != Approx( 3.141 ).epsilon( 0.0001 )
+          gen.get() == 2.0
         </Original>
         <Expanded>
-          3.1428571429 != Approx( 3.141 )
+          2.0 == 2.0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Approximate comparisons with different epsilons" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          d != Approx( 1.231 )
+          gen.next()
         </Original>
         <Expanded>
-          1.23 != Approx( 1.231 )
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          d == Approx( 1.231 ).epsilon( 0.1 )
+          gen.get() == 4.0
         </Original>
         <Expanded>
-          1.23 == Approx( 1.231 )
+          4.0 == 4.0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Approximate comparisons with floats" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          1.23f == Approx( 1.23f )
+          gen.next()
         </Original>
         <Expanded>
-          1.23f == Approx( 1.2300000191 )
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          0.0f == Approx( 0.0f )
+          gen.get() == 6.0
         </Original>
         <Expanded>
-          0.0f == Approx( 0.0 )
+          6.0 == 6.0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Approximate comparisons with ints" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          1 == Approx( 1 )
+          !(gen.next())
         </Original>
         <Expanded>
-          1 == Approx( 1.0 )
+          !false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Map with deduced return type" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          0 == Approx( 0 )
+          gen.get() == 2.0
         </Original>
         <Expanded>
-          0 == Approx( 0.0 )
+          2.0 == 2.0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Approximate comparisons with mixed numeric types" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          1.0f == Approx( 1 )
+          gen.next()
         </Original>
         <Expanded>
-          1.0f == Approx( 1.0 )
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          0 == Approx( dZero)
+          gen.get() == 4.0
         </Original>
         <Expanded>
-          0 == Approx( 0.0 )
+          4.0 == 4.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          0 == Approx( dSmall ).margin( 0.001 )
+          gen.next()
         </Original>
         <Expanded>
-          0 == Approx( 0.00001 )
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          1.234f == Approx( dMedium )
+          gen.get() == 6.0
         </Original>
         <Expanded>
-          1.234f == Approx( 1.234 )
+          6.0 == 6.0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
         <Original>
-          dMedium == Approx( 1.234f )
+          !(gen.next())
         </Original>
         <Expanded>
-          1.234 == Approx( 1.2339999676 )
+          !false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Arbitrary predicate matcher" tags="[generic][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="Function pointer" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Repeat" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Singular repeat" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            1, Predicate&lt;int>(alwaysTrue, "always true")
+            gen.get() == 3
           </Original>
           <Expanded>
-            1 matches predicate: "always true"
+            3 == 3
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            1, !Predicate&lt;int>(alwaysFalse, "always false")
+            !(gen.next())
           </Original>
           <Expanded>
-            1 not matches predicate: "always false"
+            !false
           </Expanded>
         </Expression>
         <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Lambdas + different type" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            "Hello olleH", Predicate&lt;std::string>( [] (std::string const&amp; str) -> bool { return str.front() == str.back(); }, "First and last character should be equal")
-          </Original>
-          <Expanded>
-            "Hello olleH" matches predicate: "First and last character should be equal"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Repeat" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Actual repeat" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            "This wouldn't pass", !Predicate&lt;std::string>( [] (std::string const&amp; str) -> bool { return str.front() == str.back(); } )
+            gen.get() == 1
           </Original>
           <Expanded>
-            "This wouldn't pass" not matches undescribed predicate
+            1 == 1
           </Expanded>
         </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Assertion macros support bit operators and bool conversions" tags="[bitops][compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-        <Original>
-          lhs | rhs
-        </Original>
-        <Expanded>
-          Val: 1 | Val: 2
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-        <Original>
-          !(lhs &amp; rhs)
-        </Original>
-        <Expanded>
-          !(Val: 1 &amp; Val: 2)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-        <Original>
-          HasBitOperators{ 1 } &amp; HasBitOperators{ 1 }
-        </Original>
-        <Expanded>
-          Val: 1 &amp; Val: 1
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-        <Original>
-          lhs ^ rhs
-        </Original>
-        <Expanded>
-          Val: 1 ^ Val: 2
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-        <Original>
-          !(lhs ^ lhs)
-        </Original>
-        <Expanded>
-          !(Val: 1 ^ Val: 1)
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Assertions then sections" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          true
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Section name="A section" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            true
+            gen.next()
           </Original>
           <Expanded>
             true
           </Expanded>
         </Expression>
-        <Section name="Another section" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-            <Original>
-              true
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          true
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Section name="A section" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            true
+            gen.get() == 2
           </Original>
           <Expanded>
-            true
+            2 == 2
           </Expanded>
         </Expression>
-        <Section name="Another other section" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-            <Original>
-              true
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Basic use of the Contains range matcher" tags="[contains][matchers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-      <Section name="Different argument ranges, same element type, default comparison" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            a, Contains(1)
+            gen.next()
           </Original>
           <Expanded>
-            { 1, 2, 3 } contains element 1
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            b, Contains(1)
+            gen.get() == 3
           </Original>
           <Expanded>
-            { 0, 1, 2 } contains element 1
+            3 == 3
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            c, !Contains(1)
+            gen.next()
           </Original>
           <Expanded>
-            { 4, 5, 6 } not contains element 1
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Different argument ranges, same element type, custom comparison" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            a, Contains(0, close_enough)
+            gen.get() == 1
           </Original>
           <Expanded>
-            { 1, 2, 3 } contains element 0
+            1 == 1
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            b, Contains(0, close_enough)
+            gen.next()
           </Original>
           <Expanded>
-            { 0, 1, 2 } contains element 0
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            c, !Contains(0, close_enough)
+            gen.get() == 2
           </Original>
           <Expanded>
-            { 4, 5, 6 } not contains element 0
+            2 == 2
           </Expanded>
         </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Different element type, custom comparisons" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            a, Contains(4, [](auto&amp;&amp; lhs, size_t sz) { return lhs.size() == sz; })
+            gen.next()
           </Original>
           <Expanded>
-            { "abc", "abcd", "abcde" } contains element 4
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Can handle type that requires ADL-found free function begin and end" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            in, Contains(1)
+            gen.get() == 3
           </Original>
           <Expanded>
-            { 1, 2, 3, 4, 5 } contains element 1
+            3 == 3
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
           <Original>
-            in, !Contains(8)
+            !(gen.next())
           </Original>
           <Expanded>
-            { 1, 2, 3, 4, 5 } not contains element 8
+            !false
           </Expanded>
         </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        <OverallResults successes="12" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Initialization with move only types" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            in, Contains(MoveOnlyTestElement{ 2 })
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } contains element 2
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            in, !Contains(MoveOnlyTestElement{ 9 })
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } not contains element 9
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Matching using matcher" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            in, Contains(Catch::Matchers::WithinAbs(0.5, 0.5))
-          </Original>
-          <Expanded>
-            { 1.0, 2.0, 3.0, 0.0 } contains element matching is within 0.5 of 0.5
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Basic use of the Empty range matcher" tags="[empty][matchers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-      <Section name="Simple, std-provided containers" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            empty_array, IsEmpty()
-          </Original>
-          <Expanded>
-            {  } is empty
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            non_empty_array, !IsEmpty()
-          </Original>
-          <Expanded>
-            { 0.0 } not is empty
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            empty_vec, IsEmpty()
-          </Original>
-          <Expanded>
-            {  } is empty
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            non_empty_vec, !IsEmpty()
-          </Original>
-          <Expanded>
-            { 'a', 'b', 'c' } not is empty
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            inner_lists_are_empty, !IsEmpty()
-          </Original>
-          <Expanded>
-            { {  } } not is empty
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            inner_lists_are_empty.front(), IsEmpty()
-          </Original>
-          <Expanded>
-            {  } is empty
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
+      <OverallResults successes="12" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Positive auto step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.get() == -2
+            </Original>
+            <Expanded>
+              -2 == -2
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.next()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.get() == -1
+            </Original>
+            <Expanded>
+              -1 == -1
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.next()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.get() == 0
+            </Original>
+            <Expanded>
+              0 == 0
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.next()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.get() == 1
+            </Original>
+            <Expanded>
+              1 == 1
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              !(gen.next())
+            </Original>
+            <Expanded>
+              !false
+            </Expanded>
+          </Expression>
+          <OverallResults successes="8" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="8" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Type with empty" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            has_empty{}, !IsEmpty()
-          </Original>
-          <Expanded>
-            {?} not is empty
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      <OverallResults successes="8" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Negative auto step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.get() == 2
+            </Original>
+            <Expanded>
+              2 == 2
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.next()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.get() == 1
+            </Original>
+            <Expanded>
+              1 == 1
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.next()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.get() == 0
+            </Original>
+            <Expanded>
+              0 == 0
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.next()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              gen.get() == -1
+            </Original>
+            <Expanded>
+              -1 == -1
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Original>
+              !(gen.next())
+            </Original>
+            <Expanded>
+              !false
+            </Expanded>
+          </Expression>
+          <OverallResults successes="8" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="8" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Type requires ADL found empty free function" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            unrelated::ADL_empty{}, IsEmpty()
-          </Original>
-          <Expanded>
-            {?} is empty
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      <OverallResults successes="8" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Section name="Exact" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -7
+              </Original>
+              <Expanded>
+                -7 == -7
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -4
+              </Original>
+              <Expanded>
+                -4 == -4
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -1
+              </Original>
+              <Expanded>
+                -1 == -1
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 2
+              </Original>
+              <Expanded>
+                2 == 2
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                !(gen.next())
+              </Original>
+              <Expanded>
+                !false
+              </Expanded>
+            </Expression>
+            <OverallResults successes="8" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="8" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="8" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="CAPTURE can deal with complex expressions" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        a := 1
-      </Info>
-      <Info>
-        b := 2
-      </Info>
-      <Info>
-        c := 3
-      </Info>
-      <Info>
-        a + b := 3
-      </Info>
-      <Info>
-        a+b := 3
-      </Info>
-      <Info>
-        c > b := true
-      </Info>
-      <Info>
-        a == 1 := true
-      </Info>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="CAPTURE can deal with complex expressions involving commas" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        std::vector&lt;int>{1, 2, 3}[0, 1, 2] := 3
-      </Info>
-      <Info>
-        std::vector&lt;int>{1, 2, 3}[(0, 1)] := 2
-      </Info>
-      <Info>
-        std::vector&lt;int>{1, 2, 3}[0] := 1
-      </Info>
-      <Info>
-        (helper_1436&lt;int, int>{12, -12}) := { 12, -12 }
-      </Info>
-      <Info>
-        (helper_1436&lt;int, int>(-12, 12)) := { -12, 12 }
-      </Info>
-      <Info>
-        (1, 2) := 2
-      </Info>
-      <Info>
-        (2, 3) := 3
-      </Info>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="CAPTURE parses string and character constants" tags="[capture][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        ("comma, in string", "escaped, \", ") := "escaped, ", "
-      </Info>
-      <Info>
-        "single quote in string,'," := "single quote in string,',"
-      </Info>
-      <Info>
-        "some escapes, \\,\\\\" := "some escapes, \,\\"
-      </Info>
-      <Info>
-        "some, ), unmatched, } prenheses {[&lt;" := "some, ), unmatched, } prenheses {[&lt;"
-      </Info>
-      <Info>
-        '"' := '"'
-      </Info>
-      <Info>
-        '\'' := '''
-      </Info>
-      <Info>
-        ',' := ','
-      </Info>
-      <Info>
-        '}' := '}'
-      </Info>
-      <Info>
-        ')' := ')'
-      </Info>
-      <Info>
-        '(' := '('
-      </Info>
-      <Info>
-        '{' := '{'
-      </Info>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Capture and info messages" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-      <Section name="Capture should stringify like assertions" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Info>
-          i := 2
-        </Info>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            true
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Info should NOT stringify the way assertions do" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Info>
-          3
-        </Info>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            true
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Character pretty printing" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-      <Section name="Specifically escaped" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            tab == '\t'
-          </Original>
-          <Expanded>
-            '\t' == '\t'
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            newline == '\n'
-          </Original>
-          <Expanded>
-            '\n' == '\n'
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            carr_return == '\r'
-          </Original>
-          <Expanded>
-            '\r' == '\r'
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            form_feed == '\f'
-          </Original>
-          <Expanded>
-            '\f' == '\f'
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
+      <OverallResults successes="8" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Section name="Slightly over end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -7
+              </Original>
+              <Expanded>
+                -7 == -7
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -4
+              </Original>
+              <Expanded>
+                -4 == -4
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -1
+              </Original>
+              <Expanded>
+                -1 == -1
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 2
+              </Original>
+              <Expanded>
+                2 == 2
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                !(gen.next())
+              </Original>
+              <Expanded>
+                !false
+              </Expanded>
+            </Expression>
+            <OverallResults successes="8" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="8" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="8" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="General chars" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            space == ' '
-          </Original>
-          <Expanded>
-            ' ' == ' '
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            c == chars[i]
-          </Original>
-          <Expanded>
-            'a' == 'a'
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            c == chars[i]
-          </Original>
-          <Expanded>
-            'z' == 'z'
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            c == chars[i]
-          </Original>
-          <Expanded>
-            'A' == 'A'
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            c == chars[i]
-          </Original>
-          <Expanded>
-            'Z' == 'Z'
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
+      <OverallResults successes="8" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Section name="Slightly under end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -7
+              </Original>
+              <Expanded>
+                -7 == -7
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -4
+              </Original>
+              <Expanded>
+                -4 == -4
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -1
+              </Original>
+              <Expanded>
+                -1 == -1
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 2
+              </Original>
+              <Expanded>
+                2 == 2
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 5
+              </Original>
+              <Expanded>
+                5 == 5
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                !(gen.next())
+              </Original>
+              <Expanded>
+                !false
+              </Expanded>
+            </Expression>
+            <OverallResults successes="10" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="10" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="10" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Low ASCII" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            null_terminator == '\0'
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            c == i
-          </Original>
-          <Expanded>
-            2 == 2
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            c == i
-          </Original>
-          <Expanded>
-            3 == 3
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            c == i
-          </Original>
-          <Expanded>
-            4 == 4
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            c == i
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Clara::Arg supports single-arg parse the way Opt does" tags="[arg][clara][compilation]" filename="tests/<exe-name>/IntrospectiveTests/Clara.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/Clara.tests.cpp" >
-        <Original>
-          name.empty()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Clara.tests.cpp" >
-        <Original>
-          name == "foo"
-        </Original>
-        <Expanded>
-          "foo" == "foo"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Combining MatchAllOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, (MatcherA() &amp;&amp; MatcherB()) &amp;&amp; MatcherC()
-        </Original>
-        <Expanded>
-          1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, MatcherA() &amp;&amp; (MatcherB() &amp;&amp; MatcherC())
-        </Original>
-        <Expanded>
-          1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, (MatcherA() &amp;&amp; MatcherB()) &amp;&amp; (MatcherC() &amp;&amp; MatcherD())
-        </Original>
-        <Expanded>
-          1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 and equals: (T) 1 and equals: true )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Combining MatchAnyOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, (MatcherA() || MatcherB()) || MatcherC()
-        </Original>
-        <Expanded>
-          1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, MatcherA() || (MatcherB() || MatcherC())
-        </Original>
-        <Expanded>
-          1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, (MatcherA() || MatcherB()) || (MatcherC() || MatcherD())
-        </Original>
-        <Expanded>
-          1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 or equals: (T) 1 or equals: true )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Combining MatchNotOfGeneric does not nest" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          0, !MatcherA()
-        </Original>
-        <Expanded>
-          0 not equals: (int) 1 or (float) 1.0f
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, !!MatcherA()
-        </Original>
-        <Expanded>
-          1 equals: (int) 1 or (float) 1.0f
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          0, !!!MatcherA()
-        </Original>
-        <Expanded>
-          0 not equals: (int) 1 or (float) 1.0f
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, !!!!MatcherA()
-        </Original>
-        <Expanded>
-          1 equals: (int) 1 or (float) 1.0f
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Combining concrete matchers does not use templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Combining only templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, MatcherA() || MatcherB()
-        </Original>
-        <Expanded>
-          1 ( equals: (int) 1 or (float) 1.0f or equals: (long long) 1 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, MatcherA() &amp;&amp; MatcherB()
-        </Original>
-        <Expanded>
-          1 ( equals: (int) 1 or (float) 1.0f and equals: (long long) 1 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          1, MatcherA() || !MatcherB()
-        </Original>
-        <Expanded>
-          1 ( equals: (int) 1 or (float) 1.0f or not equals: (long long) 1 )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Combining templated and concrete matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          vec, Predicate&lt;std::vector&lt;int>>([](auto const&amp; v) { return std::all_of(v.begin(), v.end(), [](int elem) { return elem % 2 == 1; }); }, "All elements are odd") &amp;&amp; !EqualsRange(a)
-        </Original>
-        <Expanded>
-          { 1, 3, 5 } ( matches predicate: "All elements are odd" and not Equals: { 5, 3, 1 } )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          str, StartsWith("foo") &amp;&amp; EqualsRange(arr) &amp;&amp; EndsWith("bar")
-        </Original>
-        <Expanded>
-          "foobar" ( starts with: "foo" and Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and ends with: "bar" )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          str, StartsWith("foo") &amp;&amp; !EqualsRange(bad_arr) &amp;&amp; EndsWith("bar")
-        </Original>
-        <Expanded>
-          "foobar" ( starts with: "foo" and not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and ends with: "bar" )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          str, EqualsRange(arr) &amp;&amp; StartsWith("foo") &amp;&amp; EndsWith("bar")
-        </Original>
-        <Expanded>
-          "foobar" ( Equals: { 'f', 'o', 'o', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          str, !EqualsRange(bad_arr) &amp;&amp; StartsWith("foo") &amp;&amp; EndsWith("bar")
-        </Original>
-        <Expanded>
-          "foobar" ( not Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } and starts with: "foo" and ends with: "bar" )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          str, EqualsRange(bad_arr) || (StartsWith("foo") &amp;&amp; EndsWith("bar"))
-        </Original>
-        <Expanded>
-          "foobar" ( Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } or ( starts with: "foo" and ends with: "bar" ) )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          str, (StartsWith("foo") &amp;&amp; EndsWith("bar")) || EqualsRange(bad_arr)
-        </Original>
-        <Expanded>
-          "foobar" ( ( starts with: "foo" and ends with: "bar" ) or Equals: { 'o', 'o', 'f', 'b', 'a', 'r' } )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Combining templated matchers" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c)
-        </Original>
-        <Expanded>
-          { 1, 2, 3 } ( Equals: { 1, 2, 3 } or Equals: { 0, 1, 2 } or Equals: { 4, 5, 6 } )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Commas in various macros are allowed" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          std::vector&lt;constructor_throws>{constructor_throws{}, constructor_throws{}}
-        </Original>
-        <Expanded>
-          std::vector&lt;constructor_throws>{constructor_throws{}, constructor_throws{}}
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          std::vector&lt;constructor_throws>{constructor_throws{}, constructor_throws{}}
-        </Original>
-        <Expanded>
-          std::vector&lt;constructor_throws>{constructor_throws{}, constructor_throws{}}
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          std::vector&lt;int>{1, 2, 3} == std::vector&lt;int>{1, 2, 3}
-        </Original>
-        <Expanded>
-          std::vector&lt;int>{1, 2, 3} == std::vector&lt;int>{1, 2, 3}
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_NOTHROW" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          std::vector&lt;int>{1, 2, 3} == std::vector&lt;int>{1, 2, 3}
-        </Original>
-        <Expanded>
-          std::vector&lt;int>{1, 2, 3} == std::vector&lt;int>{1, 2, 3}
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
-        </Original>
-        <Expanded>
-          { 1, 2 } == { 1, 2 }
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
-        </Original>
-        <Expanded>
-          { 1, 2 } == { 1, 2 }
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          !(std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2, 3})
-        </Original>
-        <Expanded>
-          !({ 1, 2 } == { 1, 2, 3 })
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          !(std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2, 3})
-        </Original>
-        <Expanded>
-          !({ 1, 2 } == { 1, 2, 3 })
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_NOFAIL" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
-        </Original>
-        <Expanded>
-          { 1, 2 } == { 1, 2 }
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECKED_IF" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
-        </Original>
-        <Expanded>
-          { 1, 2 } == { 1, 2 }
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          true
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          std::vector&lt;int>{1, 2} == std::vector&lt;int>{1, 2}
-        </Original>
-        <Expanded>
-          { 1, 2 } == { 1, 2 }
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Comparing function pointers" tags="[function pointer][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          a
-        </Original>
-        <Expanded>
-          0x<hex digits>
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          a == &amp;foo
-        </Original>
-        <Expanded>
-          0x<hex digits> == 0x<hex digits>
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Comparison ops" tags="[rng]" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-        <Original>
-          SimplePcg32{} == SimplePcg32{}
-        </Original>
-        <Expanded>
-          {?} == {?}
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-        <Original>
-          SimplePcg32{ 0 } != SimplePcg32{}
-        </Original>
-        <Expanded>
-          {?} != {?}
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-        <Original>
-          !(SimplePcg32{ 1 } == SimplePcg32{ 2 })
-        </Original>
-        <Expanded>
-          !({?} == {?})
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-        <Original>
-          !(SimplePcg32{ 1 } != SimplePcg32{ 1 })
-        </Original>
-        <Expanded>
-          !({?} != {?})
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Comparison with explicitly convertible types" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          td == Approx(10.0)
-        </Original>
-        <Expanded>
-          StrongDoubleTypedef(10) == Approx( 10.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(10.0) == td
-        </Original>
-        <Expanded>
-          Approx( 10.0 ) == StrongDoubleTypedef(10)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          td != Approx(11.0)
-        </Original>
-        <Expanded>
-          StrongDoubleTypedef(10) != Approx( 11.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(11.0) != td
-        </Original>
-        <Expanded>
-          Approx( 11.0 ) != StrongDoubleTypedef(10)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          td &lt;= Approx(10.0)
-        </Original>
-        <Expanded>
-          StrongDoubleTypedef(10) &lt;= Approx( 10.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          td &lt;= Approx(11.0)
-        </Original>
-        <Expanded>
-          StrongDoubleTypedef(10) &lt;= Approx( 11.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(10.0) &lt;= td
-        </Original>
-        <Expanded>
-          Approx( 10.0 ) &lt;= StrongDoubleTypedef(10)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(9.0) &lt;= td
-        </Original>
-        <Expanded>
-          Approx( 9.0 ) &lt;= StrongDoubleTypedef(10)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          td >= Approx(9.0)
-        </Original>
-        <Expanded>
-          StrongDoubleTypedef(10) >= Approx( 9.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          td >= Approx(td)
-        </Original>
-        <Expanded>
-          StrongDoubleTypedef(10) >= Approx( 10.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(td) >= td
-        </Original>
-        <Expanded>
-          Approx( 10.0 ) >= StrongDoubleTypedef(10)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx(11.0) >= td
-        </Original>
-        <Expanded>
-          Approx( 11.0 ) >= StrongDoubleTypedef(10)
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Comparisons between ints where one side is computed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          54 == 6*9
-        </Original>
-        <Expanded>
-          54 == 54
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Comparisons between unsigned ints and negative signed ints match c++ standard behaviour" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          ( -1 > 2u )
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          -1 > 2u
-        </Original>
-        <Expanded>
-          -1 > 2
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          ( 2u &lt; -1 )
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          2u &lt; -1
-        </Original>
-        <Expanded>
-          2 &lt; -1
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          ( minInt > 2u )
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          minInt > 2u
-        </Original>
-        <Expanded>
-          -2147483648 > 2
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Comparisons with int literals don't warn when mixing signed/ unsigned" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          i == 1
-        </Original>
-        <Expanded>
-          1 == 1
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          ui == 2
-        </Original>
-        <Expanded>
-          2 == 2
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          l == 3
-        </Original>
-        <Expanded>
-          3 == 3
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          ul == 4
-        </Original>
-        <Expanded>
-          4 == 4
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          c == 5
-        </Original>
-        <Expanded>
-          5 == 5
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          uc == 6
-        </Original>
-        <Expanded>
-          6 == 6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          1 == i
-        </Original>
-        <Expanded>
-          1 == 1
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          2 == ui
-        </Original>
-        <Expanded>
-          2 == 2
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          3 == l
-        </Original>
-        <Expanded>
-          3 == 3
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          4 == ul
-        </Original>
-        <Expanded>
-          4 == 4
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          5 == c
-        </Original>
-        <Expanded>
-          5 == 5
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          6 == uc
-        </Original>
-        <Expanded>
-          6 == 6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          (std::numeric_limits&lt;uint32_t>::max)() > ul
-        </Original>
-        <Expanded>
-          4294967295 (0x<hex digits>) > 4
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Composed generic matchers shortcircuit" tags="[composed][generic][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="MatchAllOf" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            !(matcher.match( 1 ))
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            first.matchCalled
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            !second.matchCalled
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="MatchAnyOf" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            matcher.match(1)
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            first.matchCalled
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            !second.matchCalled
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Composed matchers shortcircuit" tags="[composed][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="MatchAllOf" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            !(matcher.match( 1 ))
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            first.matchCalled
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            !second.matchCalled
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="MatchAnyOf" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            matcher.match( 1 )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            first.matchCalled
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            !second.matchCalled
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Contains string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Contains("not there", Catch::CaseSensitive::No)
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" contains: "not there" (case insensitive)
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Contains("STRING")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" contains: "STRING"
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Copy and then generate a range" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-      <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="from var and iterators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="From a temporary container" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            elem % 2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Final validation" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            call_count == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            make_data().size() == test_count
-          </Original>
-          <Expanded>
-            6 == 6
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Custom exceptions can be translated when testing for nothrow" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Expression success="false" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Original>
-          throwCustom()
-        </Original>
-        <Expanded>
-          throwCustom()
-        </Expanded>
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          custom exception - not std
-        </Exception>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Custom exceptions can be translated when testing for throwing as something else" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Expression success="false" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Original>
-          throwCustom(), std::exception
-        </Original>
-        <Expanded>
-          throwCustom(), std::exception
-        </Expanded>
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          custom exception - not std
-        </Exception>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Custom std-exceptions can be custom translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        custom std exception
-      </Exception>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Default scale is invisible to comparison" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          101.000001 != Approx(100).epsilon(0.01)
-        </Original>
-        <Expanded>
-          101.000001 != Approx( 100.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          std::pow(10, -5) != Approx(std::pow(10, -7))
-        </Original>
-        <Expanded>
-          0.00001 != Approx( 0.0000001 )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Directly creating an EnumInfo" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-        <Original>
-          enumInfo->lookup(0) == "Value1"
-        </Original>
-        <Expanded>
-          Value1 == "Value1"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-        <Original>
-          enumInfo->lookup(1) == "Value2"
-        </Original>
-        <Expanded>
-          Value2 == "Value2"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-        <Original>
-          enumInfo->lookup(3) == "{** unexpected enum value **}"
-        </Original>
-        <Expanded>
-          {** unexpected enum value **}
-==
-"{** unexpected enum value **}"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="EndsWith string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), EndsWith("Substring")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" ends with: "Substring"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No)
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" ends with: "this" (case insensitive)
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Enums can quickly have stringification enabled using REGISTER_ENUM" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-        <Original>
-          stringify( EnumClass3::Value1 ) == "Value1"
-        </Original>
-        <Expanded>
-          "Value1" == "Value1"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-        <Original>
-          stringify( EnumClass3::Value2 ) == "Value2"
-        </Original>
-        <Expanded>
-          "Value2" == "Value2"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-        <Original>
-          stringify( EnumClass3::Value3 ) == "Value3"
-        </Original>
-        <Expanded>
-          "Value3" == "Value3"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-        <Original>
-          stringify( EnumClass3::Value4 ) == "{** unexpected enum value **}"
-        </Original>
-        <Expanded>
-          "{** unexpected enum value **}"
-==
-"{** unexpected enum value **}"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-        <Original>
-          stringify( ec3 ) == "Value2"
-        </Original>
-        <Expanded>
-          "Value2" == "Value2"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Enums in namespaces can quickly have stringification enabled using REGISTER_ENUM" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-        <Original>
-          stringify( Bikeshed::Colours::Red ) == "Red"
-        </Original>
-        <Expanded>
-          "Red" == "Red"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-        <Original>
-          stringify( Bikeshed::Colours::Blue ) == "Blue"
-        </Original>
-        <Expanded>
-          "Blue" == "Blue"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Epsilon only applies to Approx's value" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          101.01 != Approx(100).epsilon(0.01)
-        </Original>
-        <Expanded>
-          101.01 != Approx( 100.0 )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Equality checks that should fail" tags="[!mayfail][.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven == 6
-        </Original>
-        <Expanded>
-          7 == 6
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven == 8
-        </Original>
-        <Expanded>
-          7 == 8
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven == 0
-        </Original>
-        <Expanded>
-          7 == 0
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one == Approx( 9.11f )
-        </Original>
-        <Expanded>
-          9.1f == Approx( 9.1099996567 )
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one == Approx( 9.0f )
-        </Original>
-        <Expanded>
-          9.1f == Approx( 9.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one == Approx( 1 )
-        </Original>
-        <Expanded>
-          9.1f == Approx( 1.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one == Approx( 0 )
-        </Original>
-        <Expanded>
-          9.1f == Approx( 0.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.double_pi == Approx( 3.1415 )
-        </Original>
-        <Expanded>
-          3.1415926535 == Approx( 3.1415 )
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello == "goodbye"
-        </Original>
-        <Expanded>
-          "hello" == "goodbye"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello == "hell"
-        </Original>
-        <Expanded>
-          "hello" == "hell"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello == "hello1"
-        </Original>
-        <Expanded>
-          "hello" == "hello1"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello.size() == 6
-        </Original>
-        <Expanded>
-          5 == 6
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          x == Approx( 1.301 )
-        </Original>
-        <Expanded>
-          1.3 == Approx( 1.301 )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Equality checks that should succeed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven == 7
-        </Original>
-        <Expanded>
-          7 == 7
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one == Approx( 9.1f )
-        </Original>
-        <Expanded>
-          9.1f == Approx( 9.1000003815 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.double_pi == Approx( 3.1415926535 )
-        </Original>
-        <Expanded>
-          3.1415926535 == Approx( 3.1415926535 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello == "hello"
-        </Original>
-        <Expanded>
-          "hello" == "hello"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          "hello" == data.str_hello
-        </Original>
-        <Expanded>
-          "hello" == "hello"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello.size() == 5
-        </Original>
-        <Expanded>
-          5 == 5
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          x == Approx( 1.3 )
-        </Original>
-        <Expanded>
-          1.3 == Approx( 1.3 )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Equals" tags="[matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Equals("this string contains 'abc' as a substring")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Equals("this string contains 'ABC' as a substring", Catch::CaseSensitive::No)
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" equals: "this string contains 'abc' as a substring" (case insensitive)
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Equals string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Equals("this string contains 'ABC' as a substring")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" equals: "this string contains 'ABC' as a substring"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Equals("something else", Catch::CaseSensitive::No)
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" equals: "something else" (case insensitive)
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Exception as a value (e.g. in REQUIRE_THROWS_MATCHES) can be stringified" tags="[exception][toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Original>
-          ::Catch::Detail::stringify(WhatException{}) == "This exception has overridden what() method"
-        </Original>
-        <Expanded>
-          "This exception has overridden what() method"
-==
-"This exception has overridden what() method"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Original>
-          ::Catch::Detail::stringify(OperatorException{}) == "OperatorException"
-        </Original>
-        <Expanded>
-          "OperatorException" == "OperatorException"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Original>
-          ::Catch::Detail::stringify(StringMakerException{}) == "StringMakerException"
-        </Original>
-        <Expanded>
-          "StringMakerException"
-==
-"StringMakerException"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Exception matchers that fail" tags="[!throws][.][exceptions][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="No exception" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="false" type="CHECK_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            doesNotThrow(), SpecialException, ExceptionMatcher{1}
-          </Original>
-          <Expanded>
-            doesNotThrow(), SpecialException, ExceptionMatcher{1}
-          </Expanded>
-        </Expression>
-        <Expression success="false" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            doesNotThrow(), SpecialException, ExceptionMatcher{1}
-          </Original>
-          <Expanded>
-            doesNotThrow(), SpecialException, ExceptionMatcher{1}
-          </Expanded>
-        </Expression>
-        <OverallResults successes="0" failures="2" expectedFailures="0"/>
-      </Section>
-      <Section name="Type mismatch" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="false" type="CHECK_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            throwsAsInt(1), SpecialException, ExceptionMatcher{1}
-          </Original>
-          <Expanded>
-            throwsAsInt(1), SpecialException, ExceptionMatcher{1}
-          </Expanded>
-          <Exception filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            Unknown exception
-          </Exception>
-        </Expression>
-        <Expression success="false" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            throwsAsInt(1), SpecialException, ExceptionMatcher{1}
-          </Original>
-          <Expanded>
-            throwsAsInt(1), SpecialException, ExceptionMatcher{1}
-          </Expanded>
-          <Exception filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            Unknown exception
-          </Exception>
-        </Expression>
-        <OverallResults successes="0" failures="2" expectedFailures="0"/>
-      </Section>
-      <Section name="Contents are wrong" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="false" type="CHECK_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            throwsSpecialException(3), SpecialException, ExceptionMatcher{1}
-          </Original>
-          <Expanded>
-            SpecialException::what special exception has value of 1
-          </Expanded>
-        </Expression>
-        <Expression success="false" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            throwsSpecialException(4), SpecialException, ExceptionMatcher{1}
-          </Original>
-          <Expanded>
-            SpecialException::what special exception has value of 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="0" failures="2" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Exception matchers that succeed" tags="[!throws][exceptions][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="CHECK_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          throwsSpecialException(1), SpecialException, ExceptionMatcher{1}
-        </Original>
-        <Expanded>
-          SpecialException::what special exception has value of 1
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          throwsSpecialException(2), SpecialException, ExceptionMatcher{2}
-        </Original>
-        <Expanded>
-          SpecialException::what special exception has value of 2
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Exception messages can be tested for" tags="[!throws]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Section name="exact match" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          <Original>
-            thisThrows(), "expected exception"
-          </Original>
-          <Expanded>
-            "expected exception" equals: "expected exception"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="different case" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          <Original>
-            thisThrows(), Equals( "expecteD Exception", Catch::CaseSensitive::No )
-          </Original>
-          <Expanded>
-            "expected exception" equals: "expected exception" (case insensitive)
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="wildcarded" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          <Original>
-            thisThrows(), StartsWith( "expected" )
-          </Original>
-          <Expanded>
-            "expected exception" starts with: "expected"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          <Original>
-            thisThrows(), EndsWith( "exception" )
-          </Original>
-          <Expanded>
-            "expected exception" ends with: "exception"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          <Original>
-            thisThrows(), Contains( "except" )
-          </Original>
-          <Expanded>
-            "expected exception" contains: "except"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          <Original>
-            thisThrows(), Contains( "exCept", Catch::CaseSensitive::No )
-          </Original>
-          <Expanded>
-            "expected exception" contains: "except" (case insensitive)
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Exceptions matchers" tags="[!throws][exceptions][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          throwsDerivedException(), DerivedException, Message("DerivedException::what")
-        </Original>
-        <Expanded>
-          DerivedException::what exception message matches "DerivedException::what"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          throwsDerivedException(), DerivedException, !Message("derivedexception::what")
-        </Original>
-        <Expanded>
-          DerivedException::what not exception message matches "derivedexception::what"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          throwsSpecialException(2), SpecialException, !Message("DerivedException::what")
-        </Original>
-        <Expanded>
-          SpecialException::what not exception message matches "DerivedException::what"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THROWS_MATCHES" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          throwsSpecialException(2), SpecialException, Message("SpecialException::what")
-        </Original>
-        <Expanded>
-          SpecialException::what exception message matches "SpecialException::what"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Expected exceptions that don't throw or unexpected exceptions fail the test" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Expression success="false" type="CHECK_THROWS_AS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Original>
-          thisThrows(), std::string
-        </Original>
-        <Expanded>
-          thisThrows(), std::string
-        </Expanded>
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          expected exception
-        </Exception>
-      </Expression>
-      <Expression success="false" type="CHECK_THROWS_AS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Original>
-          thisDoesntThrow(), std::domain_error
-        </Original>
-        <Expanded>
-          thisDoesntThrow(), std::domain_error
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Original>
-          thisThrows()
-        </Original>
-        <Expanded>
-          thisThrows()
-        </Expanded>
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          expected exception
-        </Exception>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="FAIL aborts the test" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        This is a failure
-      </Failure>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="FAIL does not require an argument" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" />
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="FAIL_CHECK does not abort the test" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        This is a failure
-      </Failure>
-      <Warning>
-        This message appears in the output
-      </Warning>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Factorials are computed" tags="[factorial]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          Factorial(0) == 1
-        </Original>
-        <Expanded>
-          1 == 1
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          Factorial(1) == 1
-        </Original>
-        <Expanded>
-          1 == 1
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          Factorial(2) == 2
-        </Original>
-        <Expanded>
-          2 == 2
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          Factorial(3) == 6
-        </Original>
-        <Expanded>
-          6 == 6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          Factorial(10) == 3628800
-        </Original>
-        <Expanded>
-          3628800 (0x<hex digits>) == 3628800 (0x<hex digits>)
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Floating point matchers: double" tags="[floating-point][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="Relative" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            10., WithinRel(11.1, 0.1)
-          </Original>
-          <Expanded>
-            10.0 and 11.1 are within 10% of each other
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            10., !WithinRel(11.2, 0.1)
-          </Original>
-          <Expanded>
-            10.0 not and 11.2 are within 10% of each other
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1., !WithinRel(0., 0.99)
-          </Original>
-          <Expanded>
-            1.0 not and 0 are within 99% of each other
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            -0., WithinRel(0.)
-          </Original>
-          <Expanded>
-            -0.0 and 0 are within 2.22045e-12% of each other
-          </Expanded>
-        </Expression>
-        <Section name="Some subnormal values" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            <Original>
-              v1, WithinRel(v2)
-            </Original>
-            <Expanded>
-              0.0 and 2.22507e-308 are within 2.22045e-12% of each other
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Margin" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1., WithinAbs(1., 0)
-          </Original>
-          <Expanded>
-            1.0 is within 0.0 of 1.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0., WithinAbs(1., 1)
-          </Original>
-          <Expanded>
-            0.0 is within 1.0 of 1.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0., !WithinAbs(1., 0.99)
-          </Original>
-          <Expanded>
-            0.0 not is within 0.99 of 1.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0., !WithinAbs(1., 0.99)
-          </Original>
-          <Expanded>
-            0.0 not is within 0.99 of 1.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            11., !WithinAbs(10., 0.5)
-          </Original>
-          <Expanded>
-            11.0 not is within 0.5 of 10.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            10., !WithinAbs(11., 0.5)
-          </Original>
-          <Expanded>
-            10.0 not is within 0.5 of 11.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            -10., WithinAbs(-10., 0.5)
-          </Original>
-          <Expanded>
-            -10.0 is within 0.5 of -10.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            -10., WithinAbs(-9.6, 0.5)
-          </Original>
-          <Expanded>
-            -10.0 is within 0.5 of -9.6
-          </Expanded>
-        </Expression>
-        <OverallResults successes="8" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="ULPs" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1., WithinULP(1., 0)
-          </Original>
-          <Expanded>
-            1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            nextafter(1., 2.), WithinULP(1., 1)
-          </Original>
-          <Expanded>
-            1.0 is within 1 ULPs of 1.0000000000000000e+00 ([9.9999999999999989e-01, 1.0000000000000002e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0., WithinULP(nextafter(0., 1.), 1)
-          </Original>
-          <Expanded>
-            0.0 is within 1 ULPs of 4.9406564584124654e-324 ([0.0000000000000000e+00, 9.8813129168249309e-324])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1., WithinULP(nextafter(1., 0.), 1)
-          </Original>
-          <Expanded>
-            1.0 is within 1 ULPs of 9.9999999999999989e-01 ([9.9999999999999978e-01, 1.0000000000000000e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1., !WithinULP(nextafter(1., 2.), 0)
-          </Original>
-          <Expanded>
-            1.0 not is within 0 ULPs of 1.0000000000000002e+00 ([1.0000000000000002e+00, 1.0000000000000002e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1., WithinULP(1., 0)
-          </Original>
-          <Expanded>
-            1.0 is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            -0., WithinULP(0., 0)
-          </Original>
-          <Expanded>
-            -0.0 is within 0 ULPs of 0.0000000000000000e+00 ([0.0000000000000000e+00, 0.0000000000000000e+00])
-          </Expanded>
-        </Expression>
-        <OverallResults successes="7" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Composed" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1., WithinAbs(1., 0.5) || WithinULP(2., 1)
-          </Original>
-          <Expanded>
-            1.0 ( is within 0.5 of 1.0 or is within 1 ULPs of 2.0000000000000000e+00 ([1.9999999999999998e+00, 2.0000000000000004e+00]) )
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1., WithinAbs(2., 0.5) || WithinULP(1., 0)
-          </Original>
-          <Expanded>
-            1.0 ( is within 0.5 of 2.0 or is within 0 ULPs of 1.0000000000000000e+00 ([1.0000000000000000e+00, 1.0000000000000000e+00]) )
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0.0001, WithinAbs(0., 0.001) || WithinRel(0., 0.1)
-          </Original>
-          <Expanded>
-            0.0001 ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Constructor validation" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinAbs(1., 0.)
-          </Original>
-          <Expanded>
-            WithinAbs(1., 0.)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinAbs(1., -1.), std::domain_error
-          </Original>
-          <Expanded>
-            WithinAbs(1., -1.), std::domain_error
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinULP(1., 0)
-          </Original>
-          <Expanded>
-            WithinULP(1., 0)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinRel(1., 0.)
-          </Original>
-          <Expanded>
-            WithinRel(1., 0.)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinRel(1., -0.2), std::domain_error
-          </Original>
-          <Expanded>
-            WithinRel(1., -0.2), std::domain_error
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinRel(1., 1.), std::domain_error
-          </Original>
-          <Expanded>
-            WithinRel(1., 1.), std::domain_error
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Floating point matchers: float" tags="[floating-point][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="Relative" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            10.f, WithinRel(11.1f, 0.1f)
-          </Original>
-          <Expanded>
-            10.0f and 11.1 are within 10% of each other
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            10.f, !WithinRel(11.2f, 0.1f)
-          </Original>
-          <Expanded>
-            10.0f not and 11.2 are within 10% of each other
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1.f, !WithinRel(0.f, 0.99f)
-          </Original>
-          <Expanded>
-            1.0f not and 0 are within 99% of each other
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            -0.f, WithinRel(0.f)
-          </Original>
-          <Expanded>
-            -0.0f and 0 are within 0.00119209% of each other
-          </Expanded>
-        </Expression>
-        <Section name="Some subnormal values" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            <Original>
-              v1, WithinRel(v2)
-            </Original>
-            <Expanded>
-              0.0f and 1.17549e-38 are within 0.00119209% of each other
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Margin" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1.f, WithinAbs(1.f, 0)
-          </Original>
-          <Expanded>
-            1.0f is within 0.0 of 1.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0.f, WithinAbs(1.f, 1)
-          </Original>
-          <Expanded>
-            0.0f is within 1.0 of 1.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0.f, !WithinAbs(1.f, 0.99f)
-          </Original>
-          <Expanded>
-            0.0f not is within 0.9900000095 of 1.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0.f, !WithinAbs(1.f, 0.99f)
-          </Original>
-          <Expanded>
-            0.0f not is within 0.9900000095 of 1.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0.f, WithinAbs(-0.f, 0)
-          </Original>
-          <Expanded>
-            0.0f is within 0.0 of -0.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            11.f, !WithinAbs(10.f, 0.5f)
-          </Original>
-          <Expanded>
-            11.0f not is within 0.5 of 10.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            10.f, !WithinAbs(11.f, 0.5f)
-          </Original>
-          <Expanded>
-            10.0f not is within 0.5 of 11.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            -10.f, WithinAbs(-10.f, 0.5f)
-          </Original>
-          <Expanded>
-            -10.0f is within 0.5 of -10.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            -10.f, WithinAbs(-9.6f, 0.5f)
-          </Original>
-          <Expanded>
-            -10.0f is within 0.5 of -9.6000003815
-          </Expanded>
-        </Expression>
-        <OverallResults successes="9" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="ULPs" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1.f, WithinULP(1.f, 0)
-          </Original>
-          <Expanded>
-            1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            nextafter(1.f, 2.f), WithinULP(1.f, 1)
-          </Original>
-          <Expanded>
-            1.0f is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0.f, WithinULP(nextafter(0.f, 1.f), 1)
-          </Original>
-          <Expanded>
-            0.0f is within 1 ULPs of 1.40129846e-45f ([0.00000000e+00, 2.80259693e-45])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1.f, WithinULP(nextafter(1.f, 0.f), 1)
-          </Original>
-          <Expanded>
-            1.0f is within 1 ULPs of 9.99999940e-01f ([9.99999881e-01, 1.00000000e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1.f, !WithinULP(nextafter(1.f, 2.f), 0)
-          </Original>
-          <Expanded>
-            1.0f not is within 0 ULPs of 1.00000012e+00f ([1.00000012e+00, 1.00000012e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1.f, WithinULP(1.f, 0)
-          </Original>
-          <Expanded>
-            1.0f is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00])
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            -0.f, WithinULP(0.f, 0)
-          </Original>
-          <Expanded>
-            -0.0f is within 0 ULPs of 0.00000000e+00f ([0.00000000e+00, 0.00000000e+00])
-          </Expanded>
-        </Expression>
-        <OverallResults successes="7" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Composed" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1)
-          </Original>
-          <Expanded>
-            1.0f ( is within 0.5 of 1.0 or is within 1 ULPs of 1.00000000e+00f ([9.99999940e-01, 1.00000012e+00]) )
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0)
-          </Original>
-          <Expanded>
-            1.0f ( is within 0.5 of 2.0 or is within 0 ULPs of 1.00000000e+00f ([1.00000000e+00, 1.00000000e+00]) )
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            0.0001f, WithinAbs(0.f, 0.001f) || WithinRel(0.f, 0.1f)
-          </Original>
-          <Expanded>
-            0.0001f ( is within 0.001 of 0.0 or and 0 are within 10% of each other )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Constructor validation" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinAbs(1.f, 0.f)
-          </Original>
-          <Expanded>
-            WithinAbs(1.f, 0.f)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinAbs(1.f, -1.f), std::domain_error
-          </Original>
-          <Expanded>
-            WithinAbs(1.f, -1.f), std::domain_error
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinULP(1.f, 0)
-          </Original>
-          <Expanded>
-            WithinULP(1.f, 0)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinULP(1.f, static_cast&lt;uint64_t>(-1)), std::domain_error
-          </Original>
-          <Expanded>
-            WithinULP(1.f, static_cast&lt;uint64_t>(-1)), std::domain_error
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinRel(1.f, 0.f)
-          </Original>
-          <Expanded>
-            WithinRel(1.f, 0.f)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinRel(1.f, -0.2f), std::domain_error
-          </Original>
-          <Expanded>
-            WithinRel(1.f, -0.2f), std::domain_error
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            WithinRel(1.f, 1.f), std::domain_error
-          </Original>
-          <Expanded>
-            WithinRel(1.f, 1.f), std::domain_error
-          </Expanded>
-        </Expression>
-        <OverallResults successes="7" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Generators -- adapters" tags="[generators][generic]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-      <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i % 2 == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i % 2 == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i % 2 == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Filtering by predicate" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Throws if there are no matching values" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              filter([] (int) {return false; }, value(1)), Catch::GeneratorException
-            </Original>
-            <Expanded>
-              filter([] (int) {return false; }, value(1)), Catch::GeneratorException
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Shortening a range" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            i &lt; 4
-          </Original>
-          <Expanded>
-            1 &lt; 4
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Shortening a range" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            i &lt; 4
-          </Original>
-          <Expanded>
-            2 &lt; 4
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Shortening a range" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            i &lt; 4
-          </Original>
-          <Expanded>
-            3 &lt; 4
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Same type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i % 2 == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Same type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i % 2 == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Same type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i % 2 == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Different type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i.size() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Different type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i.size() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Different type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i.size() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Different deduced type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i.size() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Different deduced type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i.size() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Transforming elements" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Different deduced type" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              i.size() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j > 0
-          </Original>
-          <Expanded>
-            1 > 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j > 0
-          </Original>
-          <Expanded>
-            2 > 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j > 0
-          </Original>
-          <Expanded>
-            3 > 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j > 0
-          </Original>
-          <Expanded>
-            1 > 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j > 0
-          </Original>
-          <Expanded>
-            2 > 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Repeating a generator" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j > 0
-          </Original>
-          <Expanded>
-            3 > 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Number of elements in source is divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.size() == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.front() == chunk2.back()
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Number of elements in source is divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.size() == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.front() == chunk2.back()
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Number of elements in source is divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.size() == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.front() == chunk2.back()
-            </Original>
-            <Expanded>
-              3 == 3
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Number of elements in source is not divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.size() == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.front() == chunk2.back()
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.front() &lt; 3
-            </Original>
-            <Expanded>
-              1 &lt; 3
-            </Expanded>
-          </Expression>
-          <OverallResults successes="3" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Number of elements in source is not divisible by chunk size" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.size() == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.front() == chunk2.back()
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.front() &lt; 3
-            </Original>
-            <Expanded>
-              2 &lt; 3
-            </Expanded>
-          </Expression>
-          <OverallResults successes="3" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Chunk size of zero" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.size() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Chunk size of zero" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.size() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Chunk size of zero" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk2.size() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Chunking a generator into sized pieces" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Section name="Throws on too small generators" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-            <Original>
-              chunk(2, value(1)), Catch::GeneratorException
-            </Original>
-            <Expanded>
-              chunk(2, value(1)), Catch::GeneratorException
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Generators -- simple" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j &lt; i
-          </Original>
-          <Expanded>
-            -3 &lt; 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j &lt; i
-          </Original>
-          <Expanded>
-            -2 &lt; 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j &lt; i
-          </Original>
-          <Expanded>
-            -1 &lt; 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            4u * i > str.size()
-          </Original>
-          <Expanded>
-            4 > 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            4u * i > str.size()
-          </Original>
-          <Expanded>
-            4 > 2
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            4u * i > str.size()
-          </Original>
-          <Expanded>
-            4 > 3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j &lt; i
-          </Original>
-          <Expanded>
-            -3 &lt; 2
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j &lt; i
-          </Original>
-          <Expanded>
-            -2 &lt; 2
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j &lt; i
-          </Original>
-          <Expanded>
-            -1 &lt; 2
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            4u * i > str.size()
-          </Original>
-          <Expanded>
-            8 > 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            4u * i > str.size()
-          </Original>
-          <Expanded>
-            8 > 2
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            4u * i > str.size()
-          </Original>
-          <Expanded>
-            8 > 3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j &lt; i
-          </Original>
-          <Expanded>
-            -3 &lt; 3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j &lt; i
-          </Original>
-          <Expanded>
-            -2 &lt; 3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            j &lt; i
-          </Original>
-          <Expanded>
-            -1 &lt; 3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            4u * i > str.size()
-          </Original>
-          <Expanded>
-            12 > 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            4u * i > str.size()
-          </Original>
-          <Expanded>
-            12 > 2
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-          <Original>
-            4u * i > str.size()
-          </Original>
-          <Expanded>
-            12 > 3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Generators internals" tags="[generators][internals]" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-      <Section name="Single value" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 123
-          </Original>
-          <Expanded>
-            123 == 123
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            !(gen.next())
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Preset values" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 3
-          </Original>
-          <Expanded>
-            3 == 3
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            !(gen.next())
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Generator combinator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 2
-          </Original>
-          <Expanded>
-            2 == 2
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 4
-          </Original>
-          <Expanded>
-            4 == 4
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            !(gen.next())
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="10" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Explicitly typed generator sequence" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get().size() == 2
-          </Original>
-          <Expanded>
-            2 == 2
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == "aa"
-          </Original>
-          <Expanded>
-            "aa" == "aa"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == "bb"
-          </Original>
-          <Expanded>
-            "bb" == "bb"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == "cc"
-          </Original>
-          <Expanded>
-            "cc" == "cc"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            !(gen.next())
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="7" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Filter generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 3
-          </Original>
-          <Expanded>
-            3 == 3
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            !(gen.next())
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            filter([] (int) { return false; }, value(1)), Catch::GeneratorException
-          </Original>
-          <Expanded>
-            filter([] (int) { return false; }, value(1)), Catch::GeneratorException
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Take generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Take less" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.next()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              !(gen.next())
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <OverallResults successes="4" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Take generator" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Take more" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              !(gen.next())
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Map with explicit return type" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 2.0
-          </Original>
-          <Expanded>
-            2.0 == 2.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 4.0
-          </Original>
-          <Expanded>
-            4.0 == 4.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 6.0
-          </Original>
-          <Expanded>
-            6.0 == 6.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            !(gen.next())
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Map with deduced return type" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 2.0
-          </Original>
-          <Expanded>
-            2.0 == 2.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 4.0
-          </Original>
-          <Expanded>
-            4.0 == 4.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.next()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            gen.get() == 6.0
-          </Original>
-          <Expanded>
-            6.0 == 6.0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Original>
-            !(gen.next())
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Repeat" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Singular repeat" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 3
-            </Original>
-            <Expanded>
-              3 == 3
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              !(gen.next())
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Repeat" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Actual repeat" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.next()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.next()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 3
-            </Original>
-            <Expanded>
-              3 == 3
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.next()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.next()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.next()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              gen.get() == 3
-            </Original>
-            <Expanded>
-              3 == 3
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Original>
-              !(gen.next())
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <OverallResults successes="12" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="12" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Positive auto step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.get() == -2
-              </Original>
-              <Expanded>
-                -2 == -2
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.next()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.get() == -1
-              </Original>
-              <Expanded>
-                -1 == -1
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.next()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.get() == 0
-              </Original>
-              <Expanded>
-                0 == 0
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.next()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.get() == 1
-              </Original>
-              <Expanded>
-                1 == 1
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                !(gen.next())
-              </Original>
-              <Expanded>
-                !false
-              </Expanded>
-            </Expression>
-            <OverallResults successes="8" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="8" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="8" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Negative auto step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.get() == 2
-              </Original>
-              <Expanded>
-                2 == 2
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.next()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.get() == 1
-              </Original>
-              <Expanded>
-                1 == 1
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.next()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.get() == 0
-              </Original>
-              <Expanded>
-                0 == 0
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.next()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                gen.get() == -1
-              </Original>
-              <Expanded>
-                -1 == -1
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Original>
-                !(gen.next())
-              </Original>
-              <Expanded>
-                !false
-              </Expanded>
-            </Expression>
-            <OverallResults successes="8" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="8" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="8" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Section name="Exact" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -7
-                </Original>
-                <Expanded>
-                  -7 == -7
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -4
-                </Original>
-                <Expanded>
-                  -4 == -4
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -1
-                </Original>
-                <Expanded>
-                  -1 == -1
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 2
-                </Original>
-                <Expanded>
-                  2 == 2
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  !(gen.next())
-                </Original>
-                <Expanded>
-                  !false
-                </Expanded>
-              </Expression>
-              <OverallResults successes="8" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="8" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="8" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="8" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Section name="Slightly over end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -7
-                </Original>
-                <Expanded>
-                  -7 == -7
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -4
-                </Original>
-                <Expanded>
-                  -4 == -4
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -1
-                </Original>
-                <Expanded>
-                  -1 == -1
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 2
-                </Original>
-                <Expanded>
-                  2 == 2
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  !(gen.next())
-                </Original>
-                <Expanded>
-                  !false
-                </Expanded>
-              </Expression>
-              <OverallResults successes="8" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="8" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="8" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="8" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Section name="Slightly under end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -7
-                </Original>
-                <Expanded>
-                  -7 == -7
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -4
-                </Original>
-                <Expanded>
-                  -4 == -4
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -1
-                </Original>
-                <Expanded>
-                  -1 == -1
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 2
-                </Original>
-                <Expanded>
-                  2 == 2
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 5
-                </Original>
-                <Expanded>
-                  5 == 5
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  !(gen.next())
-                </Original>
-                <Expanded>
-                  !false
-                </Expanded>
-              </Expression>
-              <OverallResults successes="10" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="10" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="10" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="10" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Floating Point" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Section name="Exact" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Info>
-                Current expected value is -1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -1.0 == Approx( -1.0 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.9
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.9 == Approx( -0.9 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.9
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.8
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.8 == Approx( -0.8 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.8
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.7
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.7 == Approx( -0.7 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.7
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.6
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.6 == Approx( -0.6 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.6
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.5
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.5 == Approx( -0.5 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.5
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.4
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.4 == Approx( -0.4 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.4
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.3
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.3 == Approx( -0.3 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.3
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.2
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.2 == Approx( -0.2 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.2
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.1 == Approx( -0.1 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -1.38778e-16
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.0 == Approx( -0.0 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -1.38778e-16
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.1 == Approx( 0.1 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.2
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.2 == Approx( 0.2 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.2
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.3
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.3 == Approx( 0.3 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.3
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.4
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.4 == Approx( 0.4 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.4
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.5
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.5 == Approx( 0.5 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.5
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.6
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.6 == Approx( 0.6 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.6
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.7
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.7 == Approx( 0.7 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.7
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.8
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.8 == Approx( 0.8 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.8
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.9
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.9 == Approx( 0.9 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.9
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx( rangeEnd )
-                </Original>
-                <Expanded>
-                  1.0 == Approx( 1.0 )
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  !(gen.next())
-                </Original>
-                <Expanded>
-                  !false
-                </Expanded>
-              </Expression>
-              <OverallResults successes="42" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="42" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="42" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="42" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Floating Point" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Section name="Slightly over end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Info>
-                Current expected value is -1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -1.0 == Approx( -1.0 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.7
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.7 == Approx( -0.7 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.7
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.4
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.4 == Approx( -0.4 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.4
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.1 == Approx( -0.1 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.2
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.2 == Approx( 0.2 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.2
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.5
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.5 == Approx( 0.5 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.5
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  !(gen.next())
-                </Original>
-                <Expanded>
-                  !false
-                </Expanded>
-              </Expression>
-              <OverallResults successes="13" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="13" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="13" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="13" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Floating Point" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Section name="Slightly under end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Info>
-                Current expected value is -1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -1.0 == Approx( -1.0 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.7
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.7 == Approx( -0.7 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.7
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.4
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.4 == Approx( -0.4 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.4
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  -0.1 == Approx( -0.1 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is -0.1
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.2
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.2 == Approx( 0.2 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.2
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.5
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == Approx(expected)
-                </Original>
-                <Expanded>
-                  0.5 == Approx( 0.5 )
-                </Expanded>
-              </Expression>
-              <Info>
-                Current expected value is 0.5
-              </Info>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  !(gen.next())
-                </Original>
-                <Expanded>
-                  !false
-                </Expanded>
-              </Expression>
-              <OverallResults successes="13" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="13" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="13" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="13" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Negative manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Section name="Exact" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 5
-                </Original>
-                <Expanded>
-                  5 == 5
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 2
-                </Original>
-                <Expanded>
-                  2 == 2
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -1
-                </Original>
-                <Expanded>
-                  -1 == -1
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -4
-                </Original>
-                <Expanded>
-                  -4 == -4
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  !(gen.next())
-                </Original>
-                <Expanded>
-                  !false
-                </Expanded>
-              </Expression>
-              <OverallResults successes="8" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="8" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="8" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="8" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Negative manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Section name="Slightly over end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 5
-                </Original>
-                <Expanded>
-                  5 == 5
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 2
-                </Original>
-                <Expanded>
-                  2 == 2
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -1
-                </Original>
-                <Expanded>
-                  -1 == -1
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -4
-                </Original>
-                <Expanded>
-                  -4 == -4
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  !(gen.next())
-                </Original>
-                <Expanded>
-                  !false
-                </Expanded>
-              </Expression>
-              <OverallResults successes="8" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="8" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="8" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="8" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-        <Section name="Negative manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-          <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-            <Section name="Slightly under end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 5
-                </Original>
-                <Expanded>
-                  5 == 5
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == 2
-                </Original>
-                <Expanded>
-                  2 == 2
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -1
-                </Original>
-                <Expanded>
-                  -1 == -1
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -4
-                </Original>
-                <Expanded>
-                  -4 == -4
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.next()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  gen.get() == -7
-                </Original>
-                <Expanded>
-                  -7 == -7
-                </Expanded>
-              </Expression>
-              <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
-                <Original>
-                  !(gen.next())
-                </Original>
-                <Expanded>
-                  !false
-                </Expanded>
-              </Expression>
-              <OverallResults successes="10" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="10" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="10" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="10" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Greater-than inequalities with different epsilons" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d >= Approx( 1.22 )
-        </Original>
-        <Expanded>
-          1.23 >= Approx( 1.22 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d >= Approx( 1.23 )
-        </Original>
-        <Expanded>
-          1.23 >= Approx( 1.23 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          !(d >= Approx( 1.24 ))
-        </Original>
-        <Expanded>
-          !(1.23 >= Approx( 1.24 ))
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d >= Approx( 1.24 ).epsilon(0.1)
-        </Original>
-        <Expanded>
-          1.23 >= Approx( 1.24 )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="INFO and WARN do not abort tests" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        this is a message
-      </Info>
-      <Warning>
-        this is a warning
-      </Warning>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="INFO gets logged on failure" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        this message should be logged
-      </Info>
-      <Info>
-        so should this
-      </Info>
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          a == 1
-        </Original>
-        <Expanded>
-          2 == 1
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="INFO gets logged on failure, even if captured before successful assertions" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        this message may be logged later
-      </Info>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          a == 2
-        </Original>
-        <Expanded>
-          2 == 2
-        </Expanded>
-      </Expression>
-      <Info>
-        this message may be logged later
-      </Info>
-      <Info>
-        this message should be logged
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          a == 1
-        </Original>
-        <Expanded>
-          2 == 1
-        </Expanded>
-      </Expression>
-      <Info>
-        this message may be logged later
-      </Info>
-      <Info>
-        this message should be logged
-      </Info>
-      <Info>
-        and this, but later
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          a == 0
-        </Original>
-        <Expanded>
-          2 == 0
-        </Expanded>
-      </Expression>
-      <Info>
-        this message may be logged later
-      </Info>
-      <Info>
-        this message should be logged
-      </Info>
-      <Info>
-        and this, but later
-      </Info>
-      <Info>
-        but not this
-      </Info>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          a == 2
-        </Original>
-        <Expanded>
-          2 == 2
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="INFO is reset for each loop" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        current counter 0
-      </Info>
-      <Info>
-        i := 0
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          0 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 1
-      </Info>
-      <Info>
-        i := 1
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          1 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 2
-      </Info>
-      <Info>
-        i := 2
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          2 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 3
-      </Info>
-      <Info>
-        i := 3
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          3 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 4
-      </Info>
-      <Info>
-        i := 4
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          4 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 5
-      </Info>
-      <Info>
-        i := 5
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          5 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 6
-      </Info>
-      <Info>
-        i := 6
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          6 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 7
-      </Info>
-      <Info>
-        i := 7
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          7 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 8
-      </Info>
-      <Info>
-        i := 8
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          8 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 9
-      </Info>
-      <Info>
-        i := 9
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          9 &lt; 10
-        </Expanded>
-      </Expression>
-      <Info>
-        current counter 10
-      </Info>
-      <Info>
-        i := 10
-      </Info>
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          i &lt; 10
-        </Original>
-        <Expanded>
-          10 &lt; 10
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Inequality checks that should fail" tags="[!shouldfail][.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven != 7
-        </Original>
-        <Expanded>
-          7 != 7
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one != Approx( 9.1f )
-        </Original>
-        <Expanded>
-          9.1f != Approx( 9.1000003815 )
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.double_pi != Approx( 3.1415926535 )
-        </Original>
-        <Expanded>
-          3.1415926535 != Approx( 3.1415926535 )
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello != "hello"
-        </Original>
-        <Expanded>
-          "hello" != "hello"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello.size() != 5
-        </Original>
-        <Expanded>
-          5 != 5
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Inequality checks that should succeed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven != 6
-        </Original>
-        <Expanded>
-          7 != 6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven != 8
-        </Original>
-        <Expanded>
-          7 != 8
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one != Approx( 9.11f )
-        </Original>
-        <Expanded>
-          9.1f != Approx( 9.1099996567 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one != Approx( 9.0f )
-        </Original>
-        <Expanded>
-          9.1f != Approx( 9.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one != Approx( 1 )
-        </Original>
-        <Expanded>
-          9.1f != Approx( 1.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one != Approx( 0 )
-        </Original>
-        <Expanded>
-          9.1f != Approx( 0.0 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.double_pi != Approx( 3.1415 )
-        </Original>
-        <Expanded>
-          3.1415926535 != Approx( 3.1415 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello != "goodbye"
-        </Original>
-        <Expanded>
-          "hello" != "goodbye"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello != "hell"
-        </Original>
-        <Expanded>
-          "hello" != "hell"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello != "hello1"
-        </Original>
-        <Expanded>
-          "hello" != "hello1"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello.size() != 6
-        </Original>
-        <Expanded>
-          5 != 6
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Lambdas in assertions" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-        <Original>
-          []() { return true; }()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Less-than inequalities with different epsilons" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d &lt;= Approx( 1.24 )
-        </Original>
-        <Expanded>
-          1.23 &lt;= Approx( 1.24 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d &lt;= Approx( 1.23 )
-        </Original>
-        <Expanded>
-          1.23 &lt;= Approx( 1.23 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          !(d &lt;= Approx( 1.22 ))
-        </Original>
-        <Expanded>
-          !(1.23 &lt;= Approx( 1.22 ))
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d &lt;= Approx( 1.22 ).epsilon(0.1)
-        </Original>
-        <Expanded>
-          1.23 &lt;= Approx( 1.22 )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="ManuallyRegistered" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Matchers can be (AllOf) composed with the &amp;&amp; operator" tags="[matchers][operator&amp;&amp;][operators]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Contains("string") &amp;&amp; Contains("abc") &amp;&amp; Contains("substring") &amp;&amp; Contains("contains")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Matchers can be (AnyOf) composed with the || operator" tags="[matchers][operators][operator||]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Contains("string") || Contains("different") || Contains("random")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching2(), Contains("string") || Contains("different") || Contains("random")
-        </Original>
-        <Expanded>
-          "some completely different text that contains one common word" ( contains: "string" or contains: "different" or contains: "random" )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Matchers can be composed with both &amp;&amp; and ||" tags="[matchers][operator&amp;&amp;][operators][operator||]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), (Contains("string") || Contains("different")) &amp;&amp; Contains("substring")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "substring" )
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Matchers can be composed with both &amp;&amp; and || - failing" tags="[.][failing][matchers][operator&amp;&amp;][operators][operator||]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), (Contains("string") || Contains("different")) &amp;&amp; Contains("random")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Matchers can be negated (Not) with the ! operator" tags="[matchers][not][operators]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), !Contains("different")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" not contains: "different"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Matchers can be negated (Not) with the ! operator - failing" tags="[.][failing][matchers][not][operators]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), !Contains("substring")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" not contains: "substring"
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Mismatching exception messages failing the test" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Original>
-          thisThrows(), "expected exception"
-        </Original>
-        <Expanded>
-          "expected exception" equals: "expected exception"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Original>
-          thisThrows(), "should fail"
-        </Original>
-        <Expanded>
-          "expected exception" equals: "should fail"
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Nested generators and captured variables" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          3 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          4 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          5 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          6 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          -5 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          -4 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          90 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          91 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          92 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          93 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          94 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          95 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          96 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          97 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          98 > -6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-        <Original>
-          values > -6
-        </Original>
-        <Expanded>
-          99 > -6
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Nice descriptive name" tags="[.][tag1][tag2][tag3]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Warning>
-        This one ran
-      </Warning>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Non-std exceptions can be translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        custom exception
-      </Exception>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Objects that evaluated in boolean contexts can be checked" tags="[SafeBool][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          True
-        </Original>
-        <Expanded>
-          {?}
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          !False
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          !(False)
-        </Original>
-        <Expanded>
-          !{?}
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Optionally static assertions" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Ordering comparison checks that should fail" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven > 7
-        </Original>
-        <Expanded>
-          7 > 7
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven &lt; 7
-        </Original>
-        <Expanded>
-          7 &lt; 7
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven > 8
-        </Original>
-        <Expanded>
-          7 > 8
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven &lt; 6
-        </Original>
-        <Expanded>
-          7 &lt; 6
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven &lt; 0
-        </Original>
-        <Expanded>
-          7 &lt; 0
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven &lt; -1
-        </Original>
-        <Expanded>
-          7 &lt; -1
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven >= 8
-        </Original>
-        <Expanded>
-          7 >= 8
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven &lt;= 6
-        </Original>
-        <Expanded>
-          7 &lt;= 6
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one &lt; 9
-        </Original>
-        <Expanded>
-          9.1f &lt; 9
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one > 10
-        </Original>
-        <Expanded>
-          9.1f > 10
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one > 9.2
-        </Original>
-        <Expanded>
-          9.1f > 9.2
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello > "hello"
-        </Original>
-        <Expanded>
-          "hello" > "hello"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello &lt; "hello"
-        </Original>
-        <Expanded>
-          "hello" &lt; "hello"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello > "hellp"
-        </Original>
-        <Expanded>
-          "hello" > "hellp"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello > "z"
-        </Original>
-        <Expanded>
-          "hello" > "z"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello &lt; "hellm"
-        </Original>
-        <Expanded>
-          "hello" &lt; "hellm"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello &lt; "a"
-        </Original>
-        <Expanded>
-          "hello" &lt; "a"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello >= "z"
-        </Original>
-        <Expanded>
-          "hello" >= "z"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello &lt;= "a"
-        </Original>
-        <Expanded>
-          "hello" &lt;= "a"
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Ordering comparison checks that should succeed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven &lt; 8
-        </Original>
-        <Expanded>
-          7 &lt; 8
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven > 6
-        </Original>
-        <Expanded>
-          7 > 6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven > 0
-        </Original>
-        <Expanded>
-          7 > 0
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven > -1
-        </Original>
-        <Expanded>
-          7 > -1
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven >= 7
-        </Original>
-        <Expanded>
-          7 >= 7
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven >= 6
-        </Original>
-        <Expanded>
-          7 >= 6
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven &lt;= 7
-        </Original>
-        <Expanded>
-          7 &lt;= 7
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.int_seven &lt;= 8
-        </Original>
-        <Expanded>
-          7 &lt;= 8
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one > 9
-        </Original>
-        <Expanded>
-          9.1f > 9
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one &lt; 10
-        </Original>
-        <Expanded>
-          9.1f &lt; 10
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.float_nine_point_one &lt; 9.2
-        </Original>
-        <Expanded>
-          9.1f &lt; 9.2
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello &lt;= "hello"
-        </Original>
-        <Expanded>
-          "hello" &lt;= "hello"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello >= "hello"
-        </Original>
-        <Expanded>
-          "hello" >= "hello"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello &lt; "hellp"
-        </Original>
-        <Expanded>
-          "hello" &lt; "hellp"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello &lt; "zebra"
-        </Original>
-        <Expanded>
-          "hello" &lt; "zebra"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello > "hellm"
-        </Original>
-        <Expanded>
-          "hello" > "hellm"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          data.str_hello > "a"
-        </Original>
-        <Expanded>
-          "hello" > "a"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Our PCG implementation provides expected results for known seeds" tags="[rng]" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-      <Section name="Default seeded" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            4242248763 (0x<hex digits>)
-==
-4242248763 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            1867888929 (0x<hex digits>)
-==
-1867888929 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            1276619030 (0x<hex digits>)
-==
-1276619030 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            1911218783 (0x<hex digits>)
-==
-1911218783 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            1827115164 (0x<hex digits>)
-==
-1827115164 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Specific seed" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            1472234645 (0x<hex digits>)
-==
-1472234645 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            868832940 (0x<hex digits>)
-==
-868832940 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            570883446 (0x<hex digits>)
-==
-570883446 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            889299803 (0x<hex digits>)
-==
-889299803 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            4261393167 (0x<hex digits>)
-==
-4261393167 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            1472234645 (0x<hex digits>)
-==
-1472234645 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            868832940 (0x<hex digits>)
-==
-868832940 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            570883446 (0x<hex digits>)
-==
-570883446 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            889299803 (0x<hex digits>)
-==
-889299803 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
-          <Original>
-            rng() == 0x<hex digits>
-          </Original>
-          <Expanded>
-            4261393167 (0x<hex digits>)
-==
-4261393167 (0x<hex digits>)
-          </Expanded>
-        </Expression>
-        <OverallResults successes="10" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Output from all sections is reported" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-          Message from section one
-        </Failure>
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-          Message from section two
-        </Failure>
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Overloaded comma or address-of operators are not used" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed
-        </Original>
-        <Expanded>
-          (EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          &amp;EvilMatcher(), EvilAddressOfOperatorUsed
-        </Original>
-        <Expanded>
-          &amp;EvilMatcher(), EvilAddressOfOperatorUsed
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          EvilMatcher() || (EvilMatcher() &amp;&amp; !EvilMatcher())
-        </Original>
-        <Expanded>
-          EvilMatcher() || (EvilMatcher() &amp;&amp; !EvilMatcher())
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          (EvilMatcher() &amp;&amp; EvilMatcher()) || !EvilMatcher()
-        </Original>
-        <Expanded>
-          (EvilMatcher() &amp;&amp; EvilMatcher()) || !EvilMatcher()
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Parse test names and tags" tags="[command-line][test-spec]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-      <Section name="Empty test spec should have no filters" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Test spec from empty string should have no filters" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Test spec from just a comma should have no filters" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Test spec from name should have one filter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Test spec from quoted name should have one filter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Test spec from name should have one filter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Wildcard at the start" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            parseTestSpec( "*a" ).matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Wildcard at the end" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            parseTestSpec( "a*" ).matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Wildcard at both ends" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            parseTestSpec( "*a*" ).matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Redundant wildcard at the start" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Redundant wildcard at the end" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Redundant wildcard at both ends" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Wildcard at both ends, redundant at start" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Just wildcard" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Single tag" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Single tag, two matches" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Two tags" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Two tags, spare separated" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Wildcarded name and tag" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Single tag exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="One tag exclusion and one tag inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="One tag exclusion and one wldcarded name inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="One tag exclusion, using exclude:, and one wldcarded name inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="name exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="wildcarded name exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="wildcarded name exclusion with tag inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="wildcarded name exclusion, using exclude:, with tag inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="two wildcarded names" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="empty tag" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="empty quoted name" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="quoted string followed by tag exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.hasFilters() == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcA ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcB ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcC ) == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *tcD ) == true
-          </Original>
-          <Expanded>
-            true == true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Leading and trailing spaces in test spec" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( "  aardvark " ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( "  aardvark" ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( " aardvark " ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( "aardvark " ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( "aardvark" ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Leading and trailing spaces in test name" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( "  aardvark " ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( "  aardvark" ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( " aardvark " ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( "aardvark " ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches( *fakeTestCase( "aardvark" ) )
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Shortened hide tags are split apart when parsing" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches(*fakeTestCase("hidden and foo", "[.][foo]"))
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            !(spec.matches(*fakeTestCase("only foo", "[foo]")))
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Shortened hide tags also properly handle exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            !(spec.matches(*fakeTestCase("hidden and foo", "[.][foo]")))
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            !(spec.matches(*fakeTestCase("only foo", "[foo]")))
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            !(spec.matches(*fakeTestCase("only hidden", "[.]")))
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            spec.matches(*fakeTestCase("neither foo nor hidden", "[bar]"))
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Pointers can be compared to null" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          p == 0
-        </Original>
-        <Expanded>
-          0 == 0
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          p == pNULL
-        </Original>
-        <Expanded>
-          0 == 0
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          p != 0
-        </Original>
-        <Expanded>
-          0x<hex digits> != 0
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          cp != 0
-        </Original>
-        <Expanded>
-          0x<hex digits> != 0
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          cpc != 0
-        </Original>
-        <Expanded>
-          0x<hex digits> != 0
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          returnsNull() == 0
-        </Original>
-        <Expanded>
-          {null string} == 0
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          returnsConstNull() == 0
-        </Original>
-        <Expanded>
-          {null string} == 0
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          0 != p
-        </Original>
-        <Expanded>
-          0 != 0x<hex digits>
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Precision of floating point stringification can be set" tags="[floatingPoint][toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-      <Section name="Floats" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            str1.size() == 3 + 5
-          </Original>
-          <Expanded>
-            8 == 8
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            str2.size() == 3 + 10
-          </Original>
-          <Expanded>
-            13 == 13
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Double" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            str1.size() == 2 + 5
-          </Original>
-          <Expanded>
-            7 == 7
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            str2.size() == 2 + 15
-          </Original>
-          <Expanded>
-            17 == 17
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Predicate matcher can accept const char*" tags="[compilation][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          "foo", Predicate&lt;const char*>([] (const char* const&amp;) { return true; })
-        </Original>
-        <Expanded>
-          "foo" matches undescribed predicate
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Process can be configured on command line" tags="[command-line][config]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-      <Section name="empty args don't cause a crash" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            result
-          </Original>
-          <Expanded>
-            {?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            config.processName == ""
-          </Original>
-          <Expanded>
-            "" == ""
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="default - no arguments" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            result
-          </Original>
-          <Expanded>
-            {?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            config.processName == "test"
-          </Original>
-          <Expanded>
-            "test" == "test"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            config.shouldDebugBreak == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            config.abortAfter == -1
-          </Original>
-          <Expanded>
-            -1 == -1
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            config.noThrow == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            config.reporterName == "console"
-          </Original>
-          <Expanded>
-            "console" == "console"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Original>
-            !(cfg.hasTestFilters())
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <OverallResults successes="7" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="test lists" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="Specify one test case using" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              result
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cfg.hasTestFilters()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cfg.testSpec().matches(*fakeTestCase("notIncluded")) == false
-            </Original>
-            <Expanded>
-              false == false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cfg.testSpec().matches(*fakeTestCase("test1"))
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="4" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="test lists" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="Specify one test case exclusion using exclude:" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              result
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cfg.hasTestFilters()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cfg.testSpec().matches(*fakeTestCase("test1")) == false
-            </Original>
-            <Expanded>
-              false == false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cfg.testSpec().matches(*fakeTestCase("alwaysIncluded"))
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="4" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="test lists" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="Specify one test case exclusion using ~" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              result
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cfg.hasTestFilters()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cfg.testSpec().matches(*fakeTestCase("test1")) == false
-            </Original>
-            <Expanded>
-              false == false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cfg.testSpec().matches(*fakeTestCase("alwaysIncluded"))
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="4" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="-r/console" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "-r", "console"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.reporterName == "console"
-            </Original>
-            <Expanded>
-              "console" == "console"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="-r/xml" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "-r", "xml"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.reporterName == "xml"
-            </Original>
-            <Expanded>
-              "xml" == "xml"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="--reporter/junit" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "--reporter", "junit"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.reporterName == "junit"
-            </Original>
-            <Expanded>
-              "junit" == "junit"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="Only one reporter is accepted" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              !(cli.parse({ "test", "-r", "xml", "-r", "junit" }))
-            </Original>
-            <Expanded>
-              !{?}
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="must match one of the available ones" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              !result
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              result.errorMessage(), Contains("Unrecognized reporter")
-            </Original>
-            <Expanded>
-              "Unrecognized reporter, 'unsupported'. Check available with --list-reporters" contains: "Unrecognized reporter"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="debugger" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="-b" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "-b"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.shouldDebugBreak == true
-            </Original>
-            <Expanded>
-              true == true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="debugger" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="--break" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "--break"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.shouldDebugBreak
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="-a aborts after first failure" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "-a"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.abortAfter == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="-x 2 aborts after two failures" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "-x", "2"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.abortAfter == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="-x must be numeric" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              !result
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              result.errorMessage(), Contains("convert") &amp;&amp; Contains("oops")
-            </Original>
-            <Expanded>
-              "Unable to convert 'oops' to destination type" ( contains: "convert" and contains: "oops" )
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Section name="Accepted options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                cli.parse({"test", "--wait-for-keypress", std::get&lt;0>(input)})
-              </Original>
-              <Expanded>
-                {?}
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                config.waitForKeypress == std::get&lt;1>(input)
-              </Original>
-              <Expanded>
-                0 == 0
-              </Expanded>
-            </Expression>
-            <OverallResults successes="2" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Section name="Accepted options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                cli.parse({"test", "--wait-for-keypress", std::get&lt;0>(input)})
-              </Original>
-              <Expanded>
-                {?}
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                config.waitForKeypress == std::get&lt;1>(input)
-              </Original>
-              <Expanded>
-                1 == 1
-              </Expanded>
-            </Expression>
-            <OverallResults successes="2" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Section name="Accepted options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                cli.parse({"test", "--wait-for-keypress", std::get&lt;0>(input)})
-              </Original>
-              <Expanded>
-                {?}
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                config.waitForKeypress == std::get&lt;1>(input)
-              </Original>
-              <Expanded>
-                2 == 2
-              </Expanded>
-            </Expression>
-            <OverallResults successes="2" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Section name="Accepted options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                cli.parse({"test", "--wait-for-keypress", std::get&lt;0>(input)})
-              </Original>
-              <Expanded>
-                {?}
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                config.waitForKeypress == std::get&lt;1>(input)
-              </Original>
-              <Expanded>
-                3 == 3
-              </Expanded>
-            </Expression>
-            <OverallResults successes="2" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Section name="invalid options are reported" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                !result
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-              <Original>
-                result.errorMessage(), Contains("never") &amp;&amp; Contains("both")
-              </Original>
-              <Expanded>
-                "keypress argument must be one of: never, start, exit or both. 'sometimes' not recognised" ( contains: "never" and contains: "both" )
-              </Expanded>
-            </Expression>
-            <OverallResults successes="2" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="nothrow" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="-e" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "-e"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.noThrow
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="nothrow" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="--nothrow" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "--nothrow"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.noThrow
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="output filename" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="-o filename" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "-o", "filename.ext"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.outputFilename == "filename.ext"
-            </Original>
-            <Expanded>
-              "filename.ext" == "filename.ext"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="output filename" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="--out" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "--out", "filename.ext"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.outputFilename == "filename.ext"
-            </Original>
-            <Expanded>
-              "filename.ext" == "filename.ext"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="combinations" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="Single character flags can be combined" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "-abe"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.abortAfter == 1
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.shouldDebugBreak
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.noThrow == true
-            </Original>
-            <Expanded>
-              true == true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="4" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="without option" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.useColour == UseColour::Auto
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="auto" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "--use-colour", "auto"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.useColour == UseColour::Auto
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="yes" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "--use-colour", "yes"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.useColour == UseColour::Yes
-            </Original>
-            <Expanded>
-              1 == 1
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="no" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({"test", "--use-colour", "no"})
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.useColour == UseColour::No
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="error" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              !result
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              result.errorMessage(), Contains( "colour mode must be one of" )
-            </Original>
-            <Expanded>
-              "colour mode must be one of: auto, yes or no. 'wrong' not recognised" contains: "colour mode must be one of"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="samples" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({ "test", "--benchmark-samples=200" })
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.benchmarkSamples == 200
-            </Original>
-            <Expanded>
-              200 == 200
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="resamples" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({ "test", "--benchmark-resamples=20000" })
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.benchmarkResamples == 20000
-            </Original>
-            <Expanded>
-              20000 (0x<hex digits>) == 20000 (0x<hex digits>)
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="confidence-interval" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({ "test", "--benchmark-confidence-interval=0.99" })
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.benchmarkConfidenceInterval == Catch::Approx(0.99)
-            </Original>
-            <Expanded>
-              0.99 == Approx( 0.99 )
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="no-analysis" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({ "test", "--benchmark-no-analysis" })
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.benchmarkNoAnalysis
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-        <Section name="warmup-time" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              cli.parse({ "test", "--benchmark-warmup-time=10" })
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-            <Original>
-              config.benchmarkWarmupTime == 10
-            </Original>
-            <Expanded>
-              10 == 10
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Product with differing arities - std::tuple&lt;int, double, float>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          std::tuple_size&lt;TestType>::value >= 1
-        </Original>
-        <Expanded>
-          3 >= 1
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Product with differing arities - std::tuple&lt;int, double>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          std::tuple_size&lt;TestType>::value >= 1
-        </Original>
-        <Expanded>
-          2 >= 1
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Product with differing arities - std::tuple&lt;int>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          std::tuple_size&lt;TestType>::value >= 1
-        </Original>
-        <Expanded>
-          1 >= 1
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Range type with sentinel" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-        <Original>
-          Catch::Detail::stringify(UsesSentinel{}) == "{  }"
-        </Original>
-        <Expanded>
-          "{  }" == "{  }"
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Reconstruction should be based on stringification: #914" tags="[.][Decomposition][failing]" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
-        <Original>
-          truthy(false)
-        </Original>
-        <Expanded>
-          Hey, its truthy!
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Regex string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Matches("this STRING contains 'abc' as a substring")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" matches "this STRING contains 'abc' as a substring" case sensitively
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Matches("contains 'abc' as a substring")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" matches "contains 'abc' as a substring" case sensitively
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Matches("this string contains 'abc' as a")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" matches "this string contains 'abc' as a" case sensitively
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Regression test #1" tags="[matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          actual, !UnorderedEquals(expected)
-        </Original>
-        <Expanded>
-          { 'a', 'b' } not UnorderedEquals: { 'c', 'b' }
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Reporter's write listings to provided stream" tags="[reporters]" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="automake reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: automake
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fakeTag"s)
-          </Original>
-          <Expanded>
-            "All available tags:
-   1  [fakeTag]
-1 tag
-
-" contains: "fakeTag"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="automake reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: automake
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fake reporter"s)
-          </Original>
-          <Expanded>
-            "Available reporters:
-  fake reporter:  fake description
-
-" contains: "fake reporter"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="automake reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: automake
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
-          </Original>
-          <Expanded>
-            "All available test cases:
-  fake test name
-      [fakeTestTag]
-1 test case
-
-" ( contains: "fake test name" and contains: "fakeTestTag" )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="compact reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: compact
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fakeTag"s)
-          </Original>
-          <Expanded>
-            "All available tags:
-   1  [fakeTag]
-1 tag
-
-" contains: "fakeTag"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="compact reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: compact
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fake reporter"s)
-          </Original>
-          <Expanded>
-            "Available reporters:
-  fake reporter:  fake description
-
-" contains: "fake reporter"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="compact reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: compact
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
-          </Original>
-          <Expanded>
-            "All available test cases:
-  fake test name
-      [fakeTestTag]
-1 test case
-
-" ( contains: "fake test name" and contains: "fakeTestTag" )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="console reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: console
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fakeTag"s)
-          </Original>
-          <Expanded>
-            "All available tags:
-   1  [fakeTag]
-1 tag
-
-" contains: "fakeTag"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="console reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: console
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fake reporter"s)
-          </Original>
-          <Expanded>
-            "Available reporters:
-  fake reporter:  fake description
-
-" contains: "fake reporter"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="console reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: console
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
-          </Original>
-          <Expanded>
-            "All available test cases:
-  fake test name
-      [fakeTestTag]
-1 test case
-
-" ( contains: "fake test name" and contains: "fakeTestTag" )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="junit reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: junit
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fakeTag"s)
-          </Original>
-          <Expanded>
-            "&lt;?xml version="1.0" encoding="UTF-8"?>
-All available tags:
-   1  [fakeTag]
-1 tag
-
-" contains: "fakeTag"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="junit reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: junit
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fake reporter"s)
-          </Original>
-          <Expanded>
-            "&lt;?xml version="1.0" encoding="UTF-8"?>
-Available reporters:
-  fake reporter:  fake description
-
-" contains: "fake reporter"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="junit reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: junit
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
-          </Original>
-          <Expanded>
-            "&lt;?xml version="1.0" encoding="UTF-8"?>
-All available test cases:
-  fake test name
-      [fakeTestTag]
-1 test case
-
-" ( contains: "fake test name" and contains: "fakeTestTag" )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="sonarqube reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: sonarqube
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fakeTag"s)
-          </Original>
-          <Expanded>
-            "&lt;?xml version="1.0" encoding="UTF-8"?>
-All available tags:
-   1  [fakeTag]
-1 tag
-
-" contains: "fakeTag"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="sonarqube reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: sonarqube
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fake reporter"s)
-          </Original>
-          <Expanded>
-            "&lt;?xml version="1.0" encoding="UTF-8"?>
-Available reporters:
-  fake reporter:  fake description
-
-" contains: "fake reporter"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="sonarqube reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: sonarqube
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
-          </Original>
-          <Expanded>
-            "&lt;?xml version="1.0" encoding="UTF-8"?>
-All available test cases:
-  fake test name
-      [fakeTestTag]
-1 test case
-
-" ( contains: "fake test name" and contains: "fakeTestTag" )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="tap reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: tap
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fakeTag"s)
-          </Original>
-          <Expanded>
-            "All available tags:
-   1  [fakeTag]
-1 tag
-
-" contains: "fakeTag"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="tap reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: tap
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fake reporter"s)
-          </Original>
-          <Expanded>
-            "Available reporters:
-  fake reporter:  fake description
-
-" contains: "fake reporter"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="tap reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: tap
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
-          </Original>
-          <Expanded>
-            "All available test cases:
-  fake test name
-      [fakeTestTag]
-1 test case
-
-" ( contains: "fake test name" and contains: "fakeTestTag" )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="teamcity reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: teamcity
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fakeTag"s)
-          </Original>
-          <Expanded>
-            "All available tags:
-   1  [fakeTag]
-1 tag
-
-" contains: "fakeTag"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="teamcity reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: teamcity
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fake reporter"s)
-          </Original>
-          <Expanded>
-            "Available reporters:
-  fake reporter:  fake description
-
-" contains: "fake reporter"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="teamcity reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: teamcity
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
-          </Original>
-          <Expanded>
-            "All available test cases:
-  fake test name
-      [fakeTestTag]
-1 test case
-
-" ( contains: "fake test name" and contains: "fakeTestTag" )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="xml reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: xml
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fakeTag"s)
-          </Original>
-          <Expanded>
-            "&lt;?xml version="1.0" encoding="UTF-8"?>
-&lt;TagsFromMatchingTests>
-  &lt;Tag>
-    &lt;Count>1&lt;/Count>
-    &lt;Aliases>
-      &lt;Alias>fakeTag&lt;/Alias>
-    &lt;/Aliases>
-  &lt;/Tag>
-&lt;/TagsFromMatchingTests>" contains: "fakeTag"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="xml reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: xml
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains("fake reporter"s)
-          </Original>
-          <Expanded>
-            "&lt;?xml version="1.0" encoding="UTF-8"?>
-&lt;AvailableReporters>
-  &lt;Reporter>
-    &lt;Name>fake reporter&lt;/Name>
-    &lt;Description>fake description&lt;/Description>
-  &lt;/Reporter>
-&lt;/AvailableReporters>" contains: "fake reporter"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Original>
-          !(factories.empty())
-        </Original>
-        <Expanded>
-          !false
-        </Expanded>
-      </Expression>
-      <Section name="xml reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Info>
-          Tested reporter: xml
-        </Info>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-          <Original>
-            listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
-          </Original>
-          <Expanded>
-            "&lt;?xml version="1.0" encoding="UTF-8"?>
-&lt;MatchingTests>
-  &lt;TestCase>
-    &lt;Name>fake test name&lt;/Name>
-    &lt;ClassName/>
-    &lt;Tags>[fakeTestTag]&lt;/Tags>
-    &lt;SourceInfo>
-      &lt;File>fake-file.cpp&lt;/File>
-      &lt;Line>123456789&lt;/Line>
-    &lt;/SourceInfo>
-  &lt;/TestCase>
-&lt;/MatchingTests>" ( contains: "fake test name" and contains: "fakeTestTag" )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="SUCCEED counts as a test pass" tags="[messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="SUCCEED does not require an argument" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Scenario: BDD tests requiring Fixtures to provide commonly-accessed data or methods" tags="[bdd][fixtures]" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-      <Section name="Given: No operations precede me" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-          <Original>
-            before == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Section name="When: We get the count" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-          <Section name="Then: Subsequently values are higher" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-              <Original>
-                after > before
-              </Original>
-              <Expanded>
-                1 > 0
-              </Expanded>
-            </Expression>
-            <OverallResults successes="1" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Scenario: Do that thing with the thing" tags="[Tags]" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-      <Section name="Given: This stuff exists" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-        <Section name="And given: And some assumption" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-          <Section name="When: I do this" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-            <Section name="Then: it should do this" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-                <Original>
-                  itDoesThis()
-                </Original>
-                <Expanded>
-                  true
-                </Expanded>
-              </Expression>
-              <Section name="And: do that" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-                <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-                  <Original>
-                    itDoesThat()
-                  </Original>
-                  <Expanded>
-                    true
-                  </Expanded>
-                </Expression>
-                <OverallResults successes="1" failures="0" expectedFailures="0"/>
-              </Section>
-              <OverallResults successes="2" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="2" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Scenario: This is a really long scenario name to see how the list command deals with wrapping" tags="[anotherReallyLongTagNameButThisOneHasNoObviousWrapPointsSoShouldSplitWithinAWordUsingADashCharacter][long][lots][one very long tag name that should cause line wrapping writing out using the list command][tags][verbose][very long tags]" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-      <Section name="Given: A section name that is so long that it cannot fit in a single console width" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-        <Section name="When: The test headers are printed as part of the normal running of the scenario" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-          <Section name="Then: The, deliberately very long and overly verbose (you see what I did there?) section names must wrap, along with an indent" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-            <OverallResults successes="1" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Scenario: Vector resizing affects size and capacity" tags="[bdd][capacity][size][vector]" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-      <Section name="Given: an empty vector" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-          <Original>
-            v.size() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Section name="When: it is made larger" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-          <Section name="Then: the size and capacity go up" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-              <Original>
-                v.size() == 10
-              </Original>
-              <Expanded>
-                10 == 10
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-              <Original>
-                v.capacity() >= 10
-              </Original>
-              <Expanded>
-                10 >= 10
-              </Expanded>
-            </Expression>
-            <Section name="And when: it is made smaller again" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-              <Section name="Then: the size goes down but the capacity stays the same" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-                <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-                  <Original>
-                    v.size() == 5
-                  </Original>
-                  <Expanded>
-                    5 == 5
-                  </Expanded>
-                </Expression>
-                <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-                  <Original>
-                    v.capacity() >= 10
-                  </Original>
-                  <Expanded>
-                    10 >= 10
-                  </Expanded>
-                </Expression>
-                <OverallResults successes="2" failures="0" expectedFailures="0"/>
-              </Section>
-              <OverallResults successes="2" failures="0" expectedFailures="0"/>
-            </Section>
-            <OverallResults successes="4" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="4" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Given: an empty vector" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-          <Original>
-            v.size() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Section name="When: we reserve more space" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-          <Section name="Then: The capacity is increased but the size remains the same" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-              <Original>
-                v.capacity() >= 10
-              </Original>
-              <Expanded>
-                10 >= 10
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
-              <Original>
-                v.size() == 0
-              </Original>
-              <Expanded>
-                0 == 0
-              </Expanded>
-            </Expression>
-            <OverallResults successes="2" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Sends stuff to stdout and stderr" tags="[.]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="false">
-        <StdOut>
-A string sent directly to stdout
-        </StdOut>
-        <StdErr>
-A string sent directly to stderr
-A string sent to stderr via clog
-        </StdErr>
-      </OverallResult>
-    </TestCase>
-    <TestCase name="Some simple comparisons between doubles" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d == Approx( 1.23 )
-        </Original>
-        <Expanded>
-          1.23 == Approx( 1.23 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d != Approx( 1.22 )
-        </Original>
-        <Expanded>
-          1.23 != Approx( 1.22 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d != Approx( 1.24 )
-        </Original>
-        <Expanded>
-          1.23 != Approx( 1.24 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d == 1.23_a
-        </Original>
-        <Expanded>
-          1.23 == Approx( 1.23 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          d != 1.22_a
-        </Original>
-        <Expanded>
-          1.23 != Approx( 1.22 )
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx( d ) == 1.23
-        </Original>
-        <Expanded>
-          Approx( 1.23 ) == 1.23
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx( d ) != 1.22
-        </Original>
-        <Expanded>
-          Approx( 1.23 ) != 1.22
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-        <Original>
-          Approx( d ) != 1.24
-        </Original>
-        <Expanded>
-          Approx( 1.23 ) != 1.24
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Standard output from all sections is reported" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Section name="one" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <Section name="two" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="false">
-        <StdOut>
-Message from section one
-Message from section two
-        </StdOut>
-      </OverallResult>
-    </TestCase>
-    <TestCase name="StartsWith string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), StartsWith("This String")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" starts with: "This String"
-        </Expanded>
-      </Expression>
-      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No)
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" starts with: "string" (case insensitive)
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Static arrays are convertible to string" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-      <Section name="Single item" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            Catch::Detail::stringify(singular) == "{ 1 }"
-          </Original>
-          <Expanded>
-            "{ 1 }" == "{ 1 }"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Multiple" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            Catch::Detail::stringify(arr) == "{ 3, 2, 1 }"
-          </Original>
-          <Expanded>
-            "{ 3, 2, 1 }" == "{ 3, 2, 1 }"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Non-trivial inner items" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            Catch::Detail::stringify(arr) == R"({ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } })"
-          </Original>
-          <Expanded>
-            "{ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } }"
-==
-"{ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } }"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="String matchers" tags="[matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Contains("string")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" contains: "string"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Contains("string", Catch::CaseSensitive::No)
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" contains: "string" (case insensitive)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Contains("abc")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" contains: "abc"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), Contains("aBC", Catch::CaseSensitive::No)
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), StartsWith("this")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" starts with: "this"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), StartsWith("THIS", Catch::CaseSensitive::No)
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), EndsWith("substring")
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" ends with: "substring"
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Original>
-          testStringForMatching(), EndsWith(" SuBsTrInG", Catch::CaseSensitive::No)
-        </Original>
-        <Expanded>
-          "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="StringRef" tags="[StringRef][Strings]" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-      <Section name="Empty string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            empty.empty()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            empty.size() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            empty.isNullTerminated()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            std::strcmp( empty.c_str(), "" ) == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="From string literal" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            s.empty() == false
-          </Original>
-          <Expanded>
-            false == false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            s.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            s.isNullTerminated()
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            std::strcmp( rawChars, "hello" ) == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            s.c_str()
-          </Original>
-          <Expanded>
-            s.c_str()
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            s.c_str() == rawChars
-          </Original>
-          <Expanded>
-            "hello" == "hello"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            s.data() == rawChars
-          </Original>
-          <Expanded>
-            "hello" == "hello"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="7" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="From sub-string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            original == "original"
-          </Original>
-          <Expanded>
-            original == "original"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            !(original.isNullTerminated())
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            original.c_str()
-          </Original>
-          <Expanded>
-            original.c_str()
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            original.data()
-          </Original>
-          <Expanded>
-            original.data()
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="zero-based substring" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              ss.empty() == false
-            </Original>
-            <Expanded>
-              false == false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              ss.size() == 5
-            </Original>
-            <Expanded>
-              5 == 5
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              std::strncmp( ss.data(), "hello", 5 ) == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              ss == "hello"
-            </Original>
-            <Expanded>
-              hello == "hello"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="4" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="non-zero-based substring" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              ss.size() == 6
-            </Original>
-            <Expanded>
-              6 == 6
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              std::strcmp( ss.c_str(), "world!" ) == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="Pointer values of full refs should match" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              s.data() == s2.data()
-            </Original>
-            <Expanded>
-              "hello world!" == "hello world!"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="Pointer values of substring refs should also match" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              s.data() == ss.data()
-            </Original>
-            <Expanded>
-              "hello world!" == "hello world!"
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="Past the end substring" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              s.substr(s.size() + 1, 123).empty()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="Substring off the end are trimmed" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              std::strcmp(ss.c_str(), "world!") == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="substring start after the end is empty" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              s.substr(1'000'000, 1).empty()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      <OverallResults successes="10" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Floating Point" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Section name="Exact" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Info>
+              Current expected value is -1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -1.0 == Approx( -1.0 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.9
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.9 == Approx( -0.9 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.9
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.8
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.8 == Approx( -0.8 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.8
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.7
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.7 == Approx( -0.7 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.7
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.6
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.6 == Approx( -0.6 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.6
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.5
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.5 == Approx( -0.5 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.5
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.4
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.4 == Approx( -0.4 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.4
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.3
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.3 == Approx( -0.3 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.3
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.2
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.2 == Approx( -0.2 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.2
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.1 == Approx( -0.1 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -1.38778e-16
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.0 == Approx( -0.0 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -1.38778e-16
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.1 == Approx( 0.1 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.2
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.2 == Approx( 0.2 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.2
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.3
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.3 == Approx( 0.3 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.3
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.4
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.4 == Approx( 0.4 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.4
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.5
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.5 == Approx( 0.5 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.5
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.6
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.6 == Approx( 0.6 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.6
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.7
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.7 == Approx( 0.7 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.7
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.8
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.8 == Approx( 0.8 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.8
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.9
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.9 == Approx( 0.9 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.9
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx( rangeEnd )
+              </Original>
+              <Expanded>
+                1.0 == Approx( 1.0 )
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                !(gen.next())
+              </Original>
+              <Expanded>
+                !false
+              </Expanded>
+            </Expression>
+            <OverallResults successes="42" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="42" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Comparisons are deep" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            reinterpret_cast&lt;char*>(buffer1) != reinterpret_cast&lt;char*>(buffer2)
-          </Original>
-          <Expanded>
-            "Hello" != "Hello"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            left == right
-          </Original>
-          <Expanded>
-            Hello == Hello
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            left != left.substr(0, 3)
-          </Original>
-          <Expanded>
-            Hello != Hel
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
+        <OverallResults successes="42" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="from std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="implicitly constructed" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              sr == "a standard string"
-            </Original>
-            <Expanded>
-              a standard string == "a standard string"
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              sr.size() == stdStr.size()
-            </Original>
-            <Expanded>
-              17 == 17
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      <OverallResults successes="42" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Floating Point" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Section name="Slightly over end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Info>
+              Current expected value is -1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -1.0 == Approx( -1.0 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.7
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.7 == Approx( -0.7 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.7
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.4
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.4 == Approx( -0.4 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.4
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.1 == Approx( -0.1 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.2
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.2 == Approx( 0.2 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.2
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.5
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.5 == Approx( 0.5 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.5
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                !(gen.next())
+              </Original>
+              <Expanded>
+                !false
+              </Expanded>
+            </Expression>
+            <OverallResults successes="13" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="13" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        <OverallResults successes="13" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="from std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="explicitly constructed" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              sr == "a standard string"
-            </Original>
-            <Expanded>
-              a standard string == "a standard string"
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              sr.size() == stdStr.size()
-            </Original>
-            <Expanded>
-              17 == 17
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      <OverallResults successes="13" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Positive manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Floating Point" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Section name="Slightly under end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Info>
+              Current expected value is -1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -1.0 == Approx( -1.0 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.7
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.7 == Approx( -0.7 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.7
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.4
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.4 == Approx( -0.4 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.4
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                -0.1 == Approx( -0.1 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is -0.1
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.2
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.2 == Approx( 0.2 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.2
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.5
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == Approx(expected)
+              </Original>
+              <Expanded>
+                0.5 == Approx( 0.5 )
+              </Expanded>
+            </Expression>
+            <Info>
+              Current expected value is 0.5
+            </Info>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                !(gen.next())
+              </Original>
+              <Expanded>
+                !false
+              </Expanded>
+            </Expression>
+            <OverallResults successes="13" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="13" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        <OverallResults successes="13" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="from std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="assigned" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              sr == "a standard string"
-            </Original>
-            <Expanded>
-              a standard string == "a standard string"
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              sr.size() == stdStr.size()
-            </Original>
-            <Expanded>
-              17 == 17
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      <OverallResults successes="13" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Negative manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Section name="Exact" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 5
+              </Original>
+              <Expanded>
+                5 == 5
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 2
+              </Original>
+              <Expanded>
+                2 == 2
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -1
+              </Original>
+              <Expanded>
+                -1 == -1
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -4
+              </Original>
+              <Expanded>
+                -4 == -4
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                !(gen.next())
+              </Original>
+              <Expanded>
+                !false
+              </Expanded>
+            </Expression>
+            <OverallResults successes="8" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="8" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        <OverallResults successes="8" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="to std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="explicitly constructed" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              stdStr == "a stringref"
-            </Original>
-            <Expanded>
-              "a stringref" == "a stringref"
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              stdStr.size() == sr.size()
-            </Original>
-            <Expanded>
-              11 == 11
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      <OverallResults successes="8" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Negative manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Section name="Slightly over end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 5
+              </Original>
+              <Expanded>
+                5 == 5
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 2
+              </Original>
+              <Expanded>
+                2 == 2
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -1
+              </Original>
+              <Expanded>
+                -1 == -1
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -4
+              </Original>
+              <Expanded>
+                -4 == -4
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                !(gen.next())
+              </Original>
+              <Expanded>
+                !false
+              </Expanded>
+            </Expression>
+            <OverallResults successes="8" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="8" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        <OverallResults successes="8" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="to std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Section name="assigned" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              stdStr == "a stringref"
-            </Original>
-            <Expanded>
-              "a stringref" == "a stringref"
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-            <Original>
-              stdStr.size() == sr.size()
-            </Original>
-            <Expanded>
-              11 == 11
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      <OverallResults successes="8" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Range" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+      <Section name="Negative manual step" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+        <Section name="Integer" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+          <Section name="Slightly under end" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 5
+              </Original>
+              <Expanded>
+                5 == 5
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == 2
+              </Original>
+              <Expanded>
+                2 == 2
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -1
+              </Original>
+              <Expanded>
+                -1 == -1
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -4
+              </Original>
+              <Expanded>
+                -4 == -4
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.next()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                gen.get() == -7
+              </Original>
+              <Expanded>
+                -7 == -7
+              </Expanded>
+            </Expression>
+            <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/GeneratorsImpl.tests.cpp" >
+              <Original>
+                !(gen.next())
+              </Original>
+              <Expanded>
+                !false
+              </Expanded>
+            </Expression>
+            <OverallResults successes="10" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="10" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        <OverallResults successes="10" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="std::string += StringRef" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            lhs == "some string += the stringref contents"
-          </Original>
-          <Expanded>
-            "some string += the stringref contents"
+      <OverallResults successes="10" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Greater-than inequalities with different epsilons" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d >= Approx( 1.22 )
+      </Original>
+      <Expanded>
+        1.23 >= Approx( 1.22 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d >= Approx( 1.23 )
+      </Original>
+      <Expanded>
+        1.23 >= Approx( 1.23 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        !(d >= Approx( 1.24 ))
+      </Original>
+      <Expanded>
+        !(1.23 >= Approx( 1.24 ))
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d >= Approx( 1.24 ).epsilon(0.1)
+      </Original>
+      <Expanded>
+        1.23 >= Approx( 1.24 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="INFO and WARN do not abort tests" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      this is a message
+    </Info>
+    <Warning>
+      this is a warning
+    </Warning>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="INFO gets logged on failure" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      this message should be logged
+    </Info>
+    <Info>
+      so should this
+    </Info>
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        a == 1
+      </Original>
+      <Expanded>
+        2 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="INFO gets logged on failure, even if captured before successful assertions" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      this message may be logged later
+    </Info>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        a == 2
+      </Original>
+      <Expanded>
+        2 == 2
+      </Expanded>
+    </Expression>
+    <Info>
+      this message may be logged later
+    </Info>
+    <Info>
+      this message should be logged
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        a == 1
+      </Original>
+      <Expanded>
+        2 == 1
+      </Expanded>
+    </Expression>
+    <Info>
+      this message may be logged later
+    </Info>
+    <Info>
+      this message should be logged
+    </Info>
+    <Info>
+      and this, but later
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        a == 0
+      </Original>
+      <Expanded>
+        2 == 0
+      </Expanded>
+    </Expression>
+    <Info>
+      this message may be logged later
+    </Info>
+    <Info>
+      this message should be logged
+    </Info>
+    <Info>
+      and this, but later
+    </Info>
+    <Info>
+      but not this
+    </Info>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        a == 2
+      </Original>
+      <Expanded>
+        2 == 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="INFO is reset for each loop" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      current counter 0
+    </Info>
+    <Info>
+      i := 0
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        0 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 1
+    </Info>
+    <Info>
+      i := 1
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        1 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 2
+    </Info>
+    <Info>
+      i := 2
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        2 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 3
+    </Info>
+    <Info>
+      i := 3
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        3 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 4
+    </Info>
+    <Info>
+      i := 4
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        4 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 5
+    </Info>
+    <Info>
+      i := 5
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        5 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 6
+    </Info>
+    <Info>
+      i := 6
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        6 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 7
+    </Info>
+    <Info>
+      i := 7
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        7 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 8
+    </Info>
+    <Info>
+      i := 8
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        8 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 9
+    </Info>
+    <Info>
+      i := 9
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        9 &lt; 10
+      </Expanded>
+    </Expression>
+    <Info>
+      current counter 10
+    </Info>
+    <Info>
+      i := 10
+    </Info>
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        i &lt; 10
+      </Original>
+      <Expanded>
+        10 &lt; 10
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Inequality checks that should fail" tags="[!shouldfail][.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven != 7
+      </Original>
+      <Expanded>
+        7 != 7
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one != Approx( 9.1f )
+      </Original>
+      <Expanded>
+        9.1f != Approx( 9.1000003815 )
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.double_pi != Approx( 3.1415926535 )
+      </Original>
+      <Expanded>
+        3.1415926535 != Approx( 3.1415926535 )
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello != "hello"
+      </Original>
+      <Expanded>
+        "hello" != "hello"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello.size() != 5
+      </Original>
+      <Expanded>
+        5 != 5
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Inequality checks that should succeed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven != 6
+      </Original>
+      <Expanded>
+        7 != 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven != 8
+      </Original>
+      <Expanded>
+        7 != 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one != Approx( 9.11f )
+      </Original>
+      <Expanded>
+        9.1f != Approx( 9.1099996567 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one != Approx( 9.0f )
+      </Original>
+      <Expanded>
+        9.1f != Approx( 9.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one != Approx( 1 )
+      </Original>
+      <Expanded>
+        9.1f != Approx( 1.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one != Approx( 0 )
+      </Original>
+      <Expanded>
+        9.1f != Approx( 0.0 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.double_pi != Approx( 3.1415 )
+      </Original>
+      <Expanded>
+        3.1415926535 != Approx( 3.1415 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello != "goodbye"
+      </Original>
+      <Expanded>
+        "hello" != "goodbye"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello != "hell"
+      </Original>
+      <Expanded>
+        "hello" != "hell"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello != "hello1"
+      </Original>
+      <Expanded>
+        "hello" != "hello1"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello.size() != 6
+      </Original>
+      <Expanded>
+        5 != 6
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Lambdas in assertions" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+      <Original>
+        []() { return true; }()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Less-than inequalities with different epsilons" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d &lt;= Approx( 1.24 )
+      </Original>
+      <Expanded>
+        1.23 &lt;= Approx( 1.24 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d &lt;= Approx( 1.23 )
+      </Original>
+      <Expanded>
+        1.23 &lt;= Approx( 1.23 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        !(d &lt;= Approx( 1.22 ))
+      </Original>
+      <Expanded>
+        !(1.23 &lt;= Approx( 1.22 ))
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d &lt;= Approx( 1.22 ).epsilon(0.1)
+      </Original>
+      <Expanded>
+        1.23 &lt;= Approx( 1.22 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="ManuallyRegistered" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Matchers can be (AllOf) composed with the &amp;&amp; operator" tags="[matchers][operator&amp;&amp;][operators]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Contains( "string" ) &amp;&amp; Contains( "abc" ) &amp;&amp; Contains( "substring" ) &amp;&amp; Contains( "contains" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" ( contains: "string" and contains: "abc" and contains: "substring" and contains: "contains" )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Matchers can be (AnyOf) composed with the || operator" tags="[matchers][operators][operator||]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Contains( "string" ) || Contains( "different" ) || Contains( "random" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" ( contains: "string" or contains: "different" or contains: "random" )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching2(), Contains( "string" ) || Contains( "different" ) || Contains( "random" )
+      </Original>
+      <Expanded>
+        "some completely different text that contains one common word" ( contains: "string" or contains: "different" or contains: "random" )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Matchers can be composed with both &amp;&amp; and ||" tags="[matchers][operator&amp;&amp;][operators][operator||]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) &amp;&amp; Contains( "substring" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "substring" )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Matchers can be composed with both &amp;&amp; and || - failing" tags="[.][failing][matchers][operator&amp;&amp;][operators][operator||]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), ( Contains( "string" ) || Contains( "different" ) ) &amp;&amp; Contains( "random" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" ( ( contains: "string" or contains: "different" ) and contains: "random" )
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Matchers can be negated (Not) with the ! operator" tags="[matchers][not][operators]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), !Contains( "different" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" not contains: "different"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Matchers can be negated (Not) with the ! operator - failing" tags="[.][failing][matchers][not][operators]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), !Contains( "substring" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" not contains: "substring"
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Mismatching exception messages failing the test" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisThrows(), "expected exception"
+      </Original>
+      <Expanded>
+        "expected exception" equals: "expected exception"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="REQUIRE_THROWS_WITH" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisThrows(), "should fail"
+      </Original>
+      <Expanded>
+        "expected exception" equals: "should fail"
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Nested generators and captured variables" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        3 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        4 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        5 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        6 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        -5 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        -4 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        90 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        91 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        92 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        93 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        94 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        95 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        96 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        97 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        98 > -6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        values > -6
+      </Original>
+      <Expanded>
+        99 > -6
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Nice descriptive name" tags="[.][tag1][tag2][tag3]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Warning>
+      This one ran
+    </Warning>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Non-std exceptions can be translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      custom exception
+    </Exception>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Objects that evaluated in boolean contexts can be checked" tags="[SafeBool][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        True
+      </Original>
+      <Expanded>
+        {?}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        !False
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        !(False)
+      </Original>
+      <Expanded>
+        !{?}
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Optionally static assertions" tags="[compilation]" filename="tests/<exe-name>/UsageTests/Compilation.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Ordering comparison checks that should fail" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven > 7
+      </Original>
+      <Expanded>
+        7 > 7
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven &lt; 7
+      </Original>
+      <Expanded>
+        7 &lt; 7
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven > 8
+      </Original>
+      <Expanded>
+        7 > 8
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven &lt; 6
+      </Original>
+      <Expanded>
+        7 &lt; 6
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven &lt; 0
+      </Original>
+      <Expanded>
+        7 &lt; 0
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven &lt; -1
+      </Original>
+      <Expanded>
+        7 &lt; -1
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven >= 8
+      </Original>
+      <Expanded>
+        7 >= 8
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven &lt;= 6
+      </Original>
+      <Expanded>
+        7 &lt;= 6
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one &lt; 9
+      </Original>
+      <Expanded>
+        9.1f &lt; 9
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one > 10
+      </Original>
+      <Expanded>
+        9.1f > 10
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one > 9.2
+      </Original>
+      <Expanded>
+        9.1f > 9.2
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello > "hello"
+      </Original>
+      <Expanded>
+        "hello" > "hello"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello &lt; "hello"
+      </Original>
+      <Expanded>
+        "hello" &lt; "hello"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello > "hellp"
+      </Original>
+      <Expanded>
+        "hello" > "hellp"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello > "z"
+      </Original>
+      <Expanded>
+        "hello" > "z"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello &lt; "hellm"
+      </Original>
+      <Expanded>
+        "hello" &lt; "hellm"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello &lt; "a"
+      </Original>
+      <Expanded>
+        "hello" &lt; "a"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello >= "z"
+      </Original>
+      <Expanded>
+        "hello" >= "z"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello &lt;= "a"
+      </Original>
+      <Expanded>
+        "hello" &lt;= "a"
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Ordering comparison checks that should succeed" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven &lt; 8
+      </Original>
+      <Expanded>
+        7 &lt; 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven > 6
+      </Original>
+      <Expanded>
+        7 > 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven > 0
+      </Original>
+      <Expanded>
+        7 > 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven > -1
+      </Original>
+      <Expanded>
+        7 > -1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven >= 7
+      </Original>
+      <Expanded>
+        7 >= 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven >= 6
+      </Original>
+      <Expanded>
+        7 >= 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven &lt;= 7
+      </Original>
+      <Expanded>
+        7 &lt;= 7
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.int_seven &lt;= 8
+      </Original>
+      <Expanded>
+        7 &lt;= 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one > 9
+      </Original>
+      <Expanded>
+        9.1f > 9
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one &lt; 10
+      </Original>
+      <Expanded>
+        9.1f &lt; 10
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.float_nine_point_one &lt; 9.2
+      </Original>
+      <Expanded>
+        9.1f &lt; 9.2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello &lt;= "hello"
+      </Original>
+      <Expanded>
+        "hello" &lt;= "hello"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello >= "hello"
+      </Original>
+      <Expanded>
+        "hello" >= "hello"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello &lt; "hellp"
+      </Original>
+      <Expanded>
+        "hello" &lt; "hellp"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello &lt; "zebra"
+      </Original>
+      <Expanded>
+        "hello" &lt; "zebra"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello > "hellm"
+      </Original>
+      <Expanded>
+        "hello" > "hellm"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        data.str_hello > "a"
+      </Original>
+      <Expanded>
+        "hello" > "a"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Our PCG implementation provides expected results for known seeds" tags="[rng]" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+    <Section name="Default seeded" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          4242248763 (0x<hex digits>)
+==
+4242248763 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          1867888929 (0x<hex digits>)
+==
+1867888929 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          1276619030 (0x<hex digits>)
+==
+1276619030 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          1911218783 (0x<hex digits>)
+==
+1911218783 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          1827115164 (0x<hex digits>)
+==
+1827115164 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Specific seed" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          1472234645 (0x<hex digits>)
+==
+1472234645 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          868832940 (0x<hex digits>)
+==
+868832940 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          570883446 (0x<hex digits>)
+==
+570883446 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          889299803 (0x<hex digits>)
 ==
-"some string += the stringref contents"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="StringRef + StringRef" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-          <Original>
-            together == "abrakadabra"
-          </Original>
-          <Expanded>
-            "abrakadabra" == "abrakadabra"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="StringRef at compilation time" tags="[constexpr][StringRef][Strings]" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-      <Section name="Simple constructors" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <OverallResults successes="15" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="UDL construction" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Stringifying std::chrono::duration helpers" tags="[chrono][toString]" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+889299803 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          4261393167 (0x<hex digits>)
+==
+4261393167 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          1472234645 (0x<hex digits>)
+==
+1472234645 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          868832940 (0x<hex digits>)
+==
+868832940 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          570883446 (0x<hex digits>)
+==
+570883446 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          889299803 (0x<hex digits>)
+==
+889299803 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/RandomNumberGeneration.tests.cpp" >
+        <Original>
+          rng() == 0x<hex digits>
+        </Original>
+        <Expanded>
+          4261393167 (0x<hex digits>)
+==
+4261393167 (0x<hex digits>)
+        </Expanded>
+      </Expression>
+      <OverallResults successes="10" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Output from all sections is reported" tags="[.][failing][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+        Message from section one
+      </Failure>
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+        Message from section two
+      </Failure>
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Overloaded comma or address-of operators are not used" tags="[matchers][templated]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        ( EvilMatcher(), EvilMatcher() ), EvilCommaOperatorUsed
+      </Original>
+      <Expanded>
+        ( EvilMatcher(), EvilMatcher() ), EvilCommaOperatorUsed
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        &amp;EvilMatcher(), EvilAddressOfOperatorUsed
+      </Original>
+      <Expanded>
+        &amp;EvilMatcher(), EvilAddressOfOperatorUsed
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        EvilMatcher() || ( EvilMatcher() &amp;&amp; !EvilMatcher() )
+      </Original>
+      <Expanded>
+        EvilMatcher() || ( EvilMatcher() &amp;&amp; !EvilMatcher() )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        ( EvilMatcher() &amp;&amp; EvilMatcher() ) || !EvilMatcher()
+      </Original>
+      <Expanded>
+        ( EvilMatcher() &amp;&amp; EvilMatcher() ) || !EvilMatcher()
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Parse test names and tags" tags="[command-line][test-spec]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+    <Section name="Empty test spec should have no filters" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Test spec from empty string should have no filters" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Test spec from just a comma should have no filters" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Test spec from name should have one filter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Test spec from quoted name should have one filter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Test spec from name should have one filter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Wildcard at the start" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcD ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          parseTestSpec( "*a" ).matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Wildcard at the end" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcD ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          parseTestSpec( "a*" ).matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Wildcard at both ends" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcD ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          parseTestSpec( "*a*" ).matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Redundant wildcard at the start" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Redundant wildcard at the end" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Redundant wildcard at both ends" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Wildcard at both ends, redundant at start" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcD ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Just wildcard" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcD ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Single tag" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Single tag, two matches" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Two tags" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Two tags, spare separated" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Wildcarded name and tag" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          minute == seconds
+          spec.matches( *tcA ) == false
         </Original>
         <Expanded>
-          1 m == 60 s
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          hour != seconds
+          spec.matches( *tcB ) == false
         </Original>
         <Expanded>
-          1 h != 60 s
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          micro != milli
+          spec.matches( *tcC ) == true
         </Original>
         <Expanded>
-          1 us != 1 ms
+          true == true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          nano != micro
+          spec.matches( *tcD ) == false
         </Original>
         <Expanded>
-          1 ns != 1 us
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Stringifying std::chrono::duration with weird ratios" tags="[chrono][toString]" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Single tag exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          half_minute != femto_second
+          spec.hasFilters() == true
         </Original>
         <Expanded>
-          1 [30/1]s != 1 fs
+          true == true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          pico_second != atto_second
+          spec.matches( *tcA ) == true
         </Original>
         <Expanded>
-          1 ps != 1 as
+          true == true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Stringifying std::chrono::time_point&lt;system_clock>" tags="[chrono][toString]" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          now != later
+          spec.matches( *tcB ) == false
         </Original>
         <Expanded>
-          {iso8601-timestamp}
-!=
-{iso8601-timestamp}
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Tabs and newlines show in output" tags="[.][failing][whitespace]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          s1 == s2
+          spec.matches( *tcC ) == false
         </Original>
         <Expanded>
-          "if ($b == 10) {
-		$a	= 20;
-}"
-==
-"if ($b == 10) {
-	$a = 20;
-}
-"
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Tag alias can be registered against tag patterns" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-      <Section name="The same tag alias can only be registered once" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-          <Original>
-            what, Contains( "[@zzz]" )
-          </Original>
-          <Expanded>
-            "error: tag alias, '[@zzz]' already registered.
-	First seen at: file:2
-	Redefined at: file:10" contains: "[@zzz]"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-          <Original>
-            what, Contains( "file" )
-          </Original>
-          <Expanded>
-            "error: tag alias, '[@zzz]' already registered.
-	First seen at: file:2
-	Redefined at: file:10" contains: "file"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-          <Original>
-            what, Contains( "2" )
-          </Original>
-          <Expanded>
-            "error: tag alias, '[@zzz]' already registered.
-	First seen at: file:2
-	Redefined at: file:10" contains: "2"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-          <Original>
-            what, Contains( "10" )
-          </Original>
-          <Expanded>
-            "error: tag alias, '[@zzz]' already registered.
-	First seen at: file:2
-	Redefined at: file:10" contains: "10"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Tag aliases must be of the form [@name]" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-        <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-          <Original>
-            registry.add( "[no ampersat]", "", Catch::SourceLineInfo( "file", 3 ) )
-          </Original>
-          <Expanded>
-            registry.add( "[no ampersat]", "", Catch::SourceLineInfo( "file", 3 ) )
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-          <Original>
-            registry.add( "[the @ is not at the start]", "", Catch::SourceLineInfo( "file", 3 ) )
-          </Original>
-          <Expanded>
-            registry.add( "[the @ is not at the start]", "", Catch::SourceLineInfo( "file", 3 ) )
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-          <Original>
-            registry.add( "@no square bracket at start]", "", Catch::SourceLineInfo( "file", 3 ) )
-          </Original>
-          <Expanded>
-            registry.add( "@no square bracket at start]", "", Catch::SourceLineInfo( "file", 3 ) )
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-          <Original>
-            registry.add( "[@no square bracket at end", "", Catch::SourceLineInfo( "file", 3 ) )
-          </Original>
-          <Expanded>
-            registry.add( "[@no square bracket at end", "", Catch::SourceLineInfo( "file", 3 ) )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case method with test types specified inside std::tuple - MyTypes - 0" tags="[class][list][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="One tag exclusion and one tag inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          Template_Fixture&lt;TestType>::m_a == 1
+          spec.hasFilters() == true
         </Original>
         <Expanded>
-          1 == 1
+          true == true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case method with test types specified inside std::tuple - MyTypes - 1" tags="[class][list][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          Template_Fixture&lt;TestType>::m_a == 1
+          spec.matches( *tcA ) == false
         </Original>
         <Expanded>
-          1 == 1
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case method with test types specified inside std::tuple - MyTypes - 2" tags="[class][list][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          Template_Fixture&lt;TestType>::m_a == 1
+          spec.matches( *tcB ) == true
         </Original>
         <Expanded>
-          1.0 == 1
+          true == true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case with test types specified inside non-copyable and non-movable std::tuple - NonCopyableAndNonMovableTypes - 0" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          sizeof(TestType) > 0
+          spec.matches( *tcC ) == false
         </Original>
         <Expanded>
-          1 > 0
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case with test types specified inside non-copyable and non-movable std::tuple - NonCopyableAndNonMovableTypes - 1" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="One tag exclusion and one wldcarded name inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          sizeof(TestType) > 0
+          spec.hasFilters() == true
         </Original>
         <Expanded>
-          4 > 0
+          true == true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case with test types specified inside non-default-constructible std::tuple - MyNonDefaultConstructibleTypes - 0" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          sizeof(TestType) > 0
+          spec.matches( *tcA ) == false
         </Original>
         <Expanded>
-          1 > 0
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case with test types specified inside non-default-constructible std::tuple - MyNonDefaultConstructibleTypes - 1" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          sizeof(TestType) > 0
+          spec.matches( *tcB ) == false
         </Original>
         <Expanded>
-          4 > 0
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case with test types specified inside std::tuple - MyTypes - 0" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          sizeof(TestType) > 0
+          spec.matches( *tcC ) == false
         </Original>
         <Expanded>
-          4 > 0
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case with test types specified inside std::tuple - MyTypes - 1" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          sizeof(TestType) > 0
+          spec.matches( *tcD ) == true
         </Original>
         <Expanded>
-          1 > 0
+          true == true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Template test case with test types specified inside std::tuple - MyTypes - 2" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="One tag exclusion, using exclude:, and one wldcarded name inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          sizeof(TestType) > 0
+          spec.hasFilters() == true
         </Original>
         <Expanded>
-          4 > 0
+          true == true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="TemplateTest: vectors can be sized and resized - float" tags="[template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcA ) == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcB ) == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 10
-          </Original>
-          <Expanded>
-            10 == 10
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 10
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcC ) == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcD ) == true
         </Original>
         <Expanded>
-          5 >= 5
+          true == true
         </Expanded>
       </Expression>
-      <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 5
-          </Original>
-          <Expanded>
-            5 >= 5
-          </Expanded>
-        </Expression>
-        <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              v.capacity() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="name exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcD ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="wildcarded name exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcD ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="wildcarded name exclusion with tag inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcD ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="wildcarded name exclusion, using exclude:, with tag inclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.hasFilters() == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcA ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcB ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcC ) == true
         </Original>
         <Expanded>
-          5 == 5
+          true == true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcD ) == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 10
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="two wildcarded names" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.hasFilters() == true
         </Original>
         <Expanded>
-          5 == 5
+          true == true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcA ) == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 5
-          </Original>
-          <Expanded>
-            5 >= 5
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="TemplateTest: vectors can be sized and resized - int" tags="[template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcB ) == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcC ) == true
         </Original>
         <Expanded>
-          5 >= 5
+          true == true
         </Expanded>
       </Expression>
-      <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 10
-          </Original>
-          <Expanded>
-            10 == 10
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 10
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcD ) == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="empty tag" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.hasFilters() == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 5
-          </Original>
-          <Expanded>
-            5 >= 5
-          </Expanded>
-        </Expression>
-        <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              v.capacity() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcA ) == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcB ) == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 10
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcC ) == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcD ) == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 5
-          </Original>
-          <Expanded>
-            5 >= 5
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="TemplateTest: vectors can be sized and resized - std::string" tags="[template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="empty quoted name" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.hasFilters() == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcA ) == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 10
-          </Original>
-          <Expanded>
-            10 == 10
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 10
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcB ) == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcC ) == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 5
-          </Original>
-          <Expanded>
-            5 >= 5
-          </Expanded>
-        </Expression>
-        <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              v.capacity() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcD ) == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="quoted string followed by tag exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.hasFilters() == true
         </Original>
         <Expanded>
-          5 >= 5
+          true == true
         </Expanded>
       </Expression>
-      <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 10
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *tcA ) == false
         </Original>
         <Expanded>
-          5 == 5
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *tcB ) == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 5
-          </Original>
-          <Expanded>
-            5 >= 5
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="TemplateTest: vectors can be sized and resized - std::tuple&lt;int,float>" tags="[template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcC ) == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          spec.matches( *tcD ) == true
+        </Original>
+        <Expanded>
+          true == true
+        </Expanded>
+      </Expression>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Leading and trailing spaces in test spec" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *fakeTestCase( "  aardvark " ) )
         </Original>
         <Expanded>
-          5 == 5
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *fakeTestCase( "  aardvark" ) )
         </Original>
         <Expanded>
-          5 >= 5
+          true
         </Expanded>
       </Expression>
-      <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 10
-          </Original>
-          <Expanded>
-            10 == 10
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 10
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *fakeTestCase( " aardvark " ) )
         </Original>
         <Expanded>
-          5 == 5
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *fakeTestCase( "aardvark " ) )
         </Original>
         <Expanded>
-          5 >= 5
+          true
         </Expanded>
       </Expression>
-      <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 5
-          </Original>
-          <Expanded>
-            5 >= 5
-          </Expanded>
-        </Expression>
-        <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              v.capacity() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *fakeTestCase( "aardvark" ) )
         </Original>
         <Expanded>
-          5 == 5
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Leading and trailing spaces in test name" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *fakeTestCase( "  aardvark " ) )
         </Original>
         <Expanded>
-          5 >= 5
+          true
         </Expanded>
       </Expression>
-      <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 10
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == 5
+          spec.matches( *fakeTestCase( "  aardvark" ) )
         </Original>
         <Expanded>
-          5 == 5
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          spec.matches( *fakeTestCase( " aardvark " ) )
         </Original>
         <Expanded>
-          5 >= 5
+          true
         </Expanded>
       </Expression>
-      <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 5
-          </Original>
-          <Expanded>
-            5 >= 5
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="TemplateTestSig: vectors can be sized and resized - (std::tuple&lt;int, float>), 6" tags="[nttp][template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == V
+          spec.matches( *fakeTestCase( "aardvark " ) )
         </Original>
         <Expanded>
-          6 == 6
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          spec.matches( *fakeTestCase( "aardvark" ) )
         </Original>
         <Expanded>
-          6 >= 6
+          true
         </Expanded>
       </Expression>
-      <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 2 * V
-          </Original>
-          <Expanded>
-            12 == 12
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 2 * V
-          </Original>
-          <Expanded>
-            12 >= 12
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortened hide tags are split apart when parsing" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == V
+          spec.matches(*fakeTestCase("hidden and foo", "[.][foo]"))
         </Original>
         <Expanded>
-          6 == 6
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          !(spec.matches(*fakeTestCase("only foo", "[foo]")))
         </Original>
         <Expanded>
-          6 >= 6
+          !false
         </Expanded>
       </Expression>
-      <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= V
-          </Original>
-          <Expanded>
-            6 >= 6
-          </Expanded>
-        </Expression>
-        <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              v.capacity() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortened hide tags also properly handle exclusion" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == V
+          !(spec.matches(*fakeTestCase("hidden and foo", "[.][foo]")))
         </Original>
         <Expanded>
-          6 == 6
+          !false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          !(spec.matches(*fakeTestCase("only foo", "[foo]")))
         </Original>
         <Expanded>
-          6 >= 6
+          !false
         </Expanded>
       </Expression>
-      <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == V
-          </Original>
-          <Expanded>
-            6 == 6
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 2 * V
-          </Original>
-          <Expanded>
-            12 >= 12
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == V
+          !(spec.matches(*fakeTestCase("only hidden", "[.]")))
         </Original>
         <Expanded>
-          6 == 6
+          !false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          spec.matches(*fakeTestCase("neither foo nor hidden", "[bar]"))
         </Original>
         <Expanded>
-          6 >= 6
+          true
         </Expanded>
       </Expression>
-      <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == V
-          </Original>
-          <Expanded>
-            6 == 6
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= V
-          </Original>
-          <Expanded>
-            6 >= 6
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="TemplateTestSig: vectors can be sized and resized - float,4" tags="[nttp][template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.size() == V
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Pointers can be compared to null" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        p == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        p == pNULL
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        p != 0
+      </Original>
+      <Expanded>
+        0x<hex digits> != 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        cp != 0
+      </Original>
+      <Expanded>
+        0x<hex digits> != 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        cpc != 0
+      </Original>
+      <Expanded>
+        0x<hex digits> != 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        returnsNull() == 0
+      </Original>
+      <Expanded>
+        {null string} == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        returnsConstNull() == 0
+      </Original>
+      <Expanded>
+        {null string} == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        0 != p
+      </Original>
+      <Expanded>
+        0 != 0x<hex digits>
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Precision of floating point stringification can be set" tags="[floatingPoint][toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+    <Section name="Floats" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+        <Original>
+          str1.size() == 3 + 5
         </Original>
         <Expanded>
-          4 == 4
+          8 == 8
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          str2.size() == 3 + 10
         </Original>
         <Expanded>
-          4 >= 4
+          13 == 13
         </Expanded>
       </Expression>
-      <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 2 * V
-          </Original>
-          <Expanded>
-            8 == 8
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 2 * V
-          </Original>
-          <Expanded>
-            8 >= 8
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Double" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          v.size() == V
+          str1.size() == 2 + 5
         </Original>
         <Expanded>
-          4 == 4
+          7 == 7
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          str2.size() == 2 + 15
         </Original>
         <Expanded>
-          4 >= 4
+          17 == 17
         </Expanded>
       </Expression>
-      <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= V
-          </Original>
-          <Expanded>
-            4 >= 4
-          </Expanded>
-        </Expression>
-        <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              v.capacity() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Predicate matcher can accept const char*" tags="[compilation][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        "foo", Predicate&lt;const char*>( []( const char* const&amp; ) { return true; } )
+      </Original>
+      <Expanded>
+        "foo" matches undescribed predicate
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Process can be configured on command line" tags="[command-line][config]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+    <Section name="empty args don't cause a crash" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Original>
+          result
+        </Original>
+        <Expanded>
+          {?}
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == V
+          config.processName == ""
         </Original>
         <Expanded>
-          4 == 4
+          "" == ""
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="default - no arguments" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          result
         </Original>
         <Expanded>
-          4 >= 4
+          {?}
         </Expanded>
       </Expression>
-      <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == V
-          </Original>
-          <Expanded>
-            4 == 4
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 2 * V
-          </Original>
-          <Expanded>
-            8 >= 8
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == V
+          config.processName == "test"
         </Original>
         <Expanded>
-          4 == 4
+          "test" == "test"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          config.shouldDebugBreak == false
         </Original>
         <Expanded>
-          4 >= 4
+          false == false
         </Expanded>
       </Expression>
-      <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == V
-          </Original>
-          <Expanded>
-            4 == 4
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= V
-          </Original>
-          <Expanded>
-            4 >= 4
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="TemplateTestSig: vectors can be sized and resized - int,5" tags="[nttp][template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == V
+          config.abortAfter == -1
         </Original>
         <Expanded>
-          5 == 5
+          -1 == -1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          config.noThrow == false
         </Original>
         <Expanded>
-          5 >= 5
+          false == false
         </Expanded>
       </Expression>
-      <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 2 * V
-          </Original>
-          <Expanded>
-            10 == 10
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 2 * V
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.size() == V
+          config.reporterName == "console"
         </Original>
         <Expanded>
-          5 == 5
+          "console" == "console"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
         <Original>
-          v.capacity() >= V
+          !(cfg.hasTestFilters())
         </Original>
         <Expanded>
-          5 >= 5
+          !false
         </Expanded>
       </Expression>
-      <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="7" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="test lists" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="Specify one test case using" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.size() == 0
+            result
           </Original>
           <Expanded>
-            0 == 0
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.capacity() >= V
+            cfg.hasTestFilters()
           </Original>
           <Expanded>
-            5 >= 5
+            true
           </Expanded>
         </Expression>
-        <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              v.capacity() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.size() == V
-        </Original>
-        <Expanded>
-          5 == 5
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.capacity() >= V
-        </Original>
-        <Expanded>
-          5 >= 5
-        </Expanded>
-      </Expression>
-      <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.size() == V
+            cfg.testSpec().matches(*fakeTestCase("notIncluded")) == false
           </Original>
           <Expanded>
-            5 == 5
+            false == false
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.capacity() >= 2 * V
+            cfg.testSpec().matches(*fakeTestCase("test1"))
           </Original>
           <Expanded>
-            10 >= 10
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        <OverallResults successes="4" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.size() == V
-        </Original>
-        <Expanded>
-          5 == 5
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.capacity() >= V
-        </Original>
-        <Expanded>
-          5 >= 5
-        </Expanded>
-      </Expression>
-      <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="test lists" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="Specify one test case exclusion using exclude:" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.size() == V
+            result
           </Original>
           <Expanded>
-            5 == 5
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.capacity() >= V
+            cfg.hasTestFilters()
           </Original>
           <Expanded>
-            5 >= 5
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="TemplateTestSig: vectors can be sized and resized - std::string,15" tags="[nttp][template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.size() == V
-        </Original>
-        <Expanded>
-          15 == 15
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.capacity() >= V
-        </Original>
-        <Expanded>
-          15 >= 15
-        </Expanded>
-      </Expression>
-      <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.size() == 2 * V
+            cfg.testSpec().matches(*fakeTestCase("test1")) == false
           </Original>
           <Expanded>
-            30 == 30
+            false == false
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.capacity() >= 2 * V
+            cfg.testSpec().matches(*fakeTestCase("alwaysIncluded"))
           </Original>
           <Expanded>
-            30 >= 30
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        <OverallResults successes="4" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.size() == V
-        </Original>
-        <Expanded>
-          15 == 15
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.capacity() >= V
-        </Original>
-        <Expanded>
-          15 >= 15
-        </Expanded>
-      </Expression>
-      <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="test lists" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="Specify one test case exclusion using ~" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.size() == 0
+            result
           </Original>
           <Expanded>
-            0 == 0
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cfg.hasTestFilters()
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.capacity() >= V
+            cfg.testSpec().matches(*fakeTestCase("test1")) == false
           </Original>
           <Expanded>
-            15 >= 15
+            false == false
           </Expanded>
         </Expression>
-        <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              v.capacity() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cfg.testSpec().matches(*fakeTestCase("alwaysIncluded"))
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <OverallResults successes="4" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.size() == V
-        </Original>
-        <Expanded>
-          15 == 15
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.capacity() >= V
-        </Original>
-        <Expanded>
-          15 >= 15
-        </Expanded>
-      </Expression>
-      <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="-r/console" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.size() == V
+            cli.parse({"test", "-r", "console"})
           </Original>
           <Expanded>
-            15 == 15
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.capacity() >= 2 * V
+            config.reporterName == "console"
           </Original>
           <Expanded>
-            30 >= 30
+            "console" == "console"
           </Expanded>
         </Expression>
         <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.size() == V
-        </Original>
-        <Expanded>
-          15 == 15
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          v.capacity() >= V
-        </Original>
-        <Expanded>
-          15 >= 15
-        </Expanded>
-      </Expression>
-      <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="-r/xml" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.size() == V
+            cli.parse({"test", "-r", "xml"})
           </Original>
           <Expanded>
-            15 == 15
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            v.capacity() >= V
+            config.reporterName == "xml"
           </Original>
           <Expanded>
-            15 >= 15
+            "xml" == "xml"
           </Expanded>
         </Expression>
         <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Test case with one argument" filename="tests/<exe-name>/UsageTests/VariadicMacros.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Test enum bit values" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-        <Original>
-          0x<hex digits> == bit30and31
-        </Original>
-        <Expanded>
-          3221225472 (0x<hex digits>) == 3221225472
-        </Expanded>
-      </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Test with special, characters &quot;in name" tags="[cli][regression]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="The NO_FAIL macro reports a failure but does not fail the test" tags="[messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Expression success="false" type="CHECK_NOFAIL" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        <Original>
-          1 == 2
-        </Original>
-        <Expanded>
-          1 == 2
-        </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="The default listing implementation write to provided stream" tags="[reporter-helpers][reporters]" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-      <Section name="Listing tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="--reporter/junit" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            listingString, Contains("[fakeTag]"s)
+            cli.parse({"test", "--reporter", "junit"})
           </Original>
           <Expanded>
-            "All available tags:
-   1  [fakeTag]
-1 tag
-
-" contains: "[fakeTag]"
+            {?}
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Listing reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            listingString, Contains("fake reporter"s)
+            config.reporterName == "junit"
           </Original>
           <Expanded>
-            "Available reporters:
-  fake reporter:  fake description
-
-" contains: "fake reporter"
+            "junit" == "junit"
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Listing tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="Only one reporter is accepted" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
+            !(cli.parse({ "test", "-r", "xml", "-r", "junit" }))
           </Original>
           <Expanded>
-            "All available test cases:
-  fake test name
-      [fakeTestTag]
-1 test case
-
-" ( contains: "fake test name" and contains: "fakeTestTag" )
+            !{?}
           </Expanded>
         </Expression>
         <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="This test 'should' fail but doesn't" tags="[!shouldfail][.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Thrown string literals are translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        For some reason someone is throwing a string literal!
-      </Exception>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Tracker" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          testCase.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          s1.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Section name="successfully close one section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="reporter" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="must match one of the available ones" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s1.isSuccessfullyCompleted()
+            !result
           </Original>
           <Expanded>
             true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isComplete() == false
+            result.errorMessage(), Contains("Unrecognized reporter")
           </Original>
           <Expanded>
-            false == false
+            "Unrecognized reporter, 'unsupported'. Check available with --list-reporters" contains: "Unrecognized reporter"
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="debugger" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="-b" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            ctx.completedCycle()
+            cli.parse({"test", "-b"})
           </Original>
           <Expanded>
-            true
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isSuccessfullyCompleted()
+            config.shouldDebugBreak == true
           </Original>
           <Expanded>
-            true
+            true == true
           </Expanded>
         </Expression>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          testCase.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          s1.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Section name="fail one section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="debugger" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="--break" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cli.parse({"test", "--break"})
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s1.isComplete()
+            config.shouldDebugBreak
           </Original>
           <Expanded>
             true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="-a aborts after first failure" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s1.isSuccessfullyCompleted() == false
+            cli.parse({"test", "-a"})
           </Original>
           <Expanded>
-            false == false
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isComplete() == false
+            config.abortAfter == 1
           </Original>
           <Expanded>
-            false == false
+            1 == 1
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="-x 2 aborts after two failures" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            ctx.completedCycle()
+            cli.parse({"test", "-x", "2"})
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.abortAfter == 2
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="-x must be numeric" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            !result
           </Original>
           <Expanded>
             true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isSuccessfullyCompleted() == false
+            result.errorMessage(), Contains("convert") &amp;&amp; Contains("oops")
           </Original>
           <Expanded>
-            false == false
+            "Unable to convert 'oops' to destination type" ( contains: "convert" and contains: "oops" )
           </Expanded>
         </Expression>
-        <Section name="re-enter after failed section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Section name="Accepted options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
             <Original>
-              testCase2.isOpen()
+              cli.parse({"test", "--wait-for-keypress", std::get&lt;0>(input)})
             </Original>
             <Expanded>
-              true
+              {?}
             </Expanded>
           </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
             <Original>
-              s1b.isOpen() == false
+              config.waitForKeypress == std::get&lt;1>(input)
             </Original>
             <Expanded>
-              false == false
+              0 == 0
             </Expanded>
           </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Section name="Accepted options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
             <Original>
-              ctx.completedCycle()
+              cli.parse({"test", "--wait-for-keypress", std::get&lt;0>(input)})
             </Original>
             <Expanded>
-              true
+              {?}
             </Expanded>
           </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
             <Original>
-              testCase.isComplete()
+              config.waitForKeypress == std::get&lt;1>(input)
             </Original>
             <Expanded>
-              true
+              1 == 1
             </Expanded>
           </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Section name="Accepted options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+            <Original>
+              cli.parse({"test", "--wait-for-keypress", std::get&lt;0>(input)})
+            </Original>
+            <Expanded>
+              {?}
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+            <Original>
+              config.waitForKeypress == std::get&lt;1>(input)
+            </Original>
+            <Expanded>
+              2 == 2
+            </Expanded>
+          </Expression>
+          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Section name="Accepted options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+            <Original>
+              cli.parse({"test", "--wait-for-keypress", std::get&lt;0>(input)})
+            </Original>
+            <Expanded>
+              {?}
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+            <Original>
+              config.waitForKeypress == std::get&lt;1>(input)
+            </Original>
+            <Expanded>
+              3 == 3
+            </Expanded>
+          </Expression>
+          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="abort" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="wait-for-keypress" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Section name="invalid options are reported" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
             <Original>
-              testCase.isSuccessfullyCompleted()
+              !result
             </Original>
             <Expanded>
               true
             </Expanded>
           </Expression>
-          <OverallResults successes="5" failures="0" expectedFailures="0"/>
+          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+            <Original>
+              result.errorMessage(), Contains("never") &amp;&amp; Contains("both")
+            </Original>
+            <Expanded>
+              "keypress argument must be one of: never, start, exit or both. 'sometimes' not recognised" ( contains: "never" and contains: "both" )
+            </Expanded>
+          </Expression>
+          <OverallResults successes="2" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="10" failures="0" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          testCase.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          s1.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Section name="fail one section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="nothrow" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="-e" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cli.parse({"test", "-e"})
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.noThrow
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="nothrow" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="--nothrow" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cli.parse({"test", "--nothrow"})
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.noThrow
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="output filename" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="-o filename" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cli.parse({"test", "-o", "filename.ext"})
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s1.isComplete()
+            config.outputFilename == "filename.ext"
           </Original>
           <Expanded>
-            true
+            "filename.ext" == "filename.ext"
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="output filename" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="--out" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s1.isSuccessfullyCompleted() == false
+            cli.parse({"test", "--out", "filename.ext"})
           </Original>
           <Expanded>
-            false == false
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isComplete() == false
+            config.outputFilename == "filename.ext"
           </Original>
           <Expanded>
-            false == false
+            "filename.ext" == "filename.ext"
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="combinations" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="Single character flags can be combined" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            ctx.completedCycle()
+            cli.parse({"test", "-abe"})
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.abortAfter == 1
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.shouldDebugBreak
           </Original>
           <Expanded>
             true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isSuccessfullyCompleted() == false
+            config.noThrow == true
           </Original>
           <Expanded>
-            false == false
+            true == true
           </Expanded>
         </Expression>
-        <Section name="re-enter after failed section and find next section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              testCase2.isOpen()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              s1b.isOpen() == false
-            </Original>
-            <Expanded>
-              false == false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              s2.isOpen()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              ctx.completedCycle()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              testCase.isComplete()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              testCase.isSuccessfullyCompleted()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="6" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="11" failures="0" expectedFailures="0"/>
+        <OverallResults successes="4" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          testCase.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          s1.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Section name="successfully close one section, then find another" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="without option" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s2.isOpen() == false
+            cli.parse({"test"})
           </Original>
           <Expanded>
-            false == false
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isComplete() == false
+            config.useColour == UseColour::Auto
           </Original>
           <Expanded>
-            false == false
+            0 == 0
           </Expanded>
         </Expression>
-        <Section name="Re-enter - skips S1 and enters S2" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              testCase2.isOpen()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              s1b.isOpen() == false
-            </Original>
-            <Expanded>
-              false == false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              s2b.isOpen()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              ctx.completedCycle() == false
-            </Original>
-            <Expanded>
-              false == false
-            </Expanded>
-          </Expression>
-          <Section name="Successfully close S2" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                ctx.completedCycle()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                s2b.isSuccessfullyCompleted()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                testCase2.isComplete() == false
-              </Original>
-              <Expanded>
-                false == false
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                testCase2.isSuccessfullyCompleted()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <OverallResults successes="4" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="8" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="10" failures="0" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          testCase.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          s1.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Section name="successfully close one section, then find another" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="auto" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s2.isOpen() == false
+            cli.parse({"test", "--use-colour", "auto"})
           </Original>
           <Expanded>
-            false == false
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isComplete() == false
+            config.useColour == UseColour::Auto
           </Original>
           <Expanded>
-            false == false
+            0 == 0
           </Expanded>
         </Expression>
-        <Section name="Re-enter - skips S1 and enters S2" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              testCase2.isOpen()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              s1b.isOpen() == false
-            </Original>
-            <Expanded>
-              false == false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              s2b.isOpen()
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Original>
-              ctx.completedCycle() == false
-            </Original>
-            <Expanded>
-              false == false
-            </Expanded>
-          </Expression>
-          <Section name="fail S2" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                ctx.completedCycle()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                s2b.isComplete()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                s2b.isSuccessfullyCompleted() == false
-              </Original>
-              <Expanded>
-                false == false
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                testCase2.isSuccessfullyCompleted() == false
-              </Original>
-              <Expanded>
-                false == false
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                testCase3.isOpen()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                s1c.isOpen() == false
-              </Original>
-              <Expanded>
-                false == false
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                s2c.isOpen() == false
-              </Original>
-              <Expanded>
-                false == false
-              </Expanded>
-            </Expression>
-            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-              <Original>
-                testCase3.isSuccessfullyCompleted()
-              </Original>
-              <Expanded>
-                true
-              </Expanded>
-            </Expression>
-            <OverallResults successes="8" failures="0" expectedFailures="0"/>
-          </Section>
-          <OverallResults successes="12" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="14" failures="0" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="yes" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cli.parse({"test", "--use-colour", "yes"})
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.useColour == UseColour::Yes
+          </Original>
+          <Expanded>
+            1 == 1
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          testCase.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Original>
-          s1.isOpen()
-        </Original>
-        <Expanded>
-          true
-        </Expanded>
-      </Expression>
-      <Section name="open a nested section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="no" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s2.isOpen()
+            cli.parse({"test", "--use-colour", "no"})
           </Original>
           <Expanded>
-            true
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.useColour == UseColour::No
+          </Original>
+          <Expanded>
+            2 == 2
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="use-colour" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="error" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s2.isComplete()
+            !result
           </Original>
           <Expanded>
             true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s1.isComplete() == false
+            result.errorMessage(), Contains( "colour mode must be one of" )
           </Original>
           <Expanded>
-            false == false
+            "colour mode must be one of: auto, yes or no. 'wrong' not recognised" contains: "colour mode must be one of"
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="samples" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            s1.isComplete()
+            cli.parse({ "test", "--benchmark-samples=200" })
           </Original>
           <Expanded>
-            true
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isComplete() == false
+            config.benchmarkSamples == 200
           </Original>
           <Expanded>
-            false == false
+            200 == 200
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="resamples" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
           <Original>
-            testCase.isComplete()
+            cli.parse({ "test", "--benchmark-resamples=20000" })
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.benchmarkResamples == 20000
+          </Original>
+          <Expanded>
+            20000 (0x<hex digits>) == 20000 (0x<hex digits>)
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="confidence-interval" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cli.parse({ "test", "--benchmark-confidence-interval=0.99" })
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.benchmarkConfidenceInterval == Catch::Approx(0.99)
+          </Original>
+          <Expanded>
+            0.99 == Approx( 0.99 )
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="no-analysis" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cli.parse({ "test", "--benchmark-no-analysis" })
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.benchmarkNoAnalysis
           </Original>
           <Expanded>
             true
           </Expanded>
         </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Benchmark options" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+      <Section name="warmup-time" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            cli.parse({ "test", "--benchmark-warmup-time=10" })
+          </Original>
+          <Expanded>
+            {?}
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+          <Original>
+            config.benchmarkWarmupTime == 10
+          </Original>
+          <Expanded>
+            10 == 10
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Trim strings" tags="[string-manip]" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Product with differing arities - std::tuple&lt;int, double, float>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        std::tuple_size&lt;TestType>::value >= 1
+      </Original>
+      <Expanded>
+        3 >= 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Product with differing arities - std::tuple&lt;int, double>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        std::tuple_size&lt;TestType>::value >= 1
+      </Original>
+      <Expanded>
+        2 >= 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Product with differing arities - std::tuple&lt;int>" tags="[product][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        std::tuple_size&lt;TestType>::value >= 1
+      </Original>
+      <Expanded>
+        1 >= 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Range type with sentinel" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        Catch::Detail::stringify(UsesSentinel{}) == "{  }"
+      </Original>
+      <Expanded>
+        "{  }" == "{  }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Reconstruction should be based on stringification: #914" tags="[.][Decomposition][failing]" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Decomposition.tests.cpp" >
+      <Original>
+        truthy(false)
+      </Original>
+      <Expanded>
+        Hey, its truthy!
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Regex string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Matches( "this STRING contains 'abc' as a substring" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" matches "this STRING contains 'abc' as a substring" case sensitively
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Matches( "contains 'abc' as a substring" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" matches "contains 'abc' as a substring" case sensitively
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Matches( "this string contains 'abc' as a" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" matches "this string contains 'abc' as a" case sensitively
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Regression test #1" tags="[matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        actual, !UnorderedEquals( expected )
+      </Original>
+      <Expanded>
+        { 'a', 'b' } not UnorderedEquals: { 'c', 'b' }
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Reporter's write listings to provided stream" tags="[reporters]" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="automake reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: automake
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fakeTag"s)
+        </Original>
+        <Expanded>
+          "All available tags:
+   1  [fakeTag]
+1 tag
+
+" contains: "fakeTag"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="automake reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: automake
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fake reporter"s)
+        </Original>
+        <Expanded>
+          "Available reporters:
+  fake reporter:  fake description
+
+" contains: "fake reporter"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="automake reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: automake
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
+        </Original>
+        <Expanded>
+          "All available test cases:
+  fake test name
+      [fakeTestTag]
+1 test case
+
+" ( contains: "fake test name" and contains: "fakeTestTag" )
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="compact reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: compact
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fakeTag"s)
+        </Original>
+        <Expanded>
+          "All available tags:
+   1  [fakeTag]
+1 tag
+
+" contains: "fakeTag"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="compact reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: compact
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fake reporter"s)
+        </Original>
+        <Expanded>
+          "Available reporters:
+  fake reporter:  fake description
+
+" contains: "fake reporter"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="compact reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: compact
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
+        </Original>
+        <Expanded>
+          "All available test cases:
+  fake test name
+      [fakeTestTag]
+1 test case
+
+" ( contains: "fake test name" and contains: "fakeTestTag" )
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="console reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: console
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fakeTag"s)
+        </Original>
+        <Expanded>
+          "All available tags:
+   1  [fakeTag]
+1 tag
+
+" contains: "fakeTag"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="console reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: console
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fake reporter"s)
+        </Original>
+        <Expanded>
+          "Available reporters:
+  fake reporter:  fake description
+
+" contains: "fake reporter"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="console reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: console
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
+        </Original>
+        <Expanded>
+          "All available test cases:
+  fake test name
+      [fakeTestTag]
+1 test case
+
+" ( contains: "fake test name" and contains: "fakeTestTag" )
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="junit reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: junit
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fakeTag"s)
+        </Original>
+        <Expanded>
+          "&lt;?xml version="1.0" encoding="UTF-8"?>
+All available tags:
+   1  [fakeTag]
+1 tag
+
+" contains: "fakeTag"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="junit reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: junit
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fake reporter"s)
+        </Original>
+        <Expanded>
+          "&lt;?xml version="1.0" encoding="UTF-8"?>
+Available reporters:
+  fake reporter:  fake description
+
+" contains: "fake reporter"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="junit reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: junit
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
+        </Original>
+        <Expanded>
+          "&lt;?xml version="1.0" encoding="UTF-8"?>
+All available test cases:
+  fake test name
+      [fakeTestTag]
+1 test case
+
+" ( contains: "fake test name" and contains: "fakeTestTag" )
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="sonarqube reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: sonarqube
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fakeTag"s)
+        </Original>
+        <Expanded>
+          "&lt;?xml version="1.0" encoding="UTF-8"?>
+All available tags:
+   1  [fakeTag]
+1 tag
+
+" contains: "fakeTag"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="sonarqube reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: sonarqube
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
         <Original>
-          trim(std::string(no_whitespace)) == no_whitespace
+          listingString, Contains("fake reporter"s)
         </Original>
         <Expanded>
-          "There is no extra whitespace here"
-==
-"There is no extra whitespace here"
+          "&lt;?xml version="1.0" encoding="UTF-8"?>
+Available reporters:
+  fake reporter:  fake description
+
+" contains: "fake reporter"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="sonarqube reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: sonarqube
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
         <Original>
-          trim(std::string(leading_whitespace)) == no_whitespace
+          listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
         </Original>
         <Expanded>
-          "There is no extra whitespace here"
-==
-"There is no extra whitespace here"
+          "&lt;?xml version="1.0" encoding="UTF-8"?>
+All available test cases:
+  fake test name
+      [fakeTestTag]
+1 test case
+
+" ( contains: "fake test name" and contains: "fakeTestTag" )
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="tap reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: tap
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
         <Original>
-          trim(std::string(trailing_whitespace)) == no_whitespace
+          listingString, Contains("fakeTag"s)
         </Original>
         <Expanded>
-          "There is no extra whitespace here"
-==
-"There is no extra whitespace here"
+          "All available tags:
+   1  [fakeTag]
+1 tag
+
+" contains: "fakeTag"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="tap reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: tap
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
         <Original>
-          trim(std::string(whitespace_at_both_ends)) == no_whitespace
+          listingString, Contains("fake reporter"s)
         </Original>
         <Expanded>
-          "There is no extra whitespace here"
-==
-"There is no extra whitespace here"
+          "Available reporters:
+  fake reporter:  fake description
+
+" contains: "fake reporter"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="tap reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: tap
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
         <Original>
-          trim(StringRef(no_whitespace)) == StringRef(no_whitespace)
+          listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
         </Original>
         <Expanded>
-          There is no extra whitespace here
-==
-There is no extra whitespace here
+          "All available test cases:
+  fake test name
+      [fakeTestTag]
+1 test case
+
+" ( contains: "fake test name" and contains: "fakeTestTag" )
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="teamcity reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: teamcity
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
         <Original>
-          trim(StringRef(leading_whitespace)) == StringRef(no_whitespace)
+          listingString, Contains("fakeTag"s)
         </Original>
         <Expanded>
-          There is no extra whitespace here
-==
-There is no extra whitespace here
+          "All available tags:
+   1  [fakeTag]
+1 tag
+
+" contains: "fakeTag"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="teamcity reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: teamcity
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
         <Original>
-          trim(StringRef(trailing_whitespace)) == StringRef(no_whitespace)
+          listingString, Contains("fake reporter"s)
         </Original>
         <Expanded>
-          There is no extra whitespace here
-==
-There is no extra whitespace here
+          "Available reporters:
+  fake reporter:  fake description
+
+" contains: "fake reporter"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="teamcity reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: teamcity
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
         <Original>
-          trim(StringRef(whitespace_at_both_ends)) == StringRef(no_whitespace)
+          listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
         </Original>
         <Expanded>
-          There is no extra whitespace here
-==
-There is no extra whitespace here
+          "All available test cases:
+  fake test name
+      [fakeTestTag]
+1 test case
+
+" ( contains: "fake test name" and contains: "fakeTestTag" )
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Unexpected exceptions can be translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        3.14
-      </Exception>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Upcasting special member functions" tags="[internals][unique-ptr]" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-      <Section name="Move constructor" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            bptr->i == 3
-          </Original>
-          <Expanded>
-            3 == 3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="move assignment" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            bptr->i == 3
-          </Original>
-          <Expanded>
-            3 == 3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Usage of AllMatch range matcher" tags="[matchers][quantifiers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-      <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            data, AllMatch(SizeIs(5))
-          </Original>
-          <Expanded>
-            { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } all match has size == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            data, !AllMatch(Contains(0) &amp;&amp; Contains(1))
-          </Original>
-          <Expanded>
-            { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } not all match ( contains element 0 and contains element 1 )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Type requires ADL found begin and end" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            needs_adl, AllMatch( Predicate&lt;int>( []( int elem ) { return elem &lt; 6; } ) )
-          </Original>
-          <Expanded>
-            { 1, 2, 3, 4, 5 } all match matches undescribed predicate
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Section name="All are read" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked, allMatch
-            </Original>
-            <Expanded>
-              { 1, 2, 3, 4, 5 } all match matches undescribed predicate
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[0]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[1]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[2]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[3]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[4]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="6" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Section name="Short-circuited" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked, !allMatch
-            </Original>
-            <Expanded>
-              { 1, 2, 3, 4, 5 } not all match matches undescribed predicate
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[0]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[1]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[2]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              !(mocked.derefed[3])
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              !(mocked.derefed[4])
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <OverallResults successes="6" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Usage of AnyMatch range matcher" tags="[matchers][quantifiers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-      <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            data, AnyMatch(SizeIs(5))
-          </Original>
-          <Expanded>
-            { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } any match has size == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            data, !AnyMatch(Contains(0) &amp;&amp; Contains(10))
-          </Original>
-          <Expanded>
-            { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } not any match ( contains element 0 and contains element 10 )
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Type requires ADL found begin and end" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            needs_adl, AnyMatch( Predicate&lt;int>( []( int elem ) { return elem &lt; 3; } ) )
-          </Original>
-          <Expanded>
-            { 1, 2, 3, 4, 5 } any match matches undescribed predicate
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Section name="All are read" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked, !anyMatch
-            </Original>
-            <Expanded>
-              { 1, 2, 3, 4, 5 } not any match matches undescribed predicate
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[0]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[1]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[2]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[3]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="xml reporter lists tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: xml
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fakeTag"s)
+        </Original>
+        <Expanded>
+          "&lt;?xml version="1.0" encoding="UTF-8"?>
+&lt;TagsFromMatchingTests>
+  &lt;Tag>
+    &lt;Count>1&lt;/Count>
+    &lt;Aliases>
+      &lt;Alias>fakeTag&lt;/Alias>
+    &lt;/Aliases>
+  &lt;/Tag>
+&lt;/TagsFromMatchingTests>" contains: "fakeTag"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="xml reporter lists reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: xml
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fake reporter"s)
+        </Original>
+        <Expanded>
+          "&lt;?xml version="1.0" encoding="UTF-8"?>
+&lt;AvailableReporters>
+  &lt;Reporter>
+    &lt;Name>fake reporter&lt;/Name>
+    &lt;Description>fake description&lt;/Description>
+  &lt;/Reporter>
+&lt;/AvailableReporters>" contains: "fake reporter"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Original>
+        !(factories.empty())
+      </Original>
+      <Expanded>
+        !false
+      </Expanded>
+    </Expression>
+    <Section name="xml reporter lists tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Info>
+        Tested reporter: xml
+      </Info>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
+        </Original>
+        <Expanded>
+          "&lt;?xml version="1.0" encoding="UTF-8"?>
+&lt;MatchingTests>
+  &lt;TestCase>
+    &lt;Name>fake test name&lt;/Name>
+    &lt;ClassName/>
+    &lt;Tags>[fakeTestTag]&lt;/Tags>
+    &lt;SourceInfo>
+      &lt;File>fake-file.cpp&lt;/File>
+      &lt;Line>123456789&lt;/Line>
+    &lt;/SourceInfo>
+  &lt;/TestCase>
+&lt;/MatchingTests>" ( contains: "fake test name" and contains: "fakeTestTag" )
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="SUCCEED counts as a test pass" tags="[messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="SUCCEED does not require an argument" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Scenario: BDD tests requiring Fixtures to provide commonly-accessed data or methods" tags="[bdd][fixtures]" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+    <Section name="Given: No operations precede me" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+        <Original>
+          before == 0
+        </Original>
+        <Expanded>
+          0 == 0
+        </Expanded>
+      </Expression>
+      <Section name="When: We get the count" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+        <Section name="Then: Subsequently values are higher" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
             <Original>
-              mocked.derefed[4]
+              after > before
             </Original>
             <Expanded>
-              true
+              1 > 0
             </Expanded>
           </Expression>
-          <OverallResults successes="6" failures="0" expectedFailures="0"/>
+          <OverallResults successes="1" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Section name="Short-circuited" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked, anyMatch
-            </Original>
-            <Expanded>
-              { 1, 2, 3, 4, 5 } any match matches undescribed predicate
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[0]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              !(mocked.derefed[1])
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              !(mocked.derefed[2])
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              !(mocked.derefed[3])
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              !(mocked.derefed[4])
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <OverallResults successes="6" failures="0" expectedFailures="0"/>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Scenario: Do that thing with the thing" tags="[Tags]" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+    <Section name="Given: This stuff exists" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+      <Section name="And given: And some assumption" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+        <Section name="When: I do this" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+          <Section name="Then: it should do this" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+            <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+              <Original>
+                itDoesThis()
+              </Original>
+              <Expanded>
+                true
+              </Expanded>
+            </Expression>
+            <Section name="And: do that" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+                <Original>
+                  itDoesThat()
+                </Original>
+                <Expanded>
+                  true
+                </Expanded>
+              </Expression>
+              <OverallResults successes="1" failures="0" expectedFailures="0"/>
+            </Section>
+            <OverallResults successes="2" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="2" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Usage of NoneMatch range matcher" tags="[matchers][quantifiers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-      <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            data, NoneMatch(SizeIs(6))
-          </Original>
-          <Expanded>
-            { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } none match has size == 6
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            data, !NoneMatch(Contains(0) &amp;&amp; Contains(1))
-          </Original>
-          <Expanded>
-            { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } not none match ( contains element 0 and contains element 1 )
-          </Expanded>
-        </Expression>
         <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Type requires ADL found begin and end" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            needs_adl, NoneMatch( Predicate&lt;int>( []( int elem ) { return elem > 6; } ) )
-          </Original>
-          <Expanded>
-            { 1, 2, 3, 4, 5 } none match matches undescribed predicate
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Section name="All are read" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked, noneMatch
-            </Original>
-            <Expanded>
-              { 1, 2, 3, 4, 5 } none match matches undescribed predicate
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[0]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[1]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[2]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[3]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[4]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <OverallResults successes="6" failures="0" expectedFailures="0"/>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Scenario: This is a really long scenario name to see how the list command deals with wrapping" tags="[anotherReallyLongTagNameButThisOneHasNoObviousWrapPointsSoShouldSplitWithinAWordUsingADashCharacter][long][lots][one very long tag name that should cause line wrapping writing out using the list command][tags][verbose][very long tags]" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+    <Section name="Given: A section name that is so long that it cannot fit in a single console width" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+      <Section name="When: The test headers are printed as part of the normal running of the scenario" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+        <Section name="Then: The, deliberately very long and overly verbose (you see what I did there?) section names must wrap, along with an indent" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+          <OverallResults successes="1" failures="0" expectedFailures="0"/>
         </Section>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Section name="Short-circuited" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked, !noneMatch
-            </Original>
-            <Expanded>
-              { 1, 2, 3, 4, 5 } not none match matches undescribed predicate
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              mocked.derefed[0]
-            </Original>
-            <Expanded>
-              true
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              !(mocked.derefed[1])
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-            <Original>
-              !(mocked.derefed[2])
-            </Original>
-            <Expanded>
-              !false
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Scenario: Vector resizing affects size and capacity" tags="[bdd][capacity][size][vector]" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+    <Section name="Given: an empty vector" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+        <Original>
+          v.size() == 0
+        </Original>
+        <Expanded>
+          0 == 0
+        </Expanded>
+      </Expression>
+      <Section name="When: it is made larger" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+        <Section name="Then: the size and capacity go up" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
             <Original>
-              !(mocked.derefed[3])
+              v.size() == 10
             </Original>
             <Expanded>
-              !false
+              10 == 10
             </Expanded>
           </Expression>
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
             <Original>
-              !(mocked.derefed[4])
+              v.capacity() >= 10
             </Original>
             <Expanded>
-              !false
+              10 >= 10
             </Expanded>
           </Expression>
-          <OverallResults successes="6" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Usage of the SizeIs range matcher" tags="[matchers][size][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-      <Section name="Some with stdlib containers" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            empty_vec, SizeIs(0)
-          </Original>
-          <Expanded>
-            {  } has size == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            empty_vec, !SizeIs(2)
-          </Original>
-          <Expanded>
-            {  } not has size == 2
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            empty_vec, SizeIs(Lt(2))
-          </Original>
-          <Expanded>
-            {  } size matches is less than 2
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            arr, SizeIs(2)
-          </Original>
-          <Expanded>
-            { 0, 0 } has size == 2
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            arr, SizeIs( Lt(3))
-          </Original>
-          <Expanded>
-            { 0, 0 } size matches is less than 3
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            arr, !SizeIs(!Lt(3))
-          </Original>
-          <Expanded>
-            { 0, 0 } not size matches not is less than 3
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            map, SizeIs(3)
-          </Original>
-          <Expanded>
-            { {?}, {?}, {?} } has size == 3
-          </Expanded>
-        </Expression>
-        <OverallResults successes="7" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Type requires ADL found size free function" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            unrelated::ADL_size{}, SizeIs(12)
-          </Original>
-          <Expanded>
-            {?} has size == 12
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+          <Section name="And when: it is made smaller again" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+            <Section name="Then: the size goes down but the capacity stays the same" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+                <Original>
+                  v.size() == 5
+                </Original>
+                <Expanded>
+                  5 == 5
+                </Expanded>
+              </Expression>
+              <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+                <Original>
+                  v.capacity() >= 10
+                </Original>
+                <Expanded>
+                  10 >= 10
+                </Expanded>
+              </Expression>
+              <OverallResults successes="2" failures="0" expectedFailures="0"/>
+            </Section>
+            <OverallResults successes="2" failures="0" expectedFailures="0"/>
+          </Section>
+          <OverallResults successes="4" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="4" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Type has size member" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
-          <Original>
-            has_size{}, SizeIs(13)
-          </Original>
-          <Expanded>
-            {?} has size == 13
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Given: an empty vector" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+        <Original>
+          v.size() == 0
+        </Original>
+        <Expanded>
+          0 == 0
+        </Expanded>
+      </Expression>
+      <Section name="When: we reserve more space" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+        <Section name="Then: The capacity is increased but the size remains the same" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+            <Original>
+              v.capacity() >= 10
+            </Original>
+            <Expanded>
+              10 >= 10
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/BDD.tests.cpp" >
+            <Original>
+              v.size() == 0
+            </Original>
+            <Expanded>
+              0 == 0
+            </Expanded>
+          </Expression>
+          <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Use a custom approx" tags="[Approx][custom]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Sends stuff to stdout and stderr" tags="[.]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="false">
+      <StdOut>
+A string sent directly to stdout
+      </StdOut>
+      <StdErr>
+A string sent directly to stderr
+A string sent to stderr via clog
+      </StdErr>
+    </OverallResult>
+  </TestCase>
+  <TestCase name="Some simple comparisons between doubles" tags="[Approx]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d == Approx( 1.23 )
+      </Original>
+      <Expanded>
+        1.23 == Approx( 1.23 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d != Approx( 1.22 )
+      </Original>
+      <Expanded>
+        1.23 != Approx( 1.22 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d != Approx( 1.24 )
+      </Original>
+      <Expanded>
+        1.23 != Approx( 1.24 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d == 1.23_a
+      </Original>
+      <Expanded>
+        1.23 == Approx( 1.23 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d != 1.22_a
+      </Original>
+      <Expanded>
+        1.23 != Approx( 1.22 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx( d ) == 1.23
+      </Original>
+      <Expanded>
+        Approx( 1.23 ) == 1.23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx( d ) != 1.22
+      </Original>
+      <Expanded>
+        Approx( 1.23 ) != 1.22
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        Approx( d ) != 1.24
+      </Original>
+      <Expanded>
+        Approx( 1.23 ) != 1.24
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Standard output from all sections is reported" tags="[.][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Section name="one" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <Section name="two" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="false">
+      <StdOut>
+Message from section one
+Message from section two
+      </StdOut>
+    </OverallResult>
+  </TestCase>
+  <TestCase name="StartsWith string matcher" tags="[.][failing][matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), StartsWith( "This String" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" starts with: "This String"
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), StartsWith( "string", Catch::CaseSensitive::No )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" starts with: "string" (case insensitive)
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Static arrays are convertible to string" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+    <Section name="Single item" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          d == approx( 1.23 )
+          Catch::Detail::stringify(singular) == "{ 1 }"
         </Original>
         <Expanded>
-          1.23 == Approx( 1.23 )
+          "{ 1 }" == "{ 1 }"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Multiple" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          d == approx( 1.22 )
+          Catch::Detail::stringify(arr) == "{ 3, 2, 1 }"
         </Original>
         <Expanded>
-          1.23 == Approx( 1.22 )
+          "{ 3, 2, 1 }" == "{ 3, 2, 1 }"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Non-trivial inner items" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          d == approx( 1.24 )
+          Catch::Detail::stringify(arr) == R"({ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } })"
+        </Original>
+        <Expanded>
+          "{ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } }"
+==
+"{ { "1:1", "1:2", "1:3" }, { "2:1", "2:2" } }"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="String matchers" tags="[matchers]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Contains( "string" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" contains: "string"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Contains( "string", Catch::CaseSensitive::No )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" contains: "string" (case insensitive)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Contains( "abc" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" contains: "abc"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), Contains( "aBC", Catch::CaseSensitive::No )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" contains: "abc" (case insensitive)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), StartsWith( "this" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" starts with: "this"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), StartsWith( "THIS", Catch::CaseSensitive::No )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" starts with: "this" (case insensitive)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), EndsWith( "substring" )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" ends with: "substring"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Original>
+        testStringForMatching(), EndsWith( " SuBsTrInG", Catch::CaseSensitive::No )
+      </Original>
+      <Expanded>
+        "this string contains 'abc' as a substring" ends with: " substring" (case insensitive)
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="StringRef" tags="[StringRef][Strings]" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+    <Section name="Empty string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Original>
+          empty.empty()
         </Original>
         <Expanded>
-          1.23 == Approx( 1.24 )
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
         <Original>
-          d != approx( 1.25 )
+          empty.size() == 0
         </Original>
         <Expanded>
-          1.23 != Approx( 1.25 )
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
         <Original>
-          approx( d ) == 1.23
+          std::strcmp( empty.data(), "" ) == 0
         </Original>
         <Expanded>
-          Approx( 1.23 ) == 1.23
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="From string literal" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
         <Original>
-          approx( d ) == 1.22
+          s.empty() == false
         </Original>
         <Expanded>
-          Approx( 1.23 ) == 1.22
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
         <Original>
-          approx( d ) == 1.24
+          s.size() == 5
         </Original>
         <Expanded>
-          Approx( 1.23 ) == 1.24
+          5 == 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
         <Original>
-          approx( d ) != 1.25
+          std::strcmp( rawChars, "hello" ) == 0
         </Original>
         <Expanded>
-          Approx( 1.23 ) != 1.25
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Variadic macros" tags="[sections][variadic]" filename="tests/<exe-name>/UsageTests/VariadicMacros.tests.cpp" >
-      <Section name="Section with one argument" filename="tests/<exe-name>/UsageTests/VariadicMacros.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Vector Approx matcher" tags="[approx][matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="Empty vector is roughly equal to an empty vector" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            empty, Approx(empty)
-          </Original>
-          <Expanded>
-            {  } is approx: {  }
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Vectors with elements" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Section name="A vector is approx equal to itself" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            <Original>
-              v1, Approx(v1)
-            </Original>
-            <Expanded>
-              { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            <Original>
-              v1, Approx&lt;double>({ 1., 2., 3. })
-            </Original>
-            <Expanded>
-              { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Vectors with elements" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Section name="Different length" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            <Original>
-              v1, !Approx(temp)
-            </Original>
-            <Expanded>
-              { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 }
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Vectors with elements" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Section name="Same length, different elements" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            <Original>
-              v1, !Approx(v2)
-            </Original>
-            <Expanded>
-              { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 }
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            <Original>
-              v1, Approx(v2).margin(0.5)
-            </Original>
-            <Expanded>
-              { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            <Original>
-              v1, Approx(v2).epsilon(0.5)
-            </Original>
-            <Expanded>
-              { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-            <Original>
-              v1, Approx(v2).epsilon(0.1).scale(500)
-            </Original>
-            <Expanded>
-              { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
-            </Expanded>
-          </Expression>
-          <OverallResults successes="4" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="4" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Vector Approx matcher -- failing" tags="[.][approx][failing][matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="Empty and non empty vectors are not approx equal" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            empty, Approx(t1)
-          </Original>
-          <Expanded>
-            {  } is approx: { 1.0, 2.0 }
-          </Expanded>
-        </Expression>
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <Section name="Just different vectors" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Original>
+          s.data() == rawChars
+        </Original>
+        <Expanded>
+          "hello" == "hello"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="From sub-string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Original>
+          original == "original"
+        </Original>
+        <Expanded>
+          original == "original"
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Original>
+          original.data()
+        </Original>
+        <Expanded>
+          original.data()
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="zero-based substring" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v1, Approx(v2)
+            ss.empty() == false
           </Original>
           <Expanded>
-            { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
+            false == false
           </Expanded>
         </Expression>
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Vector matchers" tags="[matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="Contains (element)" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v, VectorContains(1)
+            ss.size() == 5
           </Original>
           <Expanded>
-            { 1, 2, 3 } Contains: 1
+            5 == 5
           </Expanded>
         </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v, VectorContains(2)
+            std::strncmp( ss.data(), "hello", 5 ) == 0
           </Original>
           <Expanded>
-            { 1, 2, 3 } Contains: 2
+            0 == 0
           </Expanded>
         </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v5, (VectorContains&lt;int, CustomAllocator&lt;int>>(2))
+            ss == "hello"
           </Original>
           <Expanded>
-            { 1, 2, 3 } Contains: 2
+            hello == "hello"
           </Expanded>
         </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
+        <OverallResults successes="4" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Contains (vector)" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v, Contains(v2)
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } Contains: { 1, 2 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v, Contains&lt;int>({ 1, 2 })
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } Contains: { 1, 2 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v5, (Contains&lt;int, std::allocator&lt;int>, CustomAllocator&lt;int>>(v2))
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } Contains: { 1, 2 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v, Contains(v2)
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } Contains: { 1, 2, 3 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v, Contains(empty)
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } Contains: {  }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            empty, Contains(empty)
-          </Original>
-          <Expanded>
-            {  } Contains: {  }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="non-zero-based substring" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v5, (Contains&lt;int, std::allocator&lt;int>, CustomAllocator&lt;int>>(v2))
+            ss.size() == 6
           </Original>
           <Expanded>
-            { 1, 2, 3 } Contains: { 1, 2, 3 }
+            6 == 6
           </Expanded>
         </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v5, Contains(v6)
+            std::strcmp( ss.data(), "world!" ) == 0
           </Original>
           <Expanded>
-            { 1, 2, 3 } Contains: { 1, 2 }
+            0 == 0
           </Expanded>
         </Expression>
-        <OverallResults successes="8" failures="0" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Contains (element), composed" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="Pointer values of full refs should match" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v, VectorContains(1) &amp;&amp; VectorContains(2)
+            s.data() == s2.data()
           </Original>
           <Expanded>
-            { 1, 2, 3 } ( Contains: 1 and Contains: 2 )
+            "hello world!" == "hello world!"
           </Expanded>
         </Expression>
         <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Equals" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v, Equals(v)
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } Equals: { 1, 2, 3 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            empty, Equals(empty)
-          </Original>
-          <Expanded>
-            {  } Equals: {  }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v, Equals&lt;int>({ 1, 2, 3 })
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } Equals: { 1, 2, 3 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v, Equals(v2)
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } Equals: { 1, 2, 3 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v5, (Equals&lt;int, std::allocator&lt;int>, CustomAllocator&lt;int>>(v2))
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } Equals: { 1, 2, 3 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="Pointer values of substring refs should also match" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v5, Equals(v6)
+            s.data() == ss.data()
           </Original>
           <Expanded>
-            { 1, 2, 3 } Equals: { 1, 2, 3 }
+            "hello world!" == "hello world!"
           </Expanded>
         </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="UnorderedEquals" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v, UnorderedEquals(v)
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-          <Original>
-            v, UnorderedEquals&lt;int>({ 3, 2, 1 })
-          </Original>
-          <Expanded>
-            { 1, 2, 3 } UnorderedEquals: { 3, 2, 1 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="Past the end substring" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            empty, UnorderedEquals(empty)
+            s.substr(s.size() + 1, 123).empty()
           </Original>
           <Expanded>
-            {  } UnorderedEquals: {  }
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="Substring off the end are trimmed" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            permuted, UnorderedEquals(v)
+            std::strcmp(ss.data(), "world!") == 0
           </Original>
           <Expanded>
-            { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
+            0 == 0
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Substrings" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="substring start after the end is empty" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            permuted, UnorderedEquals(v)
+            s.substr(1'000'000, 1).empty()
           </Original>
           <Expanded>
-            { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Comparisons are deep" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Original>
+          reinterpret_cast&lt;char*>(buffer1) != reinterpret_cast&lt;char*>(buffer2)
+        </Original>
+        <Expanded>
+          "Hello" != "Hello"
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Original>
+          left == right
+        </Original>
+        <Expanded>
+          Hello == Hello
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Original>
+          left != left.substr(0, 3)
+        </Original>
+        <Expanded>
+          Hello != Hel
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="from std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="implicitly constructed" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v5, (UnorderedEquals&lt;int, std::allocator&lt;int>, CustomAllocator&lt;int>>(permuted))
+            sr == "a standard string"
           </Original>
           <Expanded>
-            { 1, 2, 3 } UnorderedEquals: { 2, 3, 1 }
+            a standard string == "a standard string"
           </Expanded>
         </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v5_permuted, UnorderedEquals(v5)
+            sr.size() == stdStr.size()
           </Original>
           <Expanded>
-            { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
+            17 == 17
           </Expanded>
         </Expression>
-        <OverallResults successes="7" failures="0" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="Vector matchers that fail" tags="[.][failing][matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-      <Section name="Contains (element)" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="from std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="explicitly constructed" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v, VectorContains(-1)
+            sr == "a standard string"
           </Original>
           <Expanded>
-            { 1, 2, 3 } Contains: -1
+            a standard string == "a standard string"
           </Expanded>
         </Expression>
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            empty, VectorContains(1)
+            sr.size() == stdStr.size()
           </Original>
           <Expanded>
-            {  } Contains: 1
+            17 == 17
           </Expanded>
         </Expression>
-        <OverallResults successes="0" failures="2" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Contains (vector)" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="from std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="assigned" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            empty, Contains(v)
+            sr == "a standard string"
           </Original>
           <Expanded>
-            {  } Contains: { 1, 2, 3 }
+            a standard string == "a standard string"
           </Expanded>
         </Expression>
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v, Contains(v2)
+            sr.size() == stdStr.size()
           </Original>
           <Expanded>
-            { 1, 2, 3 } Contains: { 1, 2, 4 }
+            17 == 17
           </Expanded>
         </Expression>
-        <OverallResults successes="0" failures="2" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Equals" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="to std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="explicitly constructed" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v, Equals(v2)
+            stdStr == "a stringref"
           </Original>
           <Expanded>
-            { 1, 2, 3 } Equals: { 1, 2 }
+            "a stringref" == "a stringref"
           </Expanded>
         </Expression>
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v2, Equals(v)
+            stdStr.size() == sr.size()
           </Original>
           <Expanded>
-            { 1, 2 } Equals: { 1, 2, 3 }
+            11 == 11
           </Expanded>
         </Expression>
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="to std::string" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Section name="assigned" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            empty, Equals(v)
+            stdStr == "a stringref"
           </Original>
           <Expanded>
-            {  } Equals: { 1, 2, 3 }
+            "a stringref" == "a stringref"
           </Expanded>
         </Expression>
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
           <Original>
-            v, Equals(empty)
+            stdStr.size() == sr.size()
           </Original>
           <Expanded>
-            { 1, 2, 3 } Equals: {  }
+            11 == 11
           </Expanded>
         </Expression>
-        <OverallResults successes="0" failures="4" expectedFailures="0"/>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="UnorderedEquals" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="std::string += StringRef" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Original>
+          lhs == "some string += the stringref contents"
+        </Original>
+        <Expanded>
+          "some string += the stringref contents"
+==
+"some string += the stringref contents"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="StringRef + StringRef" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+        <Original>
+          together == "abrakadabra"
+        </Original>
+        <Expanded>
+          "abrakadabra" == "abrakadabra"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="StringRef at compilation time" tags="[constexpr][StringRef][Strings]" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+    <Section name="Simple constructors" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <OverallResults successes="12" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="UDL construction" filename="tests/<exe-name>/IntrospectiveTests/String.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Stringifying char arrays with statically known sizes - char" tags="[toString]" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s
+      </Original>
+      <Expanded>
+        ""abc"" == ""abc""
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s
+      </Original>
+      <Expanded>
+        ""abc"" == ""abc""
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Stringifying char arrays with statically known sizes - signed char" tags="[toString]" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s
+      </Original>
+      <Expanded>
+        ""abc"" == ""abc""
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s
+      </Original>
+      <Expanded>
+        ""abc"" == ""abc""
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Stringifying char arrays with statically known sizes - unsigned char" tags="[toString]" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s
+      </Original>
+      <Expanded>
+        ""abc"" == ""abc""
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s
+      </Original>
+      <Expanded>
+        ""abc"" == ""abc""
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Stringifying std::chrono::duration helpers" tags="[chrono][toString]" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Original>
+        minute == seconds
+      </Original>
+      <Expanded>
+        1 m == 60 s
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Original>
+        hour != seconds
+      </Original>
+      <Expanded>
+        1 h != 60 s
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Original>
+        micro != milli
+      </Original>
+      <Expanded>
+        1 us != 1 ms
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Original>
+        nano != micro
+      </Original>
+      <Expanded>
+        1 ns != 1 us
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Stringifying std::chrono::duration with weird ratios" tags="[chrono][toString]" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Original>
+        half_minute != femto_second
+      </Original>
+      <Expanded>
+        1 [30/1]s != 1 fs
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Original>
+        pico_second != atto_second
+      </Original>
+      <Expanded>
+        1 ps != 1 as
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Stringifying std::chrono::time_point&lt;system_clock>" tags="[chrono][toString]" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringChrono.tests.cpp" >
+      <Original>
+        now != later
+      </Original>
+      <Expanded>
+        {iso8601-timestamp}
+!=
+{iso8601-timestamp}
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Tabs and newlines show in output" tags="[.][failing][whitespace]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        s1 == s2
+      </Original>
+      <Expanded>
+        "if ($b == 10) {
+		$a	= 20;
+}"
+==
+"if ($b == 10) {
+	$a = 20;
+}
+"
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Tag alias can be registered against tag patterns" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+    <Section name="The same tag alias can only be registered once" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+        <Original>
+          what, Contains( "[@zzz]" )
+        </Original>
+        <Expanded>
+          "error: tag alias, '[@zzz]' already registered.
+	First seen at: file:2
+	Redefined at: file:10" contains: "[@zzz]"
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+        <Original>
+          what, Contains( "file" )
+        </Original>
+        <Expanded>
+          "error: tag alias, '[@zzz]' already registered.
+	First seen at: file:2
+	Redefined at: file:10" contains: "file"
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+        <Original>
+          what, Contains( "2" )
+        </Original>
+        <Expanded>
+          "error: tag alias, '[@zzz]' already registered.
+	First seen at: file:2
+	Redefined at: file:10" contains: "2"
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+        <Original>
+          what, Contains( "10" )
+        </Original>
+        <Expanded>
+          "error: tag alias, '[@zzz]' already registered.
+	First seen at: file:2
+	Redefined at: file:10" contains: "10"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Tag aliases must be of the form [@name]" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+      <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+        <Original>
+          registry.add( "[no ampersat]", "", Catch::SourceLineInfo( "file", 3 ) )
+        </Original>
+        <Expanded>
+          registry.add( "[no ampersat]", "", Catch::SourceLineInfo( "file", 3 ) )
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+        <Original>
+          registry.add( "[the @ is not at the start]", "", Catch::SourceLineInfo( "file", 3 ) )
+        </Original>
+        <Expanded>
+          registry.add( "[the @ is not at the start]", "", Catch::SourceLineInfo( "file", 3 ) )
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+        <Original>
+          registry.add( "@no square bracket at start]", "", Catch::SourceLineInfo( "file", 3 ) )
+        </Original>
+        <Expanded>
+          registry.add( "@no square bracket at start]", "", Catch::SourceLineInfo( "file", 3 ) )
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+        <Original>
+          registry.add( "[@no square bracket at end", "", Catch::SourceLineInfo( "file", 3 ) )
+        </Original>
+        <Expanded>
+          registry.add( "[@no square bracket at end", "", Catch::SourceLineInfo( "file", 3 ) )
+        </Expanded>
+      </Expression>
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case method with test types specified inside std::tuple - MyTypes - 0" tags="[class][list][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture&lt;TestType>::m_a == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case method with test types specified inside std::tuple - MyTypes - 1" tags="[class][list][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture&lt;TestType>::m_a == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case method with test types specified inside std::tuple - MyTypes - 2" tags="[class][list][template]" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Class.tests.cpp" >
+      <Original>
+        Template_Fixture&lt;TestType>::m_a == 1
+      </Original>
+      <Expanded>
+        1.0 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case with test types specified inside non-copyable and non-movable std::tuple - NonCopyableAndNonMovableTypes - 0" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        sizeof(TestType) > 0
+      </Original>
+      <Expanded>
+        1 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case with test types specified inside non-copyable and non-movable std::tuple - NonCopyableAndNonMovableTypes - 1" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        sizeof(TestType) > 0
+      </Original>
+      <Expanded>
+        4 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case with test types specified inside non-default-constructible std::tuple - MyNonDefaultConstructibleTypes - 0" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        sizeof(TestType) > 0
+      </Original>
+      <Expanded>
+        1 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case with test types specified inside non-default-constructible std::tuple - MyNonDefaultConstructibleTypes - 1" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        sizeof(TestType) > 0
+      </Original>
+      <Expanded>
+        4 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case with test types specified inside std::tuple - MyTypes - 0" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        sizeof(TestType) > 0
+      </Original>
+      <Expanded>
+        4 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case with test types specified inside std::tuple - MyTypes - 1" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        sizeof(TestType) > 0
+      </Original>
+      <Expanded>
+        1 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Template test case with test types specified inside std::tuple - MyTypes - 2" tags="[list][template]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        sizeof(TestType) > 0
+      </Original>
+      <Expanded>
+        4 > 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="TemplateTest: vectors can be sized and resized - float" tags="[template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 10
+        </Original>
+        <Expanded>
+          10 == 10
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 0
+        </Original>
+        <Expanded>
+          0 == 0
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 5
+        </Original>
+        <Expanded>
+          5 >= 5
+        </Expanded>
+      </Expression>
+      <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
           <Original>
-            v, UnorderedEquals(empty)
+            v.capacity() == 0
           </Original>
           <Expanded>
-            { 1, 2, 3 } UnorderedEquals: {  }
+            0 == 0
           </Expanded>
         </Expression>
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 5
+        </Original>
+        <Expanded>
+          5 == 5
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 5
+        </Original>
+        <Expanded>
+          5 == 5
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 5
+        </Original>
+        <Expanded>
+          5 >= 5
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="TemplateTest: vectors can be sized and resized - int" tags="[template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 10
+        </Original>
+        <Expanded>
+          10 == 10
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 0
+        </Original>
+        <Expanded>
+          0 == 0
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 5
+        </Original>
+        <Expanded>
+          5 >= 5
+        </Expanded>
+      </Expression>
+      <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
           <Original>
-            empty, UnorderedEquals(v)
+            v.capacity() == 0
           </Original>
           <Expanded>
-            {  } UnorderedEquals: { 1, 2, 3 }
+            0 == 0
           </Expanded>
         </Expression>
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 5
+        </Original>
+        <Expanded>
+          5 == 5
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 5
+        </Original>
+        <Expanded>
+          5 == 5
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 5
+        </Original>
+        <Expanded>
+          5 >= 5
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="TemplateTest: vectors can be sized and resized - std::string" tags="[template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 10
+        </Original>
+        <Expanded>
+          10 == 10
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 0
+        </Original>
+        <Expanded>
+          0 == 0
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 5
+        </Original>
+        <Expanded>
+          5 >= 5
+        </Expanded>
+      </Expression>
+      <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
           <Original>
-            permuted, UnorderedEquals(v)
+            v.capacity() == 0
           </Original>
           <Expanded>
-            { 1, 3 } UnorderedEquals: { 1, 2, 3 }
+            0 == 0
           </Expanded>
         </Expression>
-        <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 5
+        </Original>
+        <Expanded>
+          5 == 5
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 5
+        </Original>
+        <Expanded>
+          5 == 5
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 5
+        </Original>
+        <Expanded>
+          5 >= 5
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="TemplateTest: vectors can be sized and resized - std::tuple&lt;int,float>" tags="[template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 10
+        </Original>
+        <Expanded>
+          10 == 10
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 0
+        </Original>
+        <Expanded>
+          0 == 0
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 5
+        </Original>
+        <Expanded>
+          5 >= 5
+        </Expanded>
+      </Expression>
+      <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
           <Original>
-            permuted, UnorderedEquals(v)
+            v.capacity() == 0
           </Original>
           <Expanded>
-            { 3, 1 } UnorderedEquals: { 1, 2, 3 }
+            0 == 0
           </Expanded>
         </Expression>
-        <OverallResults successes="0" failures="4" expectedFailures="0"/>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="When checked exceptions are thrown they can be expected or unexpected" tags="[!throws]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          thisThrows(), std::domain_error
+          v.size() == 5
         </Original>
         <Expanded>
-          thisThrows(), std::domain_error
+          5 == 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          thisDoesntThrow()
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 5
         </Original>
         <Expanded>
-          thisDoesntThrow()
+          5 == 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          thisThrows()
+          v.capacity() >= 5
         </Original>
         <Expanded>
-          thisThrows()
+          5 >= 5
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="When unchecked exceptions are thrown directly they are always failures" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        unexpected exception
-      </Exception>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="When unchecked exceptions are thrown during a CHECK the test should continue" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="TemplateTestSig: vectors can be sized and resized - (std::tuple&lt;int, float>), 6" tags="[nttp][template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        6 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        6 >= 6
+      </Expanded>
+    </Expression>
+    <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          thisThrows() == 0
+          v.size() == 2 * V
         </Original>
         <Expanded>
-          thisThrows() == 0
+          12 == 12
         </Expanded>
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          expected exception
-        </Exception>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="When unchecked exceptions are thrown during a REQUIRE the test should abort fail" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 2 * V
+        </Original>
+        <Expanded>
+          12 >= 12
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        6 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        6 >= 6
+      </Expanded>
+    </Expression>
+    <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          thisThrows() == 0
+          v.size() == 0
         </Original>
         <Expanded>
-          thisThrows() == 0
+          0 == 0
         </Expanded>
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          expected exception
-        </Exception>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="When unchecked exceptions are thrown from functions they are always failures" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          thisThrows() == 0
+          v.capacity() >= V
         </Original>
         <Expanded>
-          thisThrows() == 0
+          6 >= 6
         </Expanded>
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          expected exception
-        </Exception>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="When unchecked exceptions are thrown from sections they are always failures" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Section name="section name" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-          unexpected exception
-        </Exception>
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="When unchecked exceptions are thrown, but caught, they do not affect the test" tags="[!throws]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Where the LHS is not a simple value" tags="[.][failing][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Warning>
-        Uncomment the code in this test to check that it gives a sensible compiler error
-      </Warning>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="Where there is more to the expression after the RHS" tags="[.][failing][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Warning>
-        Uncomment the code in this test to check that it gives a sensible compiler error
-      </Warning>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="X/level/0/a" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="X/level/0/b" tags="[fizz][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="X/level/1/a" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="X/level/1/b" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="XmlEncode" tags="[XML]" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-      <Section name="normal string" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-          <Original>
-            encode( "normal string" ) == "normal string"
-          </Original>
-          <Expanded>
-            "normal string" == "normal string"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="empty string" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-          <Original>
-            encode( "" ) == ""
-          </Original>
-          <Expanded>
-            "" == ""
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="string with ampersand" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-          <Original>
-            encode( "smith &amp; jones" ) == "smith &amp;amp; jones"
-          </Original>
-          <Expanded>
-            "smith &amp;amp; jones" == "smith &amp;amp; jones"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="string with less-than" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-          <Original>
-            encode( "smith &lt; jones" ) == "smith &amp;lt; jones"
-          </Original>
-          <Expanded>
-            "smith &amp;lt; jones" == "smith &amp;lt; jones"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="string with greater-than" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-          <Original>
-            encode( "smith > jones" ) == "smith > jones"
-          </Original>
-          <Expanded>
-            "smith > jones" == "smith > jones"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-          <Original>
-            encode( "smith ]]&gt; jones" ) == "smith ]]&amp;gt; jones"
-          </Original>
-          <Expanded>
-            "smith ]]&amp;gt; jones"
-==
-"smith ]]&amp;gt; jones"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="string with quotes" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-          <Original>
-            encode( stringWithQuotes ) == stringWithQuotes
-          </Original>
-          <Expanded>
-            "don't "quote" me on that"
-==
-"don't "quote" me on that"
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-          <Original>
-            encode( stringWithQuotes, Catch::XmlEncode::ForAttributes ) == "don't &amp;quot;quote&amp;quot; me on that"
-          </Original>
-          <Expanded>
-            "don't &amp;quot;quote&amp;quot; me on that"
-==
-"don't &amp;quot;quote&amp;quot; me on that"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="string with control char (1)" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-          <Original>
-            encode( "[\x01]" ) == "[\\x01]"
-          </Original>
-          <Expanded>
-            "[\x01]" == "[\x01]"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="string with control char (x7F)" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
           <Original>
-            encode( "[\x7F]" ) == "[\\x7F]"
+            v.capacity() == 0
           </Original>
           <Expanded>
-            "[\x7F]" == "[\x7F]"
+            0 == 0
           </Expanded>
         </Expression>
         <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="analyse no analysis" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        6 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        6 >= 6
+      </Expanded>
+    </Expression>
+    <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.mean.point.count() == 23
+          v.size() == V
         </Original>
         <Expanded>
-          23.0 == 23
+          6 == 6
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 2 * V
+        </Original>
+        <Expanded>
+          12 >= 12
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        6 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        6 >= 6
+      </Expanded>
+    </Expression>
+    <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.mean.lower_bound.count() == 23
+          v.size() == V
         </Original>
         <Expanded>
-          23.0 == 23
+          6 == 6
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.mean.upper_bound.count() == 23
+          v.capacity() >= V
         </Original>
         <Expanded>
-          23.0 == 23
+          6 >= 6
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="TemplateTestSig: vectors can be sized and resized - float,4" tags="[nttp][template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        4 == 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        4 >= 4
+      </Expanded>
+    </Expression>
+    <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.standard_deviation.point.count() == 0
+          v.size() == 2 * V
         </Original>
         <Expanded>
-          0.0 == 0
+          8 == 8
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.standard_deviation.lower_bound.count() == 0
+          v.capacity() >= 2 * V
+        </Original>
+        <Expanded>
+          8 >= 8
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        4 == 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        4 >= 4
+      </Expanded>
+    </Expression>
+    <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 0
         </Original>
         <Expanded>
-          0.0 == 0
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.standard_deviation.upper_bound.count() == 0
+          v.capacity() >= V
         </Original>
         <Expanded>
-          0.0 == 0
+          4 >= 4
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+          <Original>
+            v.capacity() == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        4 == 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        4 >= 4
+      </Expanded>
+    </Expression>
+    <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.outliers.total() == 0
+          v.size() == V
         </Original>
         <Expanded>
-          0 == 0
+          4 == 4
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 2 * V
+        </Original>
+        <Expanded>
+          8 >= 8
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        4 == 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        4 >= 4
+      </Expanded>
+    </Expression>
+    <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.outliers.low_mild == 0
+          v.size() == V
         </Original>
         <Expanded>
-          0 == 0
+          4 == 4
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.outliers.low_severe == 0
+          v.capacity() >= V
         </Original>
         <Expanded>
-          0 == 0
+          4 >= 4
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="TemplateTestSig: vectors can be sized and resized - int,5" tags="[nttp][template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.outliers.high_mild == 0
+          v.size() == 2 * V
         </Original>
         <Expanded>
-          0 == 0
+          10 == 10
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= 2 * V
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.outliers.high_severe == 0
+          v.size() == 0
         </Original>
         <Expanded>
           0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.outliers.samples_seen == 0
+          v.capacity() >= V
         </Original>
         <Expanded>
-          0 == 0
+          5 >= 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+          <Original>
+            v.capacity() == 0
+          </Original>
+          <Expanded>
+            0 == 0
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          analysis.outlier_variance == 0
+          v.size() == V
         </Original>
         <Expanded>
-          0.0 == 0
+          5 == 5
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="array&lt;int, N> -> toString" tags="[array][containers][toString]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          Catch::Detail::stringify( empty ) == "{  }"
+          v.capacity() >= 2 * V
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == V
         </Original>
         <Expanded>
-          "{  }" == "{  }"
+          5 == 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          Catch::Detail::stringify( oneValue ) == "{ 42 }"
+          v.capacity() >= V
         </Original>
         <Expanded>
-          "{ 42 }" == "{ 42 }"
+          5 >= 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="TemplateTestSig: vectors can be sized and resized - std::string,15" tags="[nttp][template][vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        15 == 15
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        15 >= 15
+      </Expanded>
+    </Expression>
+    <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          Catch::Detail::stringify( twoValues ) == "{ 42, 250 }"
+          v.size() == 2 * V
         </Original>
         <Expanded>
-          "{ 42, 250 }" == "{ 42, 250 }"
+          30 == 30
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="atomic if" tags="[0][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
       <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          x == 0
+          v.capacity() >= 2 * V
+        </Original>
+        <Expanded>
+          30 >= 30
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        15 == 15
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        15 >= 15
+      </Expanded>
+    </Expression>
+    <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 0
         </Original>
         <Expanded>
           0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="benchmark function call" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Section name="without chronometer" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            model.started == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            model.finished == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            model.started == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            model.finished == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            called == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="with chronometer" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            model.started == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            model.finished == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            model.started == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.capacity() >= V
+        </Original>
+        <Expanded>
+          15 >= 15
+        </Expanded>
+      </Expression>
+      <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
           <Original>
-            model.finished == 0
+            v.capacity() == 0
           </Original>
           <Expanded>
             0 == 0
           </Expanded>
         </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            called == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="boolean member" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        15 == 15
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        15 >= 15
+      </Expanded>
+    </Expression>
+    <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == V
+        </Original>
+        <Expanded>
+          15 == 15
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          obj.prop != 0
+          v.capacity() >= 2 * V
+        </Original>
+        <Expanded>
+          30 >= 30
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == V
+      </Original>
+      <Expanded>
+        15 == 15
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= V
+      </Original>
+      <Expanded>
+        15 >= 15
+      </Expanded>
+    </Expression>
+    <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == V
         </Original>
         <Expanded>
-          0x<hex digits> != 0
+          15 == 15
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="checkedElse" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          flag
+          v.capacity() >= V
         </Original>
         <Expanded>
-          true
+          15 >= 15
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Test case with one argument" filename="tests/<exe-name>/UsageTests/VariadicMacros.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Test enum bit values" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        0x<hex digits> == bit30and31
+      </Original>
+      <Expanded>
+        3221225472 (0x<hex digits>) == 3221225472
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Test with special, characters &quot;in name" tags="[cli][regression]" filename="tests/<exe-name>/IntrospectiveTests/CmdLine.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Testing checked-if" tags="[checked-if]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="CHECKED_IF" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECKED_IF" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Testing checked-if 2" tags="[!shouldfail][checked-if]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="CHECKED_IF" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Failure filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" />
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Testing checked-if 3" tags="[!shouldfail][checked-if]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="false" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <Failure filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" />
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="The NO_FAIL macro reports a failure but does not fail the test" tags="[messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Expression success="false" type="CHECK_NOFAIL" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        1 == 2
+      </Original>
+      <Expanded>
+        1 == 2
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="The default listing implementation write to provided stream" tags="[reporter-helpers][reporters]" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+    <Section name="Listing tags" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("[fakeTag]"s)
+        </Original>
+        <Expanded>
+          "All available tags:
+   1  [fakeTag]
+1 tag
+
+" contains: "[fakeTag]"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Listing reporters" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains("fake reporter"s)
+        </Original>
+        <Expanded>
+          "Available reporters:
+  fake reporter:  fake description
+
+" contains: "fake reporter"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Listing tests" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Reporters.tests.cpp" >
+        <Original>
+          listingString, Contains( "fake test name"s ) &amp;&amp; Contains( "fakeTestTag"s )
+        </Original>
+        <Expanded>
+          "All available test cases:
+  fake test name
+      [fakeTestTag]
+1 test case
+
+" ( contains: "fake test name" and contains: "fakeTestTag" )
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="This test 'should' fail but doesn't" tags="[!shouldfail][.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Thrown string literals are translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      For some reason someone is throwing a string literal!
+    </Exception>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Tracker" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        testCase.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        s1.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Section name="successfully close one section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          testCheckedElse( true )
+          s1.isSuccessfullyCompleted()
         </Original>
         <Expanded>
           true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="checkedElse, failing" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="false" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          flag
+          testCase.isComplete() == false
         </Original>
         <Expanded>
-          false
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          testCheckedElse( false )
+          ctx.completedCycle()
         </Original>
         <Expanded>
-          false
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="checkedIf" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="CHECKED_IF" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          flag
+          testCase.isSuccessfullyCompleted()
         </Original>
         <Expanded>
           true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        testCase.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        s1.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Section name="fail one section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          testCheckedIf( true )
+          s1.isComplete()
         </Original>
         <Expanded>
           true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="checkedIf, failing" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="false" type="CHECKED_IF" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          flag
+          s1.isSuccessfullyCompleted() == false
         </Original>
         <Expanded>
-          false
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          testCheckedIf( false )
+          testCase.isComplete() == false
         </Original>
         <Expanded>
-          false
+          false == false
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="classify_outliers" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Section name="none" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.samples_seen == static_cast&lt;int>(x.size())
-          </Original>
-          <Expanded>
-            6 == 6
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.low_severe == los
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.low_mild == lom
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.high_mild == him
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.high_severe == his
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.total() == los + lom + him + his
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="low severe" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.samples_seen == static_cast&lt;int>(x.size())
-          </Original>
-          <Expanded>
-            6 == 6
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.low_severe == los
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.low_mild == lom
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.high_mild == him
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.high_severe == his
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.total() == los + lom + him + his
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="low mild" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.samples_seen == static_cast&lt;int>(x.size())
-          </Original>
-          <Expanded>
-            6 == 6
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          ctx.completedCycle()
+        </Original>
+        <Expanded>
+          true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          testCase.isSuccessfullyCompleted() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Section name="re-enter after failed section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.low_severe == los
+            testCase2.isOpen()
           </Original>
           <Expanded>
-            0 == 0
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.low_mild == lom
+            s1b.isOpen() == false
           </Original>
           <Expanded>
-            1 == 1
+            false == false
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.high_mild == him
+            ctx.completedCycle()
           </Original>
           <Expanded>
-            0 == 0
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.high_severe == his
+            testCase.isComplete()
           </Original>
           <Expanded>
-            0 == 0
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.total() == los + lom + him + his
+            testCase.isSuccessfullyCompleted()
           </Original>
           <Expanded>
-            1 == 1
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
+        <OverallResults successes="5" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="high mild" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="10" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        testCase.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        s1.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Section name="fail one section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          s1.isComplete()
+        </Original>
+        <Expanded>
+          true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          s1.isSuccessfullyCompleted() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          testCase.isComplete() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          ctx.completedCycle()
+        </Original>
+        <Expanded>
+          true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          testCase.isSuccessfullyCompleted() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Section name="re-enter after failed section and find next section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.samples_seen == static_cast&lt;int>(x.size())
+            testCase2.isOpen()
           </Original>
           <Expanded>
-            6 == 6
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.low_severe == los
+            s1b.isOpen() == false
           </Original>
           <Expanded>
-            0 == 0
+            false == false
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.low_mild == lom
+            s2.isOpen()
           </Original>
           <Expanded>
-            0 == 0
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.high_mild == him
+            ctx.completedCycle()
           </Original>
           <Expanded>
-            1 == 1
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.high_severe == his
+            testCase.isComplete()
           </Original>
           <Expanded>
-            0 == 0
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.total() == los + lom + him + his
+            testCase.isSuccessfullyCompleted()
           </Original>
           <Expanded>
-            1 == 1
+            true
           </Expanded>
         </Expression>
         <OverallResults successes="6" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="high severe" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.samples_seen == static_cast&lt;int>(x.size())
-          </Original>
-          <Expanded>
-            6 == 6
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.low_severe == los
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.low_mild == lom
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-          <Original>
-            o.high_mild == him
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="11" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        testCase.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        s1.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Section name="successfully close one section, then find another" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          s2.isOpen() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          testCase.isComplete() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Section name="Re-enter - skips S1 and enters S2" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.high_severe == his
+            testCase2.isOpen()
           </Original>
           <Expanded>
-            1 == 1
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.total() == los + lom + him + his
+            s1b.isOpen() == false
           </Original>
           <Expanded>
-            1 == 1
+            false == false
           </Expanded>
         </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="mixed" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.samples_seen == static_cast&lt;int>(x.size())
+            s2b.isOpen()
           </Original>
           <Expanded>
-            6 == 6
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.low_severe == los
+            ctx.completedCycle() == false
           </Original>
           <Expanded>
-            1 == 1
+            false == false
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Section name="Successfully close S2" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              ctx.completedCycle()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              s2b.isSuccessfullyCompleted()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              testCase2.isComplete() == false
+            </Original>
+            <Expanded>
+              false == false
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              testCase2.isSuccessfullyCompleted()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <OverallResults successes="4" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="8" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="10" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        testCase.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        s1.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Section name="successfully close one section, then find another" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          s2.isOpen() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Original>
+          testCase.isComplete() == false
+        </Original>
+        <Expanded>
+          false == false
+        </Expanded>
+      </Expression>
+      <Section name="Re-enter - skips S1 and enters S2" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.low_mild == lom
+            testCase2.isOpen()
           </Original>
           <Expanded>
-            0 == 0
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.high_mild == him
+            s1b.isOpen() == false
           </Original>
           <Expanded>
-            1 == 1
+            false == false
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.high_severe == his
+            s2b.isOpen()
           </Original>
           <Expanded>
-            0 == 0
+            true
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
           <Original>
-            o.total() == los + lom + him + his
+            ctx.completedCycle() == false
           </Original>
           <Expanded>
-            2 == 2
+            false == false
           </Expanded>
         </Expression>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
+        <Section name="fail S2" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              ctx.completedCycle()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              s2b.isComplete()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              s2b.isSuccessfullyCompleted() == false
+            </Original>
+            <Expanded>
+              false == false
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              testCase2.isSuccessfullyCompleted() == false
+            </Original>
+            <Expanded>
+              false == false
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              testCase3.isOpen()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              s1c.isOpen() == false
+            </Original>
+            <Expanded>
+              false == false
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              s2c.isOpen() == false
+            </Original>
+            <Expanded>
+              false == false
+            </Expanded>
+          </Expression>
+          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+            <Original>
+              testCase3.isSuccessfullyCompleted()
+            </Original>
+            <Expanded>
+              true
+            </Expanded>
+          </Expression>
+          <OverallResults successes="8" failures="0" expectedFailures="0"/>
+        </Section>
+        <OverallResults successes="12" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="comparisons between const int variables" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="14" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        testCase.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Original>
+        s1.isOpen()
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Section name="open a nested section" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          unsigned_char_var == 1
+          s2.isOpen()
         </Original>
         <Expanded>
-          1 == 1
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          unsigned_short_var == 1
+          s2.isComplete()
         </Original>
         <Expanded>
-          1 == 1
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          unsigned_int_var == 1
+          s1.isComplete() == false
         </Original>
         <Expanded>
-          1 == 1
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          unsigned_long_var == 1
+          s1.isComplete()
         </Original>
         <Expanded>
-          1 == 1
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="comparisons between int variables" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          long_var == unsigned_char_var
+          testCase.isComplete() == false
         </Original>
         <Expanded>
-          1 == 1
+          false == false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/PartTracker.tests.cpp" >
         <Original>
-          long_var == unsigned_short_var
+          testCase.isComplete()
         </Original>
         <Expanded>
-          1 == 1
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
-        <Original>
-          long_var == unsigned_int_var
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Trim strings" tags="[string-manip]" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        trim(std::string(no_whitespace)) == no_whitespace
+      </Original>
+      <Expanded>
+        "There is no extra whitespace here"
+==
+"There is no extra whitespace here"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        trim(std::string(leading_whitespace)) == no_whitespace
+      </Original>
+      <Expanded>
+        "There is no extra whitespace here"
+==
+"There is no extra whitespace here"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        trim(std::string(trailing_whitespace)) == no_whitespace
+      </Original>
+      <Expanded>
+        "There is no extra whitespace here"
+==
+"There is no extra whitespace here"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        trim(std::string(whitespace_at_both_ends)) == no_whitespace
+      </Original>
+      <Expanded>
+        "There is no extra whitespace here"
+==
+"There is no extra whitespace here"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        trim(StringRef(no_whitespace)) == StringRef(no_whitespace)
+      </Original>
+      <Expanded>
+        There is no extra whitespace here
+==
+There is no extra whitespace here
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        trim(StringRef(leading_whitespace)) == StringRef(no_whitespace)
+      </Original>
+      <Expanded>
+        There is no extra whitespace here
+==
+There is no extra whitespace here
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        trim(StringRef(trailing_whitespace)) == StringRef(no_whitespace)
+      </Original>
+      <Expanded>
+        There is no extra whitespace here
+==
+There is no extra whitespace here
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        trim(StringRef(whitespace_at_both_ends)) == StringRef(no_whitespace)
+      </Original>
+      <Expanded>
+        There is no extra whitespace here
+==
+There is no extra whitespace here
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Unexpected exceptions can be translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      3.14
+    </Exception>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Upcasting special member functions" tags="[internals][unique-ptr]" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+    <Section name="Move constructor" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          bptr->i == 3
         </Original>
         <Expanded>
-          1 == 1
+          3 == 3
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="move assignment" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
         <Original>
-          long_var == unsigned_long_var
+          bptr->i == 3
         </Original>
         <Expanded>
-          1 == 1
+          3 == 3
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="erfc_inv" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Usage of AllMatch range matcher" tags="[matchers][quantifiers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+    <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          erfc_inv(1.103560) == Approx(-0.09203687623843015)
+          data, AllMatch(SizeIs(5))
         </Original>
         <Expanded>
-          -0.0920368762 == Approx( -0.0920368762 )
+          { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } all match has size == 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          erfc_inv(1.067400) == Approx(-0.05980291115763361)
+          data, !AllMatch(Contains(0) &amp;&amp; Contains(1))
         </Original>
         <Expanded>
-          -0.0598029112 == Approx( -0.0598029112 )
+          { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } not all match ( contains element 0 and contains element 1 )
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Type requires ADL found begin and end" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          erfc_inv(0.050000) == Approx(1.38590382434967796)
+          needs_adl, AllMatch( Predicate&lt;int>( []( int elem ) { return elem &lt; 6; } ) )
         </Original>
         <Expanded>
-          1.3859038243 == Approx( 1.3859038243 )
+          { 1, 2, 3, 4, 5 } all match matches undescribed predicate
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="estimate_clock_resolution" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Section name="All are read" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked, allMatch
+          </Original>
+          <Expanded>
+            { 1, 2, 3, 4, 5 } all match matches undescribed predicate
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[0]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[1]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[2]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[3]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[4]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <OverallResults successes="6" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Section name="Short-circuited" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked, !allMatch
+          </Original>
+          <Expanded>
+            { 1, 2, 3, 4, 5 } not all match matches undescribed predicate
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[0]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[1]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[2]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            !(mocked.derefed[3])
+          </Original>
+          <Expanded>
+            !false
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            !(mocked.derefed[4])
+          </Original>
+          <Expanded>
+            !false
+          </Expanded>
+        </Expression>
+        <OverallResults successes="6" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Usage of AnyMatch range matcher" tags="[matchers][quantifiers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+    <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          res.mean.count() == rate
+          data, AnyMatch(SizeIs(5))
         </Original>
         <Expanded>
-          2000.0 == 2000 (0x<hex digits>)
+          { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } any match has size == 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          res.outliers.total() == 0
+          data, !AnyMatch(Contains(0) &amp;&amp; Contains(10))
         </Original>
         <Expanded>
-          0 == 0
+          { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } not any match ( contains element 0 and contains element 10 )
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="even more nested SECTION tests" tags="[sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Section name="c" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Section name="d (leaf)" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="c" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Section name="e (leaf)" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="f (leaf)" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="first tag" tags="[tag1]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="has printf" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-loose text artifact
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="just failure" tags="[.][fail][isolated info][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        Previous info should not be seen
-      </Failure>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="just failure after unscoped info" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-        previous unscoped info SHOULD not be seen
-      </Failure>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="just info" tags="[info][isolated info][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="just unscoped info" tags="[info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="long long" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Type requires ADL found begin and end" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          l == std::numeric_limits&lt;long long>::max()
+          needs_adl, AnyMatch( Predicate&lt;int>( []( int elem ) { return elem &lt; 3; } ) )
         </Original>
         <Expanded>
-          9223372036854775807 (0x<hex digits>)
-==
-9223372036854775807 (0x<hex digits>)
+          { 1, 2, 3, 4, 5 } any match matches undescribed predicate
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="looped SECTION tests" tags="[.][failing][sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Section name="b is currently: 0" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Section name="All are read" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            mocked, !anyMatch
           </Original>
           <Expanded>
-            0 > 1
+            { 1, 2, 3, 4, 5 } not any match matches undescribed predicate
           </Expanded>
         </Expression>
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <Section name="b is currently: 1" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            mocked.derefed[0]
           </Original>
           <Expanded>
-            1 > 1
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <Section name="b is currently: 2" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            mocked.derefed[1]
           </Original>
           <Expanded>
-            2 > 1
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="b is currently: 3" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[2]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            mocked.derefed[3]
           </Original>
           <Expanded>
-            3 > 1
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="b is currently: 4" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            mocked.derefed[4]
           </Original>
           <Expanded>
-            4 > 1
+            true
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+        <OverallResults successes="6" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="b is currently: 5" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Section name="Short-circuited" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            mocked, anyMatch
           </Original>
           <Expanded>
-            5 > 1
+            { 1, 2, 3, 4, 5 } any match matches undescribed predicate
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="b is currently: 6" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[0]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            !(mocked.derefed[1])
           </Original>
           <Expanded>
-            6 > 1
+            !false
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="b is currently: 7" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            !(mocked.derefed[2])
           </Original>
           <Expanded>
-            7 > 1
+            !false
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="b is currently: 8" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            !(mocked.derefed[3])
           </Original>
           <Expanded>
-            8 > 1
+            !false
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="b is currently: 9" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            b > a
+            !(mocked.derefed[4])
           </Original>
           <Expanded>
-            9 > 1
+            !false
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+        <OverallResults successes="6" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="looped tests" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Info>
-        Testing if fib[0] (1) is even
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          ( fib[i] % 2 ) == 0
-        </Original>
-        <Expanded>
-          1 == 0
-        </Expanded>
-      </Expression>
-      <Info>
-        Testing if fib[1] (1) is even
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          ( fib[i] % 2 ) == 0
-        </Original>
-        <Expanded>
-          1 == 0
-        </Expanded>
-      </Expression>
-      <Info>
-        Testing if fib[2] (2) is even
-      </Info>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          ( fib[i] % 2 ) == 0
-        </Original>
-        <Expanded>
-          0 == 0
-        </Expanded>
-      </Expression>
-      <Info>
-        Testing if fib[3] (3) is even
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          ( fib[i] % 2 ) == 0
-        </Original>
-        <Expanded>
-          1 == 0
-        </Expanded>
-      </Expression>
-      <Info>
-        Testing if fib[4] (5) is even
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Original>
-          ( fib[i] % 2 ) == 0
-        </Original>
-        <Expanded>
-          1 == 0
-        </Expanded>
-      </Expression>
-      <Info>
-        Testing if fib[5] (8) is even
-      </Info>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Usage of NoneMatch range matcher" tags="[matchers][quantifiers][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+    <Section name="Basic usage" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          ( fib[i] % 2 ) == 0
+          data, NoneMatch(SizeIs(6))
         </Original>
         <Expanded>
-          0 == 0
+          { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } none match has size == 6
         </Expanded>
       </Expression>
-      <Info>
-        Testing if fib[6] (13) is even
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          ( fib[i] % 2 ) == 0
+          data, !NoneMatch(Contains(0) &amp;&amp; Contains(1))
         </Original>
         <Expanded>
-          1 == 0
+          { { 0, 1, 2, 3, 5 }, { 4, -3, -2, 5, 0 }, { 0, 0, 0, 5, 0 }, { 0, -5, 0, 5, 0 }, { 1, 0, 0, -1, 5 } } not none match ( contains element 0 and contains element 1 )
         </Expanded>
       </Expression>
-      <Info>
-        Testing if fib[7] (21) is even
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Type requires ADL found begin and end" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          ( fib[i] % 2 ) == 0
+          needs_adl, NoneMatch( Predicate&lt;int>( []( int elem ) { return elem > 6; } ) )
         </Original>
         <Expanded>
-          1 == 0
+          { 1, 2, 3, 4, 5 } none match matches undescribed predicate
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="make_unique reimplementation" tags="[internals][unique-ptr]" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-      <Section name="From lvalue copies" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Section name="All are read" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            !(lval.has_moved)
+            mocked, noneMatch
           </Original>
           <Expanded>
-            !false
+            { 1, 2, 3, 4, 5 } none match matches undescribed predicate
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="From rvalue moves" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            rval.has_moved
+            mocked.derefed[0]
           </Original>
           <Expanded>
             true
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[1]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[2]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[3]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[4]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <OverallResults successes="6" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="Variadic constructor" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Shortcircuiting" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Section name="Short-circuited" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
           <Original>
-            *ptr == std::tuple&lt;int, double, int>{1, 2., 3}
+            mocked, !noneMatch
           </Original>
           <Expanded>
-            {?} == {?}
+            { 1, 2, 3, 4, 5 } not none match matches undescribed predicate
           </Expanded>
         </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            mocked.derefed[0]
+          </Original>
+          <Expanded>
+            true
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            !(mocked.derefed[1])
+          </Original>
+          <Expanded>
+            !false
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            !(mocked.derefed[2])
+          </Original>
+          <Expanded>
+            !false
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            !(mocked.derefed[3])
+          </Original>
+          <Expanded>
+            !false
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+          <Original>
+            !(mocked.derefed[4])
+          </Original>
+          <Expanded>
+            !false
+          </Expanded>
+        </Expression>
+        <OverallResults successes="6" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="mean" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Usage of the SizeIs range matcher" tags="[matchers][size][templated]" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+    <Section name="Some with stdlib containers" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          m == 19.
+          empty_vec, SizeIs(0)
         </Original>
         <Expanded>
-          19.0 == 19.0
+          {  } has size == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="measure" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          x == 17
+          empty_vec, !SizeIs(2)
         </Original>
         <Expanded>
-          17 == 17
+          {  } not has size == 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          x == 23
+          empty_vec, SizeIs(Lt(2))
         </Original>
         <Expanded>
-          23 == 23
+          {  } size matches is less than 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          r.elapsed.count() == 42
+          arr, SizeIs(2)
         </Original>
         <Expanded>
-          42 == 42
+          { 0, 0 } has size == 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          r.result == 23
+          arr, SizeIs( Lt(3))
         </Original>
         <Expanded>
-          23 == 23
+          { 0, 0 } size matches is less than 3
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          r.iterations == 1
+          arr, !SizeIs(!Lt(3))
         </Original>
         <Expanded>
-          1 == 1
+          { 0, 0 } not size matches not is less than 3
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          s.elapsed.count() == 69
+          map, SizeIs(3)
         </Original>
         <Expanded>
-          69 == 69
+          { {?}, {?}, {?} } has size == 3
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="7" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Type requires ADL found size free function" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          s.result == 17
+          unrelated::ADL_size{}, SizeIs(12)
         </Original>
         <Expanded>
-          17 == 17
+          {?} has size == 12
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Type has size member" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/MatchersRanges.tests.cpp" >
         <Original>
-          s.iterations == 1
+          has_size{}, SizeIs(13)
         </Original>
         <Expanded>
-          1 == 1
+          {?} has size == 13
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="mix info, unscoped info and warning" tags="[info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        info
-      </Info>
-      <Info>
-        unscoped info
-      </Info>
-      <Warning>
-        and warn may mix
-      </Warning>
-      <Info>
-        info
-      </Info>
-      <Info>
-        unscoped info
-      </Info>
-      <Warning>
-        they are not cleared after warnings
-      </Warning>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="more nested SECTION tests" tags="[.][failing][sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Section name="equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              a == b
-            </Original>
-            <Expanded>
-              1 == 2
-            </Expanded>
-          </Expression>
-          <OverallResults successes="0" failures="1" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="0" failures="1" expectedFailures="0"/>
-      </Section>
-      <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Section name="not equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              a != b
-            </Original>
-            <Expanded>
-              1 != 2
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Use a custom approx" tags="[Approx][custom]" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d == approx( 1.23 )
+      </Original>
+      <Expanded>
+        1.23 == Approx( 1.23 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d == approx( 1.22 )
+      </Original>
+      <Expanded>
+        1.23 == Approx( 1.22 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d == approx( 1.24 )
+      </Original>
+      <Expanded>
+        1.23 == Approx( 1.24 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        d != approx( 1.25 )
+      </Original>
+      <Expanded>
+        1.23 != Approx( 1.25 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        approx( d ) == 1.23
+      </Original>
+      <Expanded>
+        Approx( 1.23 ) == 1.23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        approx( d ) == 1.22
+      </Original>
+      <Expanded>
+        Approx( 1.23 ) == 1.22
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        approx( d ) == 1.24
+      </Original>
+      <Expanded>
+        Approx( 1.23 ) == 1.24
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Approx.tests.cpp" >
+      <Original>
+        approx( d ) != 1.25
+      </Original>
+      <Expanded>
+        Approx( 1.23 ) != 1.25
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Variadic macros" tags="[sections][variadic]" filename="tests/<exe-name>/UsageTests/VariadicMacros.tests.cpp" >
+    <Section name="Section with one argument" filename="tests/<exe-name>/UsageTests/VariadicMacros.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Vector Approx matcher" tags="[approx][matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="Empty vector is roughly equal to an empty vector" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Original>
+          empty, Approx( empty )
+        </Original>
+        <Expanded>
+          {  } is approx: {  }
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Vectors with elements" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Section name="A vector is approx equal to itself" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+          <Original>
+            v1, Approx( v1 )
+          </Original>
+          <Expanded>
+            { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+          <Original>
+            v1, Approx&lt;double>( { 1., 2., 3. } )
+          </Original>
+          <Expanded>
+            { 1.0, 2.0, 3.0 } is approx: { 1.0, 2.0, 3.0 }
+          </Expanded>
+        </Expression>
+        <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Section name="less than" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              a &lt; b
-            </Original>
-            <Expanded>
-              1 &lt; 2
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Vectors with elements" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Section name="Different length" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+          <Original>
+            v1, !Approx( temp )
+          </Original>
+          <Expanded>
+            { 1.0, 2.0, 3.0 } not is approx: { 1.0, 2.0, 3.0, 4.0 }
+          </Expanded>
+        </Expression>
         <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="nested SECTION tests" tags="[.][failing][sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Vectors with elements" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Section name="Same length, different elements" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+          <Original>
+            v1, !Approx( v2 )
+          </Original>
+          <Expanded>
+            { 1.0, 2.0, 3.0 } not is approx: { 1.5, 2.5, 3.5 }
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
           <Original>
-            a != b
+            v1, Approx( v2 ).margin( 0.5 )
           </Original>
           <Expanded>
-            1 != 2
+            { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
           <Original>
-            b != a
+            v1, Approx( v2 ).epsilon( 0.5 )
           </Original>
           <Expanded>
-            2 != 1
+            { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
           </Expanded>
         </Expression>
-        <Section name="not equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              a != b
-            </Original>
-            <Expanded>
-              1 != 2
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
+        <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+          <Original>
+            v1, Approx( v2 ).epsilon( 0.1 ).scale( 500 )
+          </Original>
+          <Expanded>
+            { 1.0, 2.0, 3.0 } is approx: { 1.5, 2.5, 3.5 }
+          </Expanded>
+        </Expression>
+        <OverallResults successes="4" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="non streamable - with conv. op" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <OverallResults successes="4" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Vector Approx matcher -- failing" tags="[.][approx][failing][matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="Empty and non empty vectors are not approx equal" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          s == "7"
+          empty, Approx( t1 )
         </Original>
         <Expanded>
-          "7" == "7"
+          {  } is approx: { 1.0, 2.0 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="non-copyable objects" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <Section name="Just different vectors" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          ti == typeid(int)
+          v1, Approx( v2 )
         </Original>
         <Expanded>
-          {?} == {?}
+          { 2.0, 4.0, 6.0 } is approx: { 1.0, 3.0, 5.0 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="normal_cdf" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Vector matchers" tags="[matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="Contains (element)" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          normal_cdf(0.000000) == Approx(0.50000000000000000)
+          v, VectorContains( 1 )
         </Original>
         <Expanded>
-          0.5 == Approx( 0.5 )
+          { 1, 2, 3 } Contains: 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          normal_cdf(1.000000) == Approx(0.84134474606854293)
+          v, VectorContains( 2 )
         </Original>
         <Expanded>
-          0.8413447461 == Approx( 0.8413447461 )
+          { 1, 2, 3 } Contains: 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          normal_cdf(-1.000000) == Approx(0.15865525393145705)
+          v5, ( VectorContains&lt;int, CustomAllocator&lt;int>>( 2 ) )
         </Original>
         <Expanded>
-          0.1586552539 == Approx( 0.1586552539 )
+          { 1, 2, 3 } Contains: 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Contains (vector)" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          normal_cdf(2.809729) == Approx(0.99752083845315409)
+          v, Contains( v2 )
         </Original>
         <Expanded>
-          0.9975208385 == Approx( 0.9975208385 )
+          { 1, 2, 3 } Contains: { 1, 2 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          normal_cdf(-1.352570) == Approx(0.08809652095066035)
+          v, Contains&lt;int>( { 1, 2 } )
         </Original>
         <Expanded>
-          0.088096521 == Approx( 0.088096521 )
+          { 1, 2, 3 } Contains: { 1, 2 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="normal_quantile" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          normal_quantile(0.551780) == Approx(0.13015979861484198)
+          v5, ( Contains&lt;int, std::allocator&lt;int>, CustomAllocator&lt;int>>( v2 ) )
         </Original>
         <Expanded>
-          0.1301597986 == Approx( 0.1301597986 )
+          { 1, 2, 3 } Contains: { 1, 2 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          normal_quantile(0.533700) == Approx(0.08457408802851875)
+          v, Contains( v2 )
         </Original>
         <Expanded>
-          0.084574088 == Approx( 0.084574088 )
+          { 1, 2, 3 } Contains: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          normal_quantile(0.025000) == Approx(-1.95996398454005449)
+          v, Contains( empty )
         </Original>
         <Expanded>
-          -1.9599639845 == Approx( -1.9599639845 )
+          { 1, 2, 3 } Contains: {  }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="not allowed" tags="[!throws]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="not prints unscoped info from previous failures" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        this MAY be seen only for the FIRST assertion IF info is printed for passing assertions
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          true
+          empty, Contains( empty )
         </Original>
         <Expanded>
-          true
+          {  } Contains: {  }
         </Expanded>
       </Expression>
-      <Info>
-        this MAY be seen only for the SECOND assertion IF info is printed for passing assertions
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          true
+          v5, ( Contains&lt;int, std::allocator&lt;int>, CustomAllocator&lt;int>>( v2 ) )
         </Original>
         <Expanded>
-          true
+          { 1, 2, 3 } Contains: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <Info>
-        this SHOULD be seen
-      </Info>
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          false
+          v5, Contains( v6 )
         </Original>
         <Expanded>
-          false
+          { 1, 2, 3 } Contains: { 1, 2 }
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="null strings" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="8" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Contains (element), composed" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          makeString( false ) != static_cast&lt;char*>(0)
+          v, VectorContains( 1 ) &amp;&amp; VectorContains( 2 )
         </Original>
         <Expanded>
-          "valid string" != {null string}
+          { 1, 2, 3 } ( Contains: 1 and Contains: 2 )
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Equals" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          makeString( true ) == static_cast&lt;char*>(0)
+          v, Equals( v )
         </Original>
         <Expanded>
-          {null string} == {null string}
+          { 1, 2, 3 } Equals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="null_ptr" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          ptr.get() == 0
+          empty, Equals( empty )
         </Original>
         <Expanded>
-          0 == 0
+          {  } Equals: {  }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="pair&lt;pair&lt;int,const char *,pair&lt;std::string,int> > -> toString" tags="[pair][toString]" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }"
+          v, Equals&lt;int>( { 1, 2, 3 } )
         </Original>
         <Expanded>
-          "{ { 42, "Arthur" }, { "Ford", 24 } }"
-==
-"{ { 42, "Arthur" }, { "Ford", 24 } }"
+          { 1, 2, 3 } Equals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="parseEnums" tags="[enums][Strings]" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-      <Section name="No enums" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-          <Original>
-            parseEnums( "" ), Equals( std::vector&lt;Catch::StringRef>{} )
-          </Original>
-          <Expanded>
-            {  } Equals: {  }
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="One enum value" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-          <Original>
-            parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector&lt;Catch::StringRef>{"Value1"} )
-          </Original>
-          <Expanded>
-            { Value1 } Equals: { Value1 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-          <Original>
-            parseEnums( "Value1" ), Equals( std::vector&lt;Catch::StringRef>{"Value1"} )
-          </Original>
-          <Expanded>
-            { Value1 } Equals: { Value1 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-          <Original>
-            parseEnums( "EnumName::Value1" ), Equals(std::vector&lt;Catch::StringRef>{"Value1"} )
-          </Original>
-          <Expanded>
-            { Value1 } Equals: { Value1 }
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Multiple enum values" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-          <Original>
-            parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector&lt;Catch::StringRef>{"Value1", "Value2"} )
-          </Original>
-          <Expanded>
-            { Value1, Value2 } Equals: { Value1, Value2 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-          <Original>
-            parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector&lt;Catch::StringRef>{"Value1", "Value2", "Value3"} )
-          </Original>
-          <Expanded>
-            { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
-          <Original>
-            parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector&lt;Catch::StringRef>{"Value1", "Value2", "Value3"} )
-          </Original>
-          <Expanded>
-            { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="pointer to class" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          p == 0
+          v, Equals( v2 )
         </Original>
         <Expanded>
-          0 == 0
+          { 1, 2, 3 } Equals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="print unscoped info if passing unscoped info is printed" tags="[info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        this MAY be seen IF info is printed for passing assertions
-      </Info>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          true
+          v5, ( Equals&lt;int, std::allocator&lt;int>, CustomAllocator&lt;int>>( v2 ) )
         </Original>
         <Expanded>
-          true
+          { 1, 2, 3 } Equals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="prints unscoped info on failure" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        this SHOULD be seen
-      </Info>
-      <Info>
-        this SHOULD also be seen
-      </Info>
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          false
+          v5, Equals( v6 )
         </Original>
         <Expanded>
-          false
+          { 1, 2, 3 } Equals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="prints unscoped info only for the first assertion" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        this SHOULD be seen only ONCE
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="UnorderedEquals" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          false
+          v, UnorderedEquals( v )
         </Original>
         <Expanded>
-          false
+          { 1, 2, 3 } UnorderedEquals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          true
+          v, UnorderedEquals&lt;int>( { 3, 2, 1 } )
         </Original>
         <Expanded>
-          true
+          { 1, 2, 3 } UnorderedEquals: { 3, 2, 1 }
         </Expanded>
       </Expression>
-      <Info>
-        this MAY also be seen only ONCE IF info is printed for passing assertions
-      </Info>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          true
+          empty, UnorderedEquals( empty )
         </Original>
         <Expanded>
-          true
+          {  } UnorderedEquals: {  }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          true
+          permuted, UnorderedEquals( v )
         </Original>
         <Expanded>
-          true
+          { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
         </Expanded>
-      </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="random SECTION tests" tags="[.][failing][sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            a != b
-          </Original>
-          <Expanded>
-            1 != 2
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            b != a
-          </Original>
-          <Expanded>
-            2 != 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="not equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            a != b
-          </Original>
-          <Expanded>
-            1 != 2
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="replaceInPlace" tags="[string-manip]" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-      <Section name="replace single char" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            Catch::replaceInPlace(letters, "b", "z")
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            letters == "azcdefcg"
-          </Original>
-          <Expanded>
-            "azcdefcg" == "azcdefcg"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="replace two chars" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            Catch::replaceInPlace(letters, "c", "z")
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            letters == "abzdefzg"
-          </Original>
-          <Expanded>
-            "abzdefzg" == "abzdefzg"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="replace first char" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            Catch::replaceInPlace(letters, "a", "z")
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            letters == "zbcdefcg"
-          </Original>
-          <Expanded>
-            "zbcdefcg" == "zbcdefcg"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="replace last char" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            Catch::replaceInPlace(letters, "g", "z")
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            letters == "abcdefcz"
-          </Original>
-          <Expanded>
-            "abcdefcz" == "abcdefcz"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="replace all chars" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            Catch::replaceInPlace(letters, letters, "replaced")
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            letters == "replaced"
-          </Original>
-          <Expanded>
-            "replaced" == "replaced"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="replace no chars" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-        <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            !(Catch::replaceInPlace(letters, "x", "z"))
-          </Original>
-          <Expanded>
-            !false
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            letters == letters
-          </Original>
-          <Expanded>
-            "abcdefcg" == "abcdefcg"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="escape '" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            Catch::replaceInPlace(s, "'", "|'")
-          </Original>
-          <Expanded>
-            true
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-          <Original>
-            s == "didn|'t"
-          </Original>
-          <Expanded>
-            "didn|'t" == "didn|'t"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="resolution" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      </Expression>
+      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res.size() == count
+          permuted, UnorderedEquals( v )
         </Original>
         <Expanded>
-          10 == 10
+          { 2, 3, 1 } UnorderedEquals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res[i] == rate
+          v5, ( UnorderedEquals&lt;int, std::allocator&lt;int>, CustomAllocator&lt;int>>( permuted ) )
         </Original>
         <Expanded>
-          1000.0 == 1000 (0x<hex digits>)
+          { 1, 2, 3 } UnorderedEquals: { 2, 3, 1 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res[i] == rate
+          v5_permuted, UnorderedEquals( v5 )
         </Original>
         <Expanded>
-          1000.0 == 1000 (0x<hex digits>)
+          { 1, 3, 2 } UnorderedEquals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="7" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="Vector matchers that fail" tags="[.][failing][matchers][vector]" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+    <Section name="Contains (element)" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res[i] == rate
+          v, VectorContains( -1 )
         </Original>
         <Expanded>
-          1000.0 == 1000 (0x<hex digits>)
+          { 1, 2, 3 } Contains: -1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res[i] == rate
+          empty, VectorContains( 1 )
         </Original>
         <Expanded>
-          1000.0 == 1000 (0x<hex digits>)
+          {  } Contains: 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="0" failures="2" expectedFailures="0"/>
+    </Section>
+    <Section name="Contains (vector)" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res[i] == rate
+          empty, Contains( v )
         </Original>
         <Expanded>
-          1000.0 == 1000 (0x<hex digits>)
+          {  } Contains: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res[i] == rate
+          v, Contains( v2 )
         </Original>
         <Expanded>
-          1000.0 == 1000 (0x<hex digits>)
+          { 1, 2, 3 } Contains: { 1, 2, 4 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="0" failures="2" expectedFailures="0"/>
+    </Section>
+    <Section name="Equals" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res[i] == rate
+          v, Equals( v2 )
         </Original>
         <Expanded>
-          1000.0 == 1000 (0x<hex digits>)
+          { 1, 2, 3 } Equals: { 1, 2 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res[i] == rate
+          v2, Equals( v )
         </Original>
         <Expanded>
-          1000.0 == 1000 (0x<hex digits>)
+          { 1, 2 } Equals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          res[i] == rate
+          empty, Equals( v )
         </Original>
         <Expanded>
-          1000.0 == 1000 (0x<hex digits>)
+          {  } Equals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="run_for_at_least, chronometer" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          meter.runs() >= old_runs
+          v, Equals( empty )
         </Original>
         <Expanded>
-          1 >= 1
+          { 1, 2, 3 } Equals: {  }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="0" failures="4" expectedFailures="0"/>
+    </Section>
+    <Section name="UnorderedEquals" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          meter.runs() >= old_runs
+          v, UnorderedEquals( empty )
         </Original>
         <Expanded>
-          2 >= 1
+          { 1, 2, 3 } UnorderedEquals: {  }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          meter.runs() >= old_runs
+          empty, UnorderedEquals( v )
         </Original>
         <Expanded>
-          4 >= 2
+          {  } UnorderedEquals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
         <Original>
-          meter.runs() >= old_runs
+          permuted, UnorderedEquals( v )
         </Original>
         <Expanded>
-          8 >= 4
+          { 1, 3 } UnorderedEquals: { 1, 2, 3 }
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="false" type="CHECK_THAT" filename="tests/<exe-name>/UsageTests/Matchers.tests.cpp" >
+        <Original>
+          permuted, UnorderedEquals( v )
+        </Original>
+        <Expanded>
+          { 3, 1 } UnorderedEquals: { 1, 2, 3 }
+        </Expanded>
+      </Expression>
+      <OverallResults successes="0" failures="4" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="When checked exceptions are thrown they can be expected or unexpected" tags="[!throws]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THROWS_AS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisThrows(), std::domain_error
+      </Original>
+      <Expanded>
+        thisThrows(), std::domain_error
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_NOTHROW" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisDoesntThrow()
+      </Original>
+      <Expanded>
+        thisDoesntThrow()
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisThrows()
+      </Original>
+      <Expanded>
+        thisThrows()
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="When unchecked exceptions are thrown directly they are always failures" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      unexpected exception
+    </Exception>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="When unchecked exceptions are thrown during a CHECK the test should continue" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisThrows() == 0
+      </Original>
+      <Expanded>
+        thisThrows() == 0
+      </Expanded>
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        expected exception
+      </Exception>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="When unchecked exceptions are thrown during a REQUIRE the test should abort fail" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisThrows() == 0
+      </Original>
+      <Expanded>
+        thisThrows() == 0
+      </Expanded>
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        expected exception
+      </Exception>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="When unchecked exceptions are thrown from functions they are always failures" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Original>
+        thisThrows() == 0
+      </Original>
+      <Expanded>
+        thisThrows() == 0
+      </Expanded>
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        expected exception
+      </Exception>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="When unchecked exceptions are thrown from sections they are always failures" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Section name="section name" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+        unexpected exception
+      </Exception>
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="When unchecked exceptions are thrown, but caught, they do not affect the test" tags="[!throws]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Where the LHS is not a simple value" tags="[.][failing][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Warning>
+      Uncomment the code in this test to check that it gives a sensible compiler error
+    </Warning>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="Where there is more to the expression after the RHS" tags="[.][failing][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Warning>
+      Uncomment the code in this test to check that it gives a sensible compiler error
+    </Warning>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="X/level/0/a" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="X/level/0/b" tags="[fizz][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="X/level/1/a" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="X/level/1/b" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="XmlEncode" tags="[XML]" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+    <Section name="normal string" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
         <Original>
-          meter.runs() >= old_runs
+          encode( "normal string" ) == "normal string"
         </Original>
         <Expanded>
-          16 >= 8
+          "normal string" == "normal string"
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="empty string" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
         <Original>
-          meter.runs() >= old_runs
+          encode( "" ) == ""
         </Original>
         <Expanded>
-          32 >= 16
+          "" == ""
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="string with ampersand" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
         <Original>
-          meter.runs() >= old_runs
+          encode( "smith &amp; jones" ) == "smith &amp;amp; jones"
         </Original>
         <Expanded>
-          64 >= 32
+          "smith &amp;amp; jones" == "smith &amp;amp; jones"
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="string with less-than" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
         <Original>
-          meter.runs() >= old_runs
+          encode( "smith &lt; jones" ) == "smith &amp;lt; jones"
         </Original>
         <Expanded>
-          128 >= 64
+          "smith &amp;lt; jones" == "smith &amp;lt; jones"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="string with greater-than" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
         <Original>
-          Timing.elapsed >= time
+          encode( "smith > jones" ) == "smith > jones"
         </Original>
         <Expanded>
-          128 ns >= 100 ns
+          "smith > jones" == "smith > jones"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+        <Original>
+          encode( "smith ]]&gt; jones" ) == "smith ]]&amp;gt; jones"
+        </Original>
+        <Expanded>
+          "smith ]]&amp;gt; jones"
+==
+"smith ]]&amp;gt; jones"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="string with quotes" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
         <Original>
-          Timing.result == Timing.iterations + 17
+          encode( stringWithQuotes ) == stringWithQuotes
         </Original>
         <Expanded>
-          145 == 145
+          "don't "quote" me on that"
+==
+"don't "quote" me on that"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
         <Original>
-          Timing.iterations >= time.count()
+          encode( stringWithQuotes, Catch::XmlEncode::ForAttributes ) == "don't &amp;quot;quote&amp;quot; me on that"
         </Original>
         <Expanded>
-          128 >= 100
+          "don't &amp;quot;quote&amp;quot; me on that"
+==
+"don't &amp;quot;quote&amp;quot; me on that"
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="run_for_at_least, int" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="string with control char (1)" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+        <Original>
+          encode( "[\x01]" ) == "[\\x01]"
+        </Original>
+        <Expanded>
+          "[\x01]" == "[\x01]"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="string with control char (x7F)" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+        <Original>
+          encode( "[\x7F]" ) == "[\\x7F]"
+        </Original>
+        <Expanded>
+          "[\x7F]" == "[\x7F]"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="XmlWriter writes boolean attributes as true/false" tags="[XML][XmlWriter]" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Xml.tests.cpp" >
+      <Original>
+        stream.str(), Contains(R"(attr1="true")") &amp;&amp; Contains(R"(attr2="false")")
+      </Original>
+      <Expanded>
+        "&lt;?xml version="1.0" encoding="UTF-8"?>
+&lt;Element1 attr1="true" attr2="false"/>
+" ( contains: "attr1="true"" and contains: "attr2="false"" )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="analyse no analysis" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.mean.point.count() == 23
+      </Original>
+      <Expanded>
+        23.0 == 23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.mean.lower_bound.count() == 23
+      </Original>
+      <Expanded>
+        23.0 == 23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.mean.upper_bound.count() == 23
+      </Original>
+      <Expanded>
+        23.0 == 23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.standard_deviation.point.count() == 0
+      </Original>
+      <Expanded>
+        0.0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.standard_deviation.lower_bound.count() == 0
+      </Original>
+      <Expanded>
+        0.0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.standard_deviation.upper_bound.count() == 0
+      </Original>
+      <Expanded>
+        0.0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.outliers.total() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.outliers.low_mild == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.outliers.low_severe == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.outliers.high_mild == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.outliers.high_severe == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.outliers.samples_seen == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        analysis.outlier_variance == 0
+      </Original>
+      <Expanded>
+        0.0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="array&lt;int, N> -> toString" tags="[array][containers][toString]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        Catch::Detail::stringify( empty ) == "{  }"
+      </Original>
+      <Expanded>
+        "{  }" == "{  }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        Catch::Detail::stringify( oneValue ) == "{ 42 }"
+      </Original>
+      <Expanded>
+        "{ 42 }" == "{ 42 }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        Catch::Detail::stringify( twoValues ) == "{ 42, 250 }"
+      </Original>
+      <Expanded>
+        "{ 42, 250 }" == "{ 42, 250 }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="atomic if" tags="[0][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        x == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="benchmark function call" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Section name="without chronometer" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
       <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          x >= old_x
+          model.started == 1
         </Original>
         <Expanded>
-          1 >= 1
+          1 == 1
         </Expanded>
       </Expression>
       <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          x >= old_x
+          model.finished == 0
         </Original>
         <Expanded>
-          2 >= 1
+          0 == 0
         </Expanded>
       </Expression>
       <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          x >= old_x
+          model.started == 1
         </Original>
         <Expanded>
-          4 >= 2
+          1 == 1
         </Expanded>
       </Expression>
       <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          x >= old_x
+          model.finished == 1
         </Original>
         <Expanded>
-          8 >= 4
+          1 == 1
         </Expanded>
       </Expression>
       <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          x >= old_x
+          called == 1
         </Original>
         <Expanded>
-          16 >= 8
+          1 == 1
         </Expanded>
       </Expression>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="with chronometer" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
       <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          x >= old_x
+          model.started == 0
         </Original>
         <Expanded>
-          32 >= 16
+          0 == 0
         </Expanded>
       </Expression>
       <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          x >= old_x
+          model.finished == 0
         </Original>
         <Expanded>
-          64 >= 32
+          0 == 0
         </Expanded>
       </Expression>
       <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          x >= old_x
+          model.started == 0
         </Original>
         <Expanded>
-          128 >= 64
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          Timing.elapsed >= time
+          model.finished == 0
         </Original>
         <Expanded>
-          128 ns >= 100 ns
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          Timing.result == Timing.iterations + 17
+          called == 1
         </Original>
         <Expanded>
-          145 == 145
+          1 == 1
         </Expanded>
       </Expression>
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="boolean member" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        obj.prop != 0
+      </Original>
+      <Expanded>
+        0x<hex digits> != 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="checkedElse" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        flag
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        testCheckedElse( true )
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="checkedElse, failing" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="false" type="CHECKED_ELSE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        flag
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        testCheckedElse( false )
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="checkedIf" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="CHECKED_IF" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        flag
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        testCheckedIf( true )
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="checkedIf, failing" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="false" type="CHECKED_IF" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        flag
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        testCheckedIf( false )
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="classify_outliers" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Section name="none" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
       <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          Timing.iterations >= time.count()
+          o.samples_seen == static_cast&lt;int>(x.size())
         </Original>
         <Expanded>
-          128 >= 100
+          6 == 6
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="second tag" tags="[tag2]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="send a single char to INFO" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Info>
-        3
-      </Info>
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          false
+          o.low_severe == los
         </Original>
         <Expanded>
-          false
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="sends information to INFO" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        hi
-      </Info>
-      <Info>
-        i := 7
-      </Info>
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          false
+          o.low_mild == lom
         </Original>
         <Expanded>
-          false
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="shortened hide tags are split apart" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
-      <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          tags, VectorContains("magic-tag"_catch_sr) &amp;&amp; VectorContains("."_catch_sr)
+          o.high_mild == him
         </Original>
         <Expanded>
-          { ., magic-tag } ( Contains: magic-tag and Contains: . )
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="splitString" tags="[string-manip]" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          splitStringRef("", ','), Equals(std::vector&lt;StringRef>())
+          o.high_severe == his
         </Original>
         <Expanded>
-          {  } Equals: {  }
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          splitStringRef("abc", ','), Equals(std::vector&lt;StringRef>{"abc"})
+          o.total() == los + lom + him + his
         </Original>
         <Expanded>
-          { abc } Equals: { abc }
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="low severe" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          splitStringRef("abc,def", ','), Equals(std::vector&lt;StringRef>{"abc", "def"})
+          o.samples_seen == static_cast&lt;int>(x.size())
         </Original>
         <Expanded>
-          { abc, def } Equals: { abc, def }
+          6 == 6
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="stacks unscoped info in loops" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
-      <Info>
-        Count 1 to 3...
-      </Info>
-      <Info>
-        1
-      </Info>
-      <Info>
-        2
-      </Info>
-      <Info>
-        3
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          false
+          o.low_severe == los
         </Original>
         <Expanded>
-          false
+          1 == 1
         </Expanded>
       </Expression>
-      <Info>
-        Count 4 to 6...
-      </Info>
-      <Info>
-        4
-      </Info>
-      <Info>
-        5
-      </Info>
-      <Info>
-        6
-      </Info>
-      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          false
+          o.low_mild == lom
         </Original>
         <Expanded>
-          false
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="std::map is convertible string" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-      <Section name="empty" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            Catch::Detail::stringify( emptyMap ) == "{  }"
-          </Original>
-          <Expanded>
-            "{  }" == "{  }"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="single item" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            Catch::Detail::stringify( map ) == "{ { \"one\", 1 } }"
-          </Original>
-          <Expanded>
-            "{ { "one", 1 } }" == "{ { "one", 1 } }"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="several items" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            Catch::Detail::stringify( map ) == "{ { \"abc\", 1 }, { \"def\", 2 }, { \"ghi\", 3 } }"
-          </Original>
-          <Expanded>
-            "{ { "abc", 1 }, { "def", 2 }, { "ghi", 3 } }"
-==
-"{ { "abc", 1 }, { "def", 2 }, { "ghi", 3 } }"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="std::pair&lt;int,const std::string> -> toString" tags="[pair][toString]" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(value) == "{ 34, \"xyzzy\" }"
+          o.high_mild == him
         </Original>
         <Expanded>
-          "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }"
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="std::pair&lt;int,std::string> -> toString" tags="[pair][toString]" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( value ) == "{ 34, \"xyzzy\" }"
+          o.high_severe == his
         </Original>
         <Expanded>
-          "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }"
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="std::set is convertible string" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-      <Section name="empty" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            Catch::Detail::stringify( emptySet ) == "{  }"
-          </Original>
-          <Expanded>
-            "{  }" == "{  }"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="single item" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            Catch::Detail::stringify( set ) == "{ \"one\" }"
-          </Original>
-          <Expanded>
-            "{ "one" }" == "{ "one" }"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="several items" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
-          <Original>
-            Catch::Detail::stringify( set ) == "{ \"abc\", \"def\", \"ghi\" }"
-          </Original>
-          <Expanded>
-            "{ "abc", "def", "ghi" }"
-==
-"{ "abc", "def", "ghi" }"
-          </Expanded>
-        </Expression>
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="std::vector&lt;std::pair&lt;std::string,int> > -> toString" tags="[pair][toString]" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( pr ) == "{ { \"green\", 55 } }"
+          o.total() == los + lom + him + his
         </Original>
         <Expanded>
-          "{ { "green", 55 } }"
-==
-"{ { "green", 55 } }"
+          1 == 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="string literals of different sizes can be compared" tags="[.][failing][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
-      <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="low mild" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          std::string( "first" ) == "second"
+          o.samples_seen == static_cast&lt;int>(x.size())
         </Original>
         <Expanded>
-          "first" == "second"
+          6 == 6
         </Expanded>
       </Expression>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="stringify ranges" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(streamable_range{}) == "op&lt;&lt;(streamable_range)"
+          o.low_severe == los
         </Original>
         <Expanded>
-          "op&lt;&lt;(streamable_range)"
-==
-"op&lt;&lt;(streamable_range)"
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(stringmaker_range{}) == "stringmaker(streamable_range)"
+          o.low_mild == lom
         </Original>
         <Expanded>
-          "stringmaker(streamable_range)"
-==
-"stringmaker(streamable_range)"
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(just_range{}) == "{ 1, 2, 3, 4 }"
+          o.high_mild == him
         </Original>
         <Expanded>
-          "{ 1, 2, 3, 4 }" == "{ 1, 2, 3, 4 }"
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(disabled_range{}) == "{?}"
+          o.high_severe == his
         </Original>
         <Expanded>
-          "{?}" == "{?}"
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="stringify( has_maker )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( item ) == "StringMaker&lt;has_maker>"
+          o.total() == los + lom + him + his
         </Original>
         <Expanded>
-          "StringMaker&lt;has_maker>"
-==
-"StringMaker&lt;has_maker>"
+          1 == 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="stringify( has_maker_and_operator )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="high mild" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( item ) == "StringMaker&lt;has_maker_and_operator>"
+          o.samples_seen == static_cast&lt;int>(x.size())
         </Original>
         <Expanded>
-          "StringMaker&lt;has_maker_and_operator>"
-==
-"StringMaker&lt;has_maker_and_operator>"
+          6 == 6
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="stringify( has_neither )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(item) == "{?}"
+          o.low_severe == los
         </Original>
         <Expanded>
-          "{?}" == "{?}"
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="stringify( has_operator )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( item ) == "operator&lt;&lt;( has_operator )"
+          o.low_mild == lom
         </Original>
         <Expanded>
-          "operator&lt;&lt;( has_operator )"
-==
-"operator&lt;&lt;( has_operator )"
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="stringify( has_template_operator )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( item ) == "operator&lt;&lt;( has_template_operator )"
+          o.high_mild == him
         </Original>
         <Expanded>
-          "operator&lt;&lt;( has_template_operator )"
-==
-"operator&lt;&lt;( has_template_operator )"
+          1 == 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="stringify( vectors&lt;has_maker> )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( v ) == "{ StringMaker&lt;has_maker> }"
+          o.high_severe == his
         </Original>
         <Expanded>
-          "{ StringMaker&lt;has_maker> }"
-==
-"{ StringMaker&lt;has_maker> }"
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="stringify( vectors&lt;has_maker_and_operator> )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( v ) == "{ StringMaker&lt;has_maker_and_operator> }"
+          o.total() == los + lom + him + his
         </Original>
         <Expanded>
-          "{ StringMaker&lt;has_maker_and_operator> }"
-==
-"{ StringMaker&lt;has_maker_and_operator> }"
+          1 == 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="stringify( vectors&lt;has_operator> )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="high severe" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify( v ) == "{ operator&lt;&lt;( has_operator ) }"
+          o.samples_seen == static_cast&lt;int>(x.size())
         </Original>
         <Expanded>
-          "{ operator&lt;&lt;( has_operator ) }"
-==
-"{ operator&lt;&lt;( has_operator ) }"
+          6 == 6
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="strlen3" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          data.str.size() == data.len
+          o.low_severe == los
         </Original>
         <Expanded>
-          3 == 3
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          data.str.size() == data.len
+          o.low_mild == lom
         </Original>
         <Expanded>
-          3 == 3
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          data.str.size() == data.len
+          o.high_mild == him
         </Original>
         <Expanded>
-          5 == 5
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          data.str.size() == data.len
+          o.high_severe == his
         </Original>
         <Expanded>
-          4 == 4
+          1 == 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="tables" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          strlen(std::get&lt;0>(data)) == static_cast&lt;size_t>(std::get&lt;1>(data))
+          o.total() == los + lom + him + his
         </Original>
         <Expanded>
-          5 == 5
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="mixed" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          strlen(std::get&lt;0>(data)) == static_cast&lt;size_t>(std::get&lt;1>(data))
+          o.samples_seen == static_cast&lt;int>(x.size())
         </Original>
         <Expanded>
           6 == 6
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          strlen(std::get&lt;0>(data)) == static_cast&lt;size_t>(std::get&lt;1>(data))
+          o.low_severe == los
         </Original>
         <Expanded>
-          5 == 5
+          1 == 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          strlen(std::get&lt;0>(data)) == static_cast&lt;size_t>(std::get&lt;1>(data))
+          o.low_mild == lom
         </Original>
         <Expanded>
-          6 == 6
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="thrown std::strings are translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-      <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
-        Why would you throw a std::string?
-      </Exception>
-      <OverallResult success="false"/>
-    </TestCase>
-    <TestCase name="toString on const wchar_t const pointer returns the string contents" tags="[toString]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          result == "\"wide load\""
+          o.high_mild == him
         </Original>
         <Expanded>
-          ""wide load"" == ""wide load""
+          1 == 1
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+        <Original>
+          o.high_severe == his
+        </Original>
+        <Expanded>
+          0 == 0
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="toString on const wchar_t pointer returns the string contents" tags="[toString]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
         <Original>
-          result == "\"wide load\""
+          o.total() == los + lom + him + his
         </Original>
         <Expanded>
-          ""wide load"" == ""wide load""
+          2 == 2
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="toString on wchar_t const pointer returns the string contents" tags="[toString]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="comparisons between const int variables" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        unsigned_char_var == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        unsigned_short_var == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        unsigned_int_var == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        unsigned_long_var == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="comparisons between int variables" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        long_var == unsigned_char_var
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        long_var == unsigned_short_var
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        long_var == unsigned_int_var
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Condition.tests.cpp" >
+      <Original>
+        long_var == unsigned_long_var
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="convertToBits" tags="[conversion][floating-point]" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
+      <Original>
+        convertToBits( 0.f ) == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
+      <Original>
+        convertToBits( -0.f ) == ( 1ULL &lt;&lt; 31 )
+      </Original>
+      <Expanded>
+        2147483648 (0x<hex digits>)
+==
+2147483648 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
+      <Original>
+        convertToBits( 0. ) == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
+      <Original>
+        convertToBits( -0. ) == ( 1ULL &lt;&lt; 63 )
+      </Original>
+      <Expanded>
+        9223372036854775808 (0x<hex digits>)
+==
+9223372036854775808 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
+      <Original>
+        convertToBits( std::numeric_limits&lt;float>::denorm_min() ) == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/FloatingPoint.tests.cpp" >
+      <Original>
+        convertToBits( std::numeric_limits&lt;double>::denorm_min() ) == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="empty tags are not allowed" tags="[tags]" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THROWS" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+      <Original>
+        Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo)
+      </Original>
+      <Expanded>
+        Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo)
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="erfc_inv" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        erfc_inv(1.103560) == Approx(-0.09203687623843015)
+      </Original>
+      <Expanded>
+        -0.0920368762 == Approx( -0.0920368762 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        erfc_inv(1.067400) == Approx(-0.05980291115763361)
+      </Original>
+      <Expanded>
+        -0.0598029112 == Approx( -0.0598029112 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        erfc_inv(0.050000) == Approx(1.38590382434967796)
+      </Original>
+      <Expanded>
+        1.3859038243 == Approx( 1.3859038243 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="estimate_clock_resolution" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res.mean.count() == rate
+      </Original>
+      <Expanded>
+        2000.0 == 2000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res.outliers.total() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="even more nested SECTION tests" tags="[sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Section name="c" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Section name="d (leaf)" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="c" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Section name="e (leaf)" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="f (leaf)" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="first tag" tags="[tag1]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="has printf" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+loose text artifact
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="just failure" tags="[.][fail][isolated info][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      Previous info should not be seen
+    </Failure>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="just failure after unscoped info" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Failure filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      previous unscoped info SHOULD not be seen
+    </Failure>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="just info" tags="[info][isolated info][messages]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="just unscoped info" tags="[info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="long long" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        l == std::numeric_limits&lt;long long>::max()
+      </Original>
+      <Expanded>
+        9223372036854775807 (0x<hex digits>)
+==
+9223372036854775807 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="looped SECTION tests" tags="[.][failing][sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Section name="b is currently: 0" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          result == "\"wide load\""
+          b > a
         </Original>
         <Expanded>
-          ""wide load"" == ""wide load""
+          0 > 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="toString on wchar_t returns the string contents" tags="[toString]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <Section name="b is currently: 1" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          result == "\"wide load\""
+          b > a
         </Original>
         <Expanded>
-          ""wide load"" == ""wide load""
+          1 > 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="toString(enum class w/operator&lt;&lt;)" tags="[enum][enumClass][toString]" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <Section name="b is currently: 2" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(e0) == "E2/V0"
+          b > a
         </Original>
         <Expanded>
-          "E2/V0" == "E2/V0"
+          2 > 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="b is currently: 3" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(e1) == "E2/V1"
+          b > a
         </Original>
         <Expanded>
-          "E2/V1" == "E2/V1"
+          3 > 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="b is currently: 4" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(e3) == "Unknown enum value 10"
+          b > a
         </Original>
         <Expanded>
-          "Unknown enum value 10"
-==
-"Unknown enum value 10"
+          4 > 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="toString(enum class)" tags="[enum][enumClass][toString]" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="b is currently: 5" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(e0) == "0"
+          b > a
         </Original>
         <Expanded>
-          "0" == "0"
+          5 > 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="b is currently: 6" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(e1) == "1"
+          b > a
         </Original>
         <Expanded>
-          "1" == "1"
+          6 > 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="toString(enum w/operator&lt;&lt;)" tags="[enum][toString]" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="b is currently: 7" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(e0) == "E2{0}"
+          b > a
         </Original>
         <Expanded>
-          "E2{0}" == "E2{0}"
+          7 > 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="b is currently: 8" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(e1) == "E2{1}"
+          b > a
         </Original>
         <Expanded>
-          "E2{1}" == "E2{1}"
+          8 > 1
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="toString(enum)" tags="[enum][toString]" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="b is currently: 9" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(e0) == "0"
+          b > a
+        </Original>
+        <Expanded>
+          9 > 1
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="looped tests" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Info>
+      Testing if fib[0] (1) is even
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        ( fib[i] % 2 ) == 0
+      </Original>
+      <Expanded>
+        1 == 0
+      </Expanded>
+    </Expression>
+    <Info>
+      Testing if fib[1] (1) is even
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        ( fib[i] % 2 ) == 0
+      </Original>
+      <Expanded>
+        1 == 0
+      </Expanded>
+    </Expression>
+    <Info>
+      Testing if fib[2] (2) is even
+    </Info>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        ( fib[i] % 2 ) == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Info>
+      Testing if fib[3] (3) is even
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        ( fib[i] % 2 ) == 0
+      </Original>
+      <Expanded>
+        1 == 0
+      </Expanded>
+    </Expression>
+    <Info>
+      Testing if fib[4] (5) is even
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        ( fib[i] % 2 ) == 0
+      </Original>
+      <Expanded>
+        1 == 0
+      </Expanded>
+    </Expression>
+    <Info>
+      Testing if fib[5] (8) is even
+    </Info>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        ( fib[i] % 2 ) == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <Info>
+      Testing if fib[6] (13) is even
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        ( fib[i] % 2 ) == 0
+      </Original>
+      <Expanded>
+        1 == 0
+      </Expanded>
+    </Expression>
+    <Info>
+      Testing if fib[7] (21) is even
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        ( fib[i] % 2 ) == 0
+      </Original>
+      <Expanded>
+        1 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="make_unique reimplementation" tags="[internals][unique-ptr]" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+    <Section name="From lvalue copies" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          !(lval.has_moved)
         </Original>
         <Expanded>
-          "0" == "0"
+          !false
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="From rvalue moves" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(e1) == "1"
+          rval.has_moved
         </Original>
         <Expanded>
-          "1" == "1"
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="tuple&lt;>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Variadic constructor" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
         <Original>
-          "{ }" == ::Catch::Detail::stringify(type{})
+          *ptr == std::tuple&lt;int, double, int>{1, 2., 3}
         </Original>
         <Expanded>
-          "{ }" == "{ }"
+          {?} == {?}
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="mean" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        m == 19.
+      </Original>
+      <Expanded>
+        19.0 == 19.0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="measure" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x == 17
+      </Original>
+      <Expanded>
+        17 == 17
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x == 23
+      </Original>
+      <Expanded>
+        23 == 23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        r.elapsed.count() == 42
+      </Original>
+      <Expanded>
+        42 == 42
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        r.result == 23
+      </Original>
+      <Expanded>
+        23 == 23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        r.iterations == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        s.elapsed.count() == 69
+      </Original>
+      <Expanded>
+        69 == 69
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        s.result == 17
+      </Original>
+      <Expanded>
+        17 == 17
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        s.iterations == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="mix info, unscoped info and warning" tags="[info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      info
+    </Info>
+    <Info>
+      unscoped info
+    </Info>
+    <Warning>
+      and warn may mix
+    </Warning>
+    <Info>
+      info
+    </Info>
+    <Info>
+      unscoped info
+    </Info>
+    <Warning>
+      they are not cleared after warnings
+    </Warning>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="more nested SECTION tests" tags="[.][failing][sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Section name="equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+          <Original>
+            a == b
+          </Original>
+          <Expanded>
+            1 == 2
+          </Expanded>
+        </Expression>
+        <OverallResults successes="0" failures="1" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="0" failures="1" expectedFailures="0"/>
+    </Section>
+    <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Section name="not equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+          <Original>
+            a != b
+          </Original>
+          <Expanded>
+            1 != 2
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Section name="less than" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+          <Original>
+            a &lt; b
+          </Original>
+          <Expanded>
+            1 &lt; 2
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="nested SECTION tests" tags="[.][failing][sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          "{ }" == ::Catch::Detail::stringify(value)
+          a != b
         </Original>
         <Expanded>
-          "{ }" == "{ }"
+          1 != 2
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="tuple&lt;float,int>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          "1.2f" == ::Catch::Detail::stringify(float(1.2))
+          b != a
         </Original>
         <Expanded>
-          "1.2f" == "1.2f"
+          2 != 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Section name="not equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+          <Original>
+            a != b
+          </Original>
+          <Expanded>
+            1 != 2
+          </Expanded>
+        </Expression>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
+      </Section>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="non streamable - with conv. op" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        s == "7"
+      </Original>
+      <Expanded>
+        "7" == "7"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="non-copyable objects" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        ti == typeid(int)
+      </Original>
+      <Expanded>
+        {?} == {?}
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="normal_cdf" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        normal_cdf(0.000000) == Approx(0.50000000000000000)
+      </Original>
+      <Expanded>
+        0.5 == Approx( 0.5 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        normal_cdf(1.000000) == Approx(0.84134474606854293)
+      </Original>
+      <Expanded>
+        0.8413447461 == Approx( 0.8413447461 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        normal_cdf(-1.000000) == Approx(0.15865525393145705)
+      </Original>
+      <Expanded>
+        0.1586552539 == Approx( 0.1586552539 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        normal_cdf(2.809729) == Approx(0.99752083845315409)
+      </Original>
+      <Expanded>
+        0.9975208385 == Approx( 0.9975208385 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        normal_cdf(-1.352570) == Approx(0.08809652095066035)
+      </Original>
+      <Expanded>
+        0.088096521 == Approx( 0.088096521 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="normal_quantile" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        normal_quantile(0.551780) == Approx(0.13015979861484198)
+      </Original>
+      <Expanded>
+        0.1301597986 == Approx( 0.1301597986 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        normal_quantile(0.533700) == Approx(0.08457408802851875)
+      </Original>
+      <Expanded>
+        0.084574088 == Approx( 0.084574088 )
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        normal_quantile(0.025000) == Approx(-1.95996398454005449)
+      </Original>
+      <Expanded>
+        -1.9599639845 == Approx( -1.9599639845 )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="not allowed" tags="[!throws]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="not prints unscoped info from previous failures" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      this MAY be seen only for the FIRST assertion IF info is printed for passing assertions
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Info>
+      this MAY be seen only for the SECOND assertion IF info is printed for passing assertions
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Info>
+      this SHOULD be seen
+    </Info>
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="null strings" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        makeString( false ) != static_cast&lt;char*>(0)
+      </Original>
+      <Expanded>
+        "valid string" != {null string}
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        makeString( true ) == static_cast&lt;char*>(0)
+      </Original>
+      <Expanded>
+        {null string} == {null string}
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="null_ptr" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        ptr.get() == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="pair&lt;pair&lt;int,const char *,pair&lt;std::string,int> > -> toString" tags="[pair][toString]" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( pair ) == "{ { 42, \"Arthur\" }, { \"Ford\", 24 } }"
+      </Original>
+      <Expanded>
+        "{ { 42, "Arthur" }, { "Ford", 24 } }"
+==
+"{ { 42, "Arthur" }, { "Ford", 24 } }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="parseEnums" tags="[enums][Strings]" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+    <Section name="No enums" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
         <Original>
-          "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0})
+          parseEnums( "" ), Equals( std::vector&lt;Catch::StringRef>{} )
         </Original>
         <Expanded>
-          "{ 1.2f, 0 }" == "{ 1.2f, 0 }"
+          {  } Equals: {  }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="tuple&lt;int>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="One enum value" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
         <Original>
-          "{ 0 }" == ::Catch::Detail::stringify(type{0})
+          parseEnums( "ClassName::EnumName::Value1" ), Equals(std::vector&lt;Catch::StringRef>{"Value1"} )
         </Original>
         <Expanded>
-          "{ 0 }" == "{ 0 }"
+          { Value1 } Equals: { Value1 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="tuple&lt;0,int,const char *>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
         <Original>
-          "{ 0, 42, \"Catch me\" }" == ::Catch::Detail::stringify(value)
+          parseEnums( "Value1" ), Equals( std::vector&lt;Catch::StringRef>{"Value1"} )
         </Original>
         <Expanded>
-          "{ 0, 42, "Catch me" }"
-==
-"{ 0, 42, "Catch me" }"
+          { Value1 } Equals: { Value1 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="tuple&lt;string,string>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
         <Original>
-          "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"})
+          parseEnums( "EnumName::Value1" ), Equals(std::vector&lt;Catch::StringRef>{"Value1"} )
         </Original>
         <Expanded>
-          "{ "hello", "world" }"
-==
-"{ "hello", "world" }"
+          { Value1 } Equals: { Value1 }
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="tuple&lt;tuple&lt;int>,tuple&lt;>,float>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Multiple enum values" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+        <Original>
+          parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2" ), Equals( std::vector&lt;Catch::StringRef>{"Value1", "Value2"} )
+        </Original>
+        <Expanded>
+          { Value1, Value2 } Equals: { Value1, Value2 }
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+        <Original>
+          parseEnums( "ClassName::EnumName::Value1, ClassName::EnumName::Value2, ClassName::EnumName::Value3" ), Equals( std::vector&lt;Catch::StringRef>{"Value1", "Value2", "Value3"} )
+        </Original>
+        <Expanded>
+          { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/ToString.tests.cpp" >
+        <Original>
+          parseEnums( "ClassName::EnumName::Value1,ClassName::EnumName::Value2 , ClassName::EnumName::Value3" ), Equals( std::vector&lt;Catch::StringRef>{"Value1", "Value2", "Value3"} )
+        </Original>
+        <Expanded>
+          { Value1, Value2, Value3 } Equals: { Value1, Value2, Value3 }
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="pointer to class" tags="[Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        p == 0
+      </Original>
+      <Expanded>
+        0 == 0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="print unscoped info if passing unscoped info is printed" tags="[info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      this MAY be seen IF info is printed for passing assertions
+    </Info>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="prints unscoped info on failure" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      this SHOULD be seen
+    </Info>
+    <Info>
+      this SHOULD also be seen
+    </Info>
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="prints unscoped info only for the first assertion" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      this SHOULD be seen only ONCE
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Info>
+      this MAY also be seen only ONCE IF info is printed for passing assertions
+    </Info>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        true
+      </Original>
+      <Expanded>
+        true
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="random SECTION tests" tags="[.][failing][sections]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Section name="doesn't equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value)
+          a != b
         </Original>
         <Expanded>
-          "{ { 42 }, { }, 1.2f }"
-==
-"{ { 42 }, { }, 1.2f }"
+          1 != 2
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="uniform samples" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          e.point == 23
+          b != a
         </Original>
         <Expanded>
-          23.0 == 23
+          2 != 1
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="not equal" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          e.upper_bound == 23
+          a != b
         </Original>
         <Expanded>
-          23.0 == 23
+          1 != 2
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="replaceInPlace" tags="[string-manip]" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+    <Section name="replace single char" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          e.lower_bound == 23
+          Catch::replaceInPlace(letters, "b", "z")
         </Original>
         <Expanded>
-          23.0 == 23
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          e.confidence_interval == 0.95
+          letters == "azcdefcg"
         </Original>
         <Expanded>
-          0.95 == 0.95
+          "azcdefcg" == "azcdefcg"
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="unique_ptr reimplementation: basic functionality" tags="[internals][unique-ptr]" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-      <Section name="Default constructed unique_ptr is empty" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            !(ptr)
-          </Original>
-          <Expanded>
-            !{?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            ptr.get() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Take ownership of allocation" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            ptr
-          </Original>
-          <Expanded>
-            {?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            *ptr == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            ptr.get() == naked_ptr
-          </Original>
-          <Expanded>
-            0x<hex digits> == 0x<hex digits>
-          </Expanded>
-        </Expression>
-        <Section name="Plain reset deallocates" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-            <Original>
-              !(ptr)
-            </Original>
-            <Expanded>
-              !{?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-            <Original>
-              ptr.get() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="2" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="5" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Take ownership of allocation" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            ptr
-          </Original>
-          <Expanded>
-            {?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            *ptr == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            ptr.get() == naked_ptr
-          </Original>
-          <Expanded>
-            0x<hex digits> == 0x<hex digits>
-          </Expanded>
-        </Expression>
-        <Section name="Reset replaces ownership" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-            <Original>
-              ptr
-            </Original>
-            <Expanded>
-              {?}
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-            <Original>
-              ptr.get() != 0
-            </Original>
-            <Expanded>
-              0x<hex digits> != 0
-            </Expanded>
-          </Expression>
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-            <Original>
-              *ptr == 2
-            </Original>
-            <Expanded>
-              2 == 2
-            </Expanded>
-          </Expression>
-          <OverallResults successes="3" failures="0" expectedFailures="0"/>
-        </Section>
-        <OverallResults successes="6" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Release releases ownership" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            !(ptr)
-          </Original>
-          <Expanded>
-            !{?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            ptr.get() == 0
-          </Original>
-          <Expanded>
-            0 == 0
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Move constructor" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            !(ptr1)
-          </Original>
-          <Expanded>
-            !{?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            ptr2
-          </Original>
-          <Expanded>
-            {?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            *ptr2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="Move assignment" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            !(ptr2)
-          </Original>
-          <Expanded>
-            !{?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            ptr1
-          </Original>
-          <Expanded>
-            {?}
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            *ptr1 == 2
-          </Original>
-          <Expanded>
-            2 == 2
-          </Expanded>
-        </Expression>
-        <OverallResults successes="3" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="free swap" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            *ptr1 == 2
-          </Original>
-          <Expanded>
-            2 == 2
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
-          <Original>
-            *ptr2 == 1
-          </Original>
-          <Expanded>
-            1 == 1
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="vec&lt;vec&lt;string,alloc>> -> toString" tags="[toString][vector,allocator]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="replace two chars" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+        <Original>
+          Catch::replaceInPlace(letters, "c", "z")
+        </Original>
+        <Expanded>
+          true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(v) == "{  }"
+          letters == "abzdefzg"
         </Original>
         <Expanded>
-          "{  }" == "{  }"
+          "abzdefzg" == "abzdefzg"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="replace first char" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(v) == "{ { \"hello\" }, { \"world\" } }"
+          Catch::replaceInPlace(letters, "a", "z")
         </Original>
         <Expanded>
-          "{ { "hello" }, { "world" } }"
-==
-"{ { "hello" }, { "world" } }"
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="vector&lt;bool> -> toString" tags="[containers][toString][vector]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(bools) == "{  }"
+          letters == "zbcdefcg"
         </Original>
         <Expanded>
-          "{  }" == "{  }"
+          "zbcdefcg" == "zbcdefcg"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="replace last char" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(bools) == "{ true }"
+          Catch::replaceInPlace(letters, "g", "z")
         </Original>
         <Expanded>
-          "{ true }" == "{ true }"
+          true
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(bools) == "{ true, false }"
+          letters == "abcdefcz"
         </Original>
         <Expanded>
-          "{ true, false }" == "{ true, false }"
+          "abcdefcz" == "abcdefcz"
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="vector&lt;int,allocator> -> toString" tags="[toString][vector,allocator]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="replace all chars" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(vv) == "{  }"
+          Catch::replaceInPlace(letters, letters, "replaced")
         </Original>
         <Expanded>
-          "{  }" == "{  }"
+          true
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+        <Original>
+          letters == "replaced"
+        </Original>
+        <Expanded>
+          "replaced" == "replaced"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="replace no chars" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+        <Original>
+          !(Catch::replaceInPlace(letters, "x", "z"))
+        </Original>
+        <Expanded>
+          !false
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(vv) == "{ 42 }"
+          letters == letters
         </Original>
         <Expanded>
-          "{ 42 }" == "{ 42 }"
+          "abcdefcg" == "abcdefcg"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="escape '" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(vv) == "{ 42, 250 }"
+          Catch::replaceInPlace(s, "'", "|'")
         </Original>
         <Expanded>
-          "{ 42, 250 }" == "{ 42, 250 }"
+          true
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="vector&lt;int> -> toString" tags="[toString][vector]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+        <Original>
+          s == "didn|'t"
+        </Original>
+        <Expanded>
+          "didn|'t" == "didn|'t"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="resolution" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res.size() == count
+      </Original>
+      <Expanded>
+        10 == 10
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res[i] == rate
+      </Original>
+      <Expanded>
+        1000.0 == 1000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res[i] == rate
+      </Original>
+      <Expanded>
+        1000.0 == 1000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res[i] == rate
+      </Original>
+      <Expanded>
+        1000.0 == 1000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res[i] == rate
+      </Original>
+      <Expanded>
+        1000.0 == 1000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res[i] == rate
+      </Original>
+      <Expanded>
+        1000.0 == 1000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res[i] == rate
+      </Original>
+      <Expanded>
+        1000.0 == 1000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res[i] == rate
+      </Original>
+      <Expanded>
+        1000.0 == 1000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res[i] == rate
+      </Original>
+      <Expanded>
+        1000.0 == 1000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        res[i] == rate
+      </Original>
+      <Expanded>
+        1000.0 == 1000 (0x<hex digits>)
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="run_for_at_least, chronometer" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        meter.runs() >= old_runs
+      </Original>
+      <Expanded>
+        1 >= 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        meter.runs() >= old_runs
+      </Original>
+      <Expanded>
+        2 >= 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        meter.runs() >= old_runs
+      </Original>
+      <Expanded>
+        4 >= 2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        meter.runs() >= old_runs
+      </Original>
+      <Expanded>
+        8 >= 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        meter.runs() >= old_runs
+      </Original>
+      <Expanded>
+        16 >= 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        meter.runs() >= old_runs
+      </Original>
+      <Expanded>
+        32 >= 16
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        meter.runs() >= old_runs
+      </Original>
+      <Expanded>
+        64 >= 32
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        meter.runs() >= old_runs
+      </Original>
+      <Expanded>
+        128 >= 64
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        Timing.elapsed >= time
+      </Original>
+      <Expanded>
+        128 ns >= 100 ns
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        Timing.result == Timing.iterations + 17
+      </Original>
+      <Expanded>
+        145 == 145
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        Timing.iterations >= time.count()
+      </Original>
+      <Expanded>
+        128 >= 100
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="run_for_at_least, int" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x >= old_x
+      </Original>
+      <Expanded>
+        1 >= 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x >= old_x
+      </Original>
+      <Expanded>
+        2 >= 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x >= old_x
+      </Original>
+      <Expanded>
+        4 >= 2
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x >= old_x
+      </Original>
+      <Expanded>
+        8 >= 4
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x >= old_x
+      </Original>
+      <Expanded>
+        16 >= 8
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x >= old_x
+      </Original>
+      <Expanded>
+        32 >= 16
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x >= old_x
+      </Original>
+      <Expanded>
+        64 >= 32
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        x >= old_x
+      </Original>
+      <Expanded>
+        128 >= 64
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        Timing.elapsed >= time
+      </Original>
+      <Expanded>
+        128 ns >= 100 ns
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        Timing.result == Timing.iterations + 17
+      </Original>
+      <Expanded>
+        145 == 145
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        Timing.iterations >= time.count()
+      </Original>
+      <Expanded>
+        128 >= 100
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="second tag" tags="[tag2]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="send a single char to INFO" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Info>
+      3
+    </Info>
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="sends information to INFO" tags="[.][failing]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      hi
+    </Info>
+    <Info>
+      i := 7
+    </Info>
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="shortened hide tags are split apart" tags="[tags]" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+    <Expression success="true" type="REQUIRE_THAT" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+      <Original>
+        tags, VectorContains("magic-tag"_catch_sr) &amp;&amp; VectorContains("."_catch_sr)
+      </Original>
+      <Expanded>
+        { ., magic-tag } ( Contains: magic-tag and Contains: . )
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="splitString" tags="[string-manip]" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        splitStringRef("", ','), Equals(std::vector&lt;StringRef>())
+      </Original>
+      <Expanded>
+        {  } Equals: {  }
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        splitStringRef("abc", ','), Equals(std::vector&lt;StringRef>{"abc"})
+      </Original>
+      <Expanded>
+        { abc } Equals: { abc }
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK_THAT" filename="tests/<exe-name>/IntrospectiveTests/StringManip.tests.cpp" >
+      <Original>
+        splitStringRef("abc,def", ','), Equals(std::vector&lt;StringRef>{"abc", "def"})
+      </Original>
+      <Expanded>
+        { abc, def } Equals: { abc, def }
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="stacks unscoped info in loops" tags="[.][failing][info][unscoped]" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+    <Info>
+      Count 1 to 3...
+    </Info>
+    <Info>
+      1
+    </Info>
+    <Info>
+      2
+    </Info>
+    <Info>
+      3
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <Info>
+      Count 4 to 6...
+    </Info>
+    <Info>
+      4
+    </Info>
+    <Info>
+      5
+    </Info>
+    <Info>
+      6
+    </Info>
+    <Expression success="false" type="CHECK" filename="tests/<exe-name>/UsageTests/Message.tests.cpp" >
+      <Original>
+        false
+      </Original>
+      <Expanded>
+        false
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="std::map is convertible string" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+    <Section name="empty" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(vv) == "{  }"
+          Catch::Detail::stringify( emptyMap ) == "{  }"
         </Original>
         <Expanded>
           "{  }" == "{  }"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="single item" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(vv) == "{ 42 }"
+          Catch::Detail::stringify( map ) == "{ { \"one\", 1 } }"
         </Original>
         <Expanded>
-          "{ 42 }" == "{ 42 }"
+          "{ { "one", 1 } }" == "{ { "one", 1 } }"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="several items" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(vv) == "{ 42, 250 }"
+          Catch::Detail::stringify( map ) == "{ { \"abc\", 1 }, { \"def\", 2 }, { \"ghi\", 3 } }"
         </Original>
         <Expanded>
-          "{ 42, 250 }" == "{ 42, 250 }"
+          "{ { "abc", 1 }, { "def", 2 }, { "ghi", 3 } }"
+==
+"{ { "abc", 1 }, { "def", 2 }, { "ghi", 3 } }"
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="vector&lt;string> -> toString" tags="[toString][vector]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="std::pair&lt;int,const std::string> -> toString" tags="[pair][toString]" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(value) == "{ 34, \"xyzzy\" }"
+      </Original>
+      <Expanded>
+        "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="std::pair&lt;int,std::string> -> toString" tags="[pair][toString]" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( value ) == "{ 34, \"xyzzy\" }"
+      </Original>
+      <Expanded>
+        "{ 34, "xyzzy" }" == "{ 34, "xyzzy" }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="std::set is convertible string" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+    <Section name="empty" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(vv) == "{  }"
+          Catch::Detail::stringify( emptySet ) == "{  }"
         </Original>
         <Expanded>
           "{  }" == "{  }"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="single item" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(vv) == "{ \"hello\" }"
+          Catch::Detail::stringify( set ) == "{ \"one\" }"
         </Original>
         <Expanded>
-          "{ "hello" }" == "{ "hello" }"
+          "{ "one" }" == "{ "one" }"
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="several items" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringGeneral.tests.cpp" >
         <Original>
-          ::Catch::Detail::stringify(vv) == "{ \"hello\", \"world\" }"
+          Catch::Detail::stringify( set ) == "{ \"abc\", \"def\", \"ghi\" }"
         </Original>
         <Expanded>
-          "{ "hello", "world" }"
+          "{ "abc", "def", "ghi" }"
+==
+"{ "abc", "def", "ghi" }"
+        </Expanded>
+      </Expression>
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="std::vector&lt;std::pair&lt;std::string,int> > -> toString" tags="[pair][toString]" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringPair.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( pr ) == "{ { \"green\", 55 } }"
+      </Original>
+      <Expanded>
+        "{ { "green", 55 } }"
+==
+"{ { "green", 55 } }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="string literals of different sizes can be compared" tags="[.][failing][Tricky]" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+    <Expression success="false" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Tricky.tests.cpp" >
+      <Original>
+        std::string( "first" ) == "second"
+      </Original>
+      <Expanded>
+        "first" == "second"
+      </Expanded>
+    </Expression>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="stringify ranges" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(streamable_range{}) == "op&lt;&lt;(streamable_range)"
+      </Original>
+      <Expanded>
+        "op&lt;&lt;(streamable_range)"
+==
+"op&lt;&lt;(streamable_range)"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(stringmaker_range{}) == "stringmaker(streamable_range)"
+      </Original>
+      <Expanded>
+        "stringmaker(streamable_range)"
+==
+"stringmaker(streamable_range)"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(just_range{}) == "{ 1, 2, 3, 4 }"
+      </Original>
+      <Expanded>
+        "{ 1, 2, 3, 4 }" == "{ 1, 2, 3, 4 }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(disabled_range{}) == "{?}"
+      </Original>
+      <Expanded>
+        "{?}" == "{?}"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="stringify( has_maker )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( item ) == "StringMaker&lt;has_maker>"
+      </Original>
+      <Expanded>
+        "StringMaker&lt;has_maker>"
+==
+"StringMaker&lt;has_maker>"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="stringify( has_maker_and_operator )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( item ) == "StringMaker&lt;has_maker_and_operator>"
+      </Original>
+      <Expanded>
+        "StringMaker&lt;has_maker_and_operator>"
+==
+"StringMaker&lt;has_maker_and_operator>"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="stringify( has_neither )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(item) == "{?}"
+      </Original>
+      <Expanded>
+        "{?}" == "{?}"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="stringify( has_operator )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( item ) == "operator&lt;&lt;( has_operator )"
+      </Original>
+      <Expanded>
+        "operator&lt;&lt;( has_operator )"
+==
+"operator&lt;&lt;( has_operator )"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="stringify( has_template_operator )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( item ) == "operator&lt;&lt;( has_template_operator )"
+      </Original>
+      <Expanded>
+        "operator&lt;&lt;( has_template_operator )"
+==
+"operator&lt;&lt;( has_template_operator )"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="stringify( vectors&lt;has_maker> )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( v ) == "{ StringMaker&lt;has_maker> }"
+      </Original>
+      <Expanded>
+        "{ StringMaker&lt;has_maker> }"
+==
+"{ StringMaker&lt;has_maker> }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="stringify( vectors&lt;has_maker_and_operator> )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( v ) == "{ StringMaker&lt;has_maker_and_operator> }"
+      </Original>
+      <Expanded>
+        "{ StringMaker&lt;has_maker_and_operator> }"
+==
+"{ StringMaker&lt;has_maker_and_operator> }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="stringify( vectors&lt;has_operator> )" tags="[toString]" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringWhich.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify( v ) == "{ operator&lt;&lt;( has_operator ) }"
+      </Original>
+      <Expanded>
+        "{ operator&lt;&lt;( has_operator ) }"
+==
+"{ operator&lt;&lt;( has_operator ) }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="strlen3" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        data.str.size() == data.len
+      </Original>
+      <Expanded>
+        3 == 3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        data.str.size() == data.len
+      </Original>
+      <Expanded>
+        3 == 3
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        data.str.size() == data.len
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        data.str.size() == data.len
+      </Original>
+      <Expanded>
+        4 == 4
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="tables" tags="[generators]" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        strlen(std::get&lt;0>(data)) == static_cast&lt;size_t>(std::get&lt;1>(data))
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        strlen(std::get&lt;0>(data)) == static_cast&lt;size_t>(std::get&lt;1>(data))
+      </Original>
+      <Expanded>
+        6 == 6
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        strlen(std::get&lt;0>(data)) == static_cast&lt;size_t>(std::get&lt;1>(data))
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Generators.tests.cpp" >
+      <Original>
+        strlen(std::get&lt;0>(data)) == static_cast&lt;size_t>(std::get&lt;1>(data))
+      </Original>
+      <Expanded>
+        6 == 6
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="tags with dots in later positions are not parsed as hidden" tags="[tags]" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+      <Original>
+        testcase.tags.size() == 1
+      </Original>
+      <Expanded>
+        1 == 1
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/Tag.tests.cpp" >
+      <Original>
+        testcase.tags[0].original == "magic.tag"_catch_sr
+      </Original>
+      <Expanded>
+        magic.tag == magic.tag
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="thrown std::strings are translated" tags="[!throws][.][failing]" filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+    <Exception filename="tests/<exe-name>/UsageTests/Exception.tests.cpp" >
+      Why would you throw a std::string?
+    </Exception>
+    <OverallResult success="false"/>
+  </TestCase>
+  <TestCase name="toString on const wchar_t const pointer returns the string contents" tags="[toString]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        result == "\"wide load\""
+      </Original>
+      <Expanded>
+        ""wide load"" == ""wide load""
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="toString on const wchar_t pointer returns the string contents" tags="[toString]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        result == "\"wide load\""
+      </Original>
+      <Expanded>
+        ""wide load"" == ""wide load""
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="toString on wchar_t const pointer returns the string contents" tags="[toString]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        result == "\"wide load\""
+      </Original>
+      <Expanded>
+        ""wide load"" == ""wide load""
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="toString on wchar_t returns the string contents" tags="[toString]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        result == "\"wide load\""
+      </Original>
+      <Expanded>
+        ""wide load"" == ""wide load""
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="toString(enum class w/operator&lt;&lt;)" tags="[enum][enumClass][toString]" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(e0) == "E2/V0"
+      </Original>
+      <Expanded>
+        "E2/V0" == "E2/V0"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(e1) == "E2/V1"
+      </Original>
+      <Expanded>
+        "E2/V1" == "E2/V1"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(e3) == "Unknown enum value 10"
+      </Original>
+      <Expanded>
+        "Unknown enum value 10"
+==
+"Unknown enum value 10"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="toString(enum class)" tags="[enum][enumClass][toString]" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(e0) == "0"
+      </Original>
+      <Expanded>
+        "0" == "0"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(e1) == "1"
+      </Original>
+      <Expanded>
+        "1" == "1"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="toString(enum w/operator&lt;&lt;)" tags="[enum][toString]" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(e0) == "E2{0}"
+      </Original>
+      <Expanded>
+        "E2{0}" == "E2{0}"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(e1) == "E2{1}"
+      </Original>
+      <Expanded>
+        "E2{1}" == "E2{1}"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="toString(enum)" tags="[enum][toString]" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(e0) == "0"
+      </Original>
+      <Expanded>
+        "0" == "0"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/EnumToString.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(e1) == "1"
+      </Original>
+      <Expanded>
+        "1" == "1"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="tuple&lt;>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Original>
+        "{ }" == ::Catch::Detail::stringify(type{})
+      </Original>
+      <Expanded>
+        "{ }" == "{ }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Original>
+        "{ }" == ::Catch::Detail::stringify(value)
+      </Original>
+      <Expanded>
+        "{ }" == "{ }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="tuple&lt;float,int>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Original>
+        "1.2f" == ::Catch::Detail::stringify(float(1.2))
+      </Original>
+      <Expanded>
+        "1.2f" == "1.2f"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Original>
+        "{ 1.2f, 0 }" == ::Catch::Detail::stringify(type{1.2f,0})
+      </Original>
+      <Expanded>
+        "{ 1.2f, 0 }" == "{ 1.2f, 0 }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="tuple&lt;int>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Original>
+        "{ 0 }" == ::Catch::Detail::stringify(type{0})
+      </Original>
+      <Expanded>
+        "{ 0 }" == "{ 0 }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="tuple&lt;0,int,const char *>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Original>
+        "{ 0, 42, \"Catch me\" }" == ::Catch::Detail::stringify(value)
+      </Original>
+      <Expanded>
+        "{ 0, 42, "Catch me" }"
+==
+"{ 0, 42, "Catch me" }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="tuple&lt;string,string>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Original>
+        "{ \"hello\", \"world\" }" == ::Catch::Detail::stringify(type{"hello","world"})
+      </Original>
+      <Expanded>
+        "{ "hello", "world" }"
 ==
 "{ "hello", "world" }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="tuple&lt;tuple&lt;int>,tuple&lt;>,float>" tags="[toString][tuple]" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/UsageTests/ToStringTuple.tests.cpp" >
+      <Original>
+        "{ { 42 }, { }, 1.2f }" == ::Catch::Detail::stringify(value)
+      </Original>
+      <Expanded>
+        "{ { 42 }, { }, 1.2f }"
+==
+"{ { 42 }, { }, 1.2f }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="uniform samples" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        e.point == 23
+      </Original>
+      <Expanded>
+        23.0 == 23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        e.upper_bound == 23
+      </Original>
+      <Expanded>
+        23.0 == 23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        e.lower_bound == 23
+      </Original>
+      <Expanded>
+        23.0 == 23
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        e.confidence_interval == 0.95
+      </Original>
+      <Expanded>
+        0.95 == 0.95
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="unique_ptr reimplementation: basic functionality" tags="[internals][unique-ptr]" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+    <Section name="Default constructed unique_ptr is empty" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          !(ptr)
+        </Original>
+        <Expanded>
+          !{?}
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="vectors can be sized and resized" tags="[vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
         <Original>
-          v.size() == 5
+          ptr.get() == 0
         </Original>
         <Expanded>
-          5 == 5
+          0 == 0
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Take ownership of allocation" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          ptr
         </Original>
         <Expanded>
-          5 >= 5
+          {?}
         </Expanded>
       </Expression>
-      <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          *ptr == 0
+        </Original>
+        <Expanded>
+          0 == 0
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          ptr.get() == naked_ptr
+        </Original>
+        <Expanded>
+          0x<hex digits> == 0x<hex digits>
+        </Expanded>
+      </Expression>
+      <Section name="Plain reset deallocates" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
           <Original>
-            v.size() == 10
+            !(ptr)
           </Original>
           <Expanded>
-            10 == 10
+            !{?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
           <Original>
-            v.capacity() >= 10
+            ptr.get() == 0
           </Original>
           <Expanded>
-            10 >= 10
+            0 == 0
           </Expanded>
         </Expression>
         <OverallResults successes="2" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="5" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Take ownership of allocation" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
         <Original>
-          v.size() == 5
+          ptr
         </Original>
         <Expanded>
-          5 == 5
+          {?}
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          *ptr == 0
         </Original>
         <Expanded>
-          5 >= 5
+          0 == 0
         </Expanded>
       </Expression>
-      <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          ptr.get() == naked_ptr
+        </Original>
+        <Expanded>
+          0x<hex digits> == 0x<hex digits>
+        </Expanded>
+      </Expression>
+      <Section name="Reset replaces ownership" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
           <Original>
-            v.size() == 0
+            ptr
           </Original>
           <Expanded>
-            0 == 0
+            {?}
           </Expanded>
         </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
           <Original>
-            v.capacity() >= 5
+            ptr.get() != 0
           </Original>
           <Expanded>
-            5 >= 5
+            0x<hex digits> != 0
+          </Expanded>
+        </Expression>
+        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+          <Original>
+            *ptr == 2
+          </Original>
+          <Expanded>
+            2 == 2
           </Expanded>
         </Expression>
-        <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-            <Original>
-              v.capacity() == 0
-            </Original>
-            <Expanded>
-              0 == 0
-            </Expanded>
-          </Expression>
-          <OverallResults successes="1" failures="0" expectedFailures="0"/>
-        </Section>
         <OverallResults successes="3" failures="0" expectedFailures="0"/>
       </Section>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="6" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Release releases ownership" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="CHECK_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
         <Original>
-          v.size() == 5
+          !(ptr)
         </Original>
         <Expanded>
-          5 == 5
+          !{?}
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="CHECK" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          ptr.get() == 0
+        </Original>
+        <Expanded>
+          0 == 0
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Move constructor" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          !(ptr1)
+        </Original>
+        <Expanded>
+          !{?}
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          ptr2
+        </Original>
+        <Expanded>
+          {?}
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          *ptr2 == 1
+        </Original>
+        <Expanded>
+          1 == 1
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="Move assignment" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE_FALSE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          !(ptr2)
+        </Original>
+        <Expanded>
+          !{?}
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          ptr1
+        </Original>
+        <Expanded>
+          {?}
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          *ptr1 == 2
+        </Original>
+        <Expanded>
+          2 == 2
+        </Expanded>
+      </Expression>
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="free swap" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          *ptr1 == 2
+        </Original>
+        <Expanded>
+          2 == 2
+        </Expanded>
+      </Expression>
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/UniquePtr.tests.cpp" >
+        <Original>
+          *ptr2 == 1
+        </Original>
+        <Expanded>
+          1 == 1
         </Expanded>
       </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="vec&lt;vec&lt;string,alloc>> -> toString" tags="[toString][vector,allocator]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(v) == "{  }"
+      </Original>
+      <Expanded>
+        "{  }" == "{  }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(v) == "{ { \"hello\" }, { \"world\" } }"
+      </Original>
+      <Expanded>
+        "{ { "hello" }, { "world" } }"
+==
+"{ { "hello" }, { "world" } }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="vector&lt;bool> -> toString" tags="[containers][toString][vector]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(bools) == "{  }"
+      </Original>
+      <Expanded>
+        "{  }" == "{  }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(bools) == "{ true }"
+      </Original>
+      <Expanded>
+        "{ true }" == "{ true }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(bools) == "{ true, false }"
+      </Original>
+      <Expanded>
+        "{ true, false }" == "{ true, false }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="vector&lt;int,allocator> -> toString" tags="[toString][vector,allocator]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(vv) == "{  }"
+      </Original>
+      <Expanded>
+        "{  }" == "{  }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(vv) == "{ 42 }"
+      </Original>
+      <Expanded>
+        "{ 42 }" == "{ 42 }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(vv) == "{ 42, 250 }"
+      </Original>
+      <Expanded>
+        "{ 42, 250 }" == "{ 42, 250 }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="vector&lt;int> -> toString" tags="[toString][vector]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(vv) == "{  }"
+      </Original>
+      <Expanded>
+        "{  }" == "{  }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(vv) == "{ 42 }"
+      </Original>
+      <Expanded>
+        "{ 42 }" == "{ 42 }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(vv) == "{ 42, 250 }"
+      </Original>
+      <Expanded>
+        "{ 42, 250 }" == "{ 42, 250 }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="vector&lt;string> -> toString" tags="[toString][vector]" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(vv) == "{  }"
+      </Original>
+      <Expanded>
+        "{  }" == "{  }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(vv) == "{ \"hello\" }"
+      </Original>
+      <Expanded>
+        "{ "hello" }" == "{ "hello" }"
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/ToStringVector.tests.cpp" >
+      <Original>
+        ::Catch::Detail::stringify(vv) == "{ \"hello\", \"world\" }"
+      </Original>
+      <Expanded>
+        "{ "hello", "world" }"
+==
+"{ "hello", "world" }"
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="vectors can be sized and resized" tags="[vector]" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing bigger changes size and capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
       <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          v.capacity() >= 5
+          v.size() == 10
         </Original>
         <Expanded>
-          5 >= 5
+          10 == 10
         </Expanded>
       </Expression>
-      <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.capacity() >= 10
-          </Original>
-          <Expanded>
-            10 >= 10
-          </Expanded>
-        </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
-      </Section>
       <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          v.size() == 5
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="resizing smaller changes size but not capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+        <Original>
+          v.size() == 0
         </Original>
         <Expanded>
-          5 == 5
+          0 == 0
         </Expanded>
       </Expression>
       <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
@@ -19639,85 +19786,146 @@ loose text artifact
           5 >= 5
         </Expanded>
       </Expression>
-      <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-          <Original>
-            v.size() == 5
-          </Original>
-          <Expanded>
-            5 == 5
-          </Expanded>
-        </Expression>
+      <Section name="We can use the 'swap trick' to reset the capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
           <Original>
-            v.capacity() >= 5
+            v.capacity() == 0
           </Original>
           <Expanded>
-            5 >= 5
+            0 == 0
           </Expanded>
         </Expression>
-        <OverallResults successes="2" failures="0" expectedFailures="0"/>
+        <OverallResults successes="1" failures="0" expectedFailures="0"/>
       </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="warmup" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-        <Original>
-          (iterations * rate) > Catch::Benchmark::Detail::warmup_time.count()
-        </Original>
-        <Expanded>
-          160000000 (0x<hex digits>) > 100
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <OverallResults successes="3" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving bigger changes capacity but not size" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          (end - start) > Catch::Benchmark::Detail::warmup_time
+          v.size() == 5
         </Original>
         <Expanded>
-          310016000 ns > 100 ms
+          5 == 5
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="weighted_average_quantile" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          q1 == 14.5
-        </Original>
-        <Expanded>
-          14.5 == 14.5
-        </Expanded>
-      </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+          v.capacity() >= 10
+        </Original>
+        <Expanded>
+          10 >= 10
+        </Expanded>
+      </Expression>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.size() == 5
+      </Original>
+      <Expanded>
+        5 == 5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Original>
+        v.capacity() >= 5
+      </Original>
+      <Expanded>
+        5 >= 5
+      </Expanded>
+    </Expression>
+    <Section name="reserving smaller does not change size or capacity" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          med == 18.
+          v.size() == 5
         </Original>
         <Expanded>
-          18.0 == 18.0
+          5 == 5
         </Expanded>
       </Expression>
-      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
         <Original>
-          q3 == 23.
+          v.capacity() >= 5
         </Original>
         <Expanded>
-          23.0 == 23.0
+          5 >= 5
         </Expanded>
       </Expression>
-      <OverallResult success="true"/>
-    </TestCase>
-    <TestCase name="xmlentitycheck" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-      <Section name="embedded xml: &lt;test>it should be possible to embed xml characters, such as &lt;, &quot; or &amp;, or even whole &lt;xml>documents&lt;/xml> within an attribute&lt;/test>" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <Section name="encoded chars: these should all be encoded: &amp;&amp;&amp;&quot;&quot;&quot;&lt;&lt;&lt;&amp;&quot;&lt;&lt;&amp;&quot;" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
-        <OverallResults successes="1" failures="0" expectedFailures="0"/>
-      </Section>
-      <OverallResult success="true"/>
-    </TestCase>
-    <OverallResults successes="1936" failures="149" expectedFailures="21"/>
-    <OverallResultsCases successes="266" failures="86" expectedFailures="4"/>
-  </Group>
-  <OverallResults successes="1936" failures="148" expectedFailures="21"/>
-  <OverallResultsCases successes="266" failures="86" expectedFailures="4"/>
-</Catch>
+      <OverallResults successes="2" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="warmup" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        (iterations * rate) > Catch::Benchmark::Detail::warmup_time.count()
+      </Original>
+      <Expanded>
+        160000000 (0x<hex digits>) > 100
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        (end - start) > Catch::Benchmark::Detail::warmup_time
+      </Original>
+      <Expanded>
+        310016000 ns > 100 ms
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="weighted_average_quantile" tags="[benchmark]" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        q1 == 14.5
+      </Original>
+      <Expanded>
+        14.5 == 14.5
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        med == 18.
+      </Original>
+      <Expanded>
+        18.0 == 18.0
+      </Expanded>
+    </Expression>
+    <Expression success="true" type="REQUIRE" filename="tests/<exe-name>/IntrospectiveTests/InternalBenchmark.tests.cpp" >
+      <Original>
+        q3 == 23.
+      </Original>
+      <Expanded>
+        23.0 == 23.0
+      </Expanded>
+    </Expression>
+    <OverallResult success="true"/>
+  </TestCase>
+  <TestCase name="xmlentitycheck" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+    <Section name="embedded xml: &lt;test>it should be possible to embed xml characters, such as &lt;, &quot; or &amp;, or even whole &lt;xml>documents&lt;/xml> within an attribute&lt;/test>" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <Section name="encoded chars: these should all be encoded: &amp;&amp;&amp;&quot;&quot;&quot;&lt;&lt;&lt;&amp;&quot;&lt;&lt;&amp;&quot;" filename="tests/<exe-name>/UsageTests/Misc.tests.cpp" >
+      <OverallResults successes="1" failures="0" expectedFailures="0"/>
+    </Section>
+    <OverallResult success="true"/>
+  </TestCase>
+  <OverallResults successes="1951" failures="146" expectedFailures="23"/>
+  <OverallResultsCases successes="276" failures="86" expectedFailures="6"/>
+</Catch2TestRun>
diff --git a/packages/Catch2/tests/SelfTest/CompileTimePerfTests/10.tests.cpp b/packages/Catch2/tests/SelfTest/CompileTimePerfTests/10.tests.cpp
deleted file mode 100644
index 01cd072d9fbd307f749290f1b0a33d6564c5e348..0000000000000000000000000000000000000000
--- a/packages/Catch2/tests/SelfTest/CompileTimePerfTests/10.tests.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// Include set of usage tests multiple times - for compile-time performance testing
-// (do not run)
-
-#include "All.tests.cpp"
-#include "All.tests.cpp"
-#include "All.tests.cpp"
-#include "All.tests.cpp"
-#include "All.tests.cpp"
-#include "All.tests.cpp"
-#include "All.tests.cpp"
-#include "All.tests.cpp"
-#include "All.tests.cpp"
-#include "All.tests.cpp"
diff --git a/packages/Catch2/tests/SelfTest/CompileTimePerfTests/100.tests.cpp b/packages/Catch2/tests/SelfTest/CompileTimePerfTests/100.tests.cpp
deleted file mode 100644
index e03ca838284c64d6e5bcc572c4a97d75699080ed..0000000000000000000000000000000000000000
--- a/packages/Catch2/tests/SelfTest/CompileTimePerfTests/100.tests.cpp
+++ /dev/null
@@ -1,13 +0,0 @@
-// Include set of usage tests multiple times - for compile-time performance testing
-// (do not run)
-
-#include "10.tests.cpp"
-#include "10.tests.cpp"
-#include "10.tests.cpp"
-#include "10.tests.cpp"
-#include "10.tests.cpp"
-#include "10.tests.cpp"
-#include "10.tests.cpp"
-#include "10.tests.cpp"
-#include "10.tests.cpp"
-#include "10.tests.cpp"
diff --git a/packages/Catch2/tests/SelfTest/CompileTimePerfTests/All.tests.cpp b/packages/Catch2/tests/SelfTest/CompileTimePerfTests/All.tests.cpp
deleted file mode 100644
index 2b6a1029141d1772766271b2f1ae22b83941a57e..0000000000000000000000000000000000000000
--- a/packages/Catch2/tests/SelfTest/CompileTimePerfTests/All.tests.cpp
+++ /dev/null
@@ -1,15 +0,0 @@
-// include set of usage tests into one file for compiler performance test purposes
-// This whole file can now be included multiple times in 10.tests.cpp, and *that*
-// file included multiple times (in 100.tests.cpp)
-
-// Note that the intention is only for these files to be compiled. They will
-// fail at runtime due to the re-user of test case names
-
-#include "../UsageTests/Approx.tests.cpp"
-#include "../UsageTests/BDD.tests.cpp"
-#include "../UsageTests/Class.tests.cpp"
-#include "../UsageTests/Compilation.tests.cpp"
-#include "../UsageTests/Condition.tests.cpp"
-#include "../UsageTests/Exception.tests.cpp"
-#include "../UsageTests/Matchers.tests.cpp"
-#include "../UsageTests/Misc.tests.cpp"
diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7a1ca110a265270c35125c96a8281bad23f8577d
--- /dev/null
+++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/FloatingPoint.tests.cpp
@@ -0,0 +1,66 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/catch_template_test_macros.hpp>
+#include <catch2/internal/catch_floating_point_helpers.hpp>
+
+
+TEST_CASE("convertToBits", "[floating-point][conversion]") {
+    using Catch::Detail::convertToBits;
+
+    CHECK( convertToBits( 0.f ) == 0 );
+    CHECK( convertToBits( -0.f ) == ( 1ULL << 31 ) );
+    CHECK( convertToBits( 0. ) == 0 );
+    CHECK( convertToBits( -0. ) == ( 1ULL << 63 ) );
+    CHECK( convertToBits( std::numeric_limits<float>::denorm_min() ) == 1 );
+    CHECK( convertToBits( std::numeric_limits<double>::denorm_min() ) == 1 );
+}
+
+TEMPLATE_TEST_CASE("type-shared ulpDistance tests", "[floating-point][ulp][approvals]", float, double) {
+    using FP = TestType;
+    using Catch::ulpDistance;
+
+    // Distance between zeros is zero
+    CHECK( ulpDistance( FP{}, FP{} ) == 0 );
+    CHECK( ulpDistance( FP{}, -FP{} ) == 0 );
+    CHECK( ulpDistance( -FP{}, -FP{} ) == 0 );
+
+    // Distance between same-sign infinities is zero
+    static constexpr FP infinity = std::numeric_limits<FP>::infinity();
+    CHECK( ulpDistance( infinity, infinity ) == 0 );
+    CHECK( ulpDistance( -infinity, -infinity ) == 0 );
+
+    // Distance between max-finite-val and same sign infinity is 1
+    static constexpr FP max_finite = std::numeric_limits<FP>::max();
+    CHECK( ulpDistance( max_finite, infinity ) == 1 );
+    CHECK( ulpDistance( -max_finite, -infinity ) == 1 );
+
+    // Distance between X and 0 is half of distance between X and -X
+    CHECK( ulpDistance( -infinity, infinity ) ==
+           2 * ulpDistance( infinity, FP{} ) );
+    CHECK( 2 * ulpDistance( FP{ -2. }, FP{} ) ==
+           ulpDistance( FP{ -2. }, FP{ 2. } ) );
+    CHECK( 2 * ulpDistance( FP{ 2. }, FP{} ) ==
+           ulpDistance( FP{ -2. }, FP{ 2. } ) );
+
+    // Denorms are supported
+    CHECK( ulpDistance( std::numeric_limits<FP>::denorm_min(), FP{} ) == 1 );
+    CHECK( ulpDistance( std::numeric_limits<FP>::denorm_min(), -FP{} ) == 1 );
+    CHECK( ulpDistance( -std::numeric_limits<FP>::denorm_min(), FP{} ) == 1 );
+    CHECK( ulpDistance( -std::numeric_limits<FP>::denorm_min(), -FP{} ) == 1 );
+    CHECK( ulpDistance( std::numeric_limits<FP>::denorm_min(),
+                        -std::numeric_limits<FP>::denorm_min() ) == 2 );
+
+    // Machine epsilon
+    CHECK( ulpDistance( FP{ 1. },
+                        FP{ 1. } + std::numeric_limits<FP>::epsilon() ) == 1 );
+    CHECK( ulpDistance( -FP{ 1. },
+                        -FP{ 1. } - std::numeric_limits<FP>::epsilon() ) == 1 );
+}
+
+TEST_CASE("UlpDistance", "[floating-point][ulp][approvals]") {
+    using Catch::ulpDistance;
+
+    CHECK( ulpDistance( 1., 2. ) == 0x10'00'00'00'00'00'00 );
+    CHECK( ulpDistance( -2., 2. ) == 0x80'00'00'00'00'00'00'00 );
+    CHECK( ulpDistance( 1.f, 2.f ) == 0x80'00'00 );
+    CHECK( ulpDistance( -2.f, 2.f ) == 0x80'00'00'00 );
+}
diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp
index 0a3895a854c4bb5a6e71872af5421c87ea333b70..19fa8f9531999931a51e34b5d7bd6139d1d17410 100644
--- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/InternalBenchmark.tests.cpp
@@ -412,3 +412,29 @@ TEST_CASE("run benchmark", "[benchmark][approvals]") {
 
     CHECK((end - start).count() == 2867251000);
 }
+
+TEST_CASE("Failing benchmarks", "[!benchmark][.approvals]") {
+    SECTION("empty", "Benchmark that has been optimized away (because it is empty)") {
+        BENCHMARK("Empty benchmark") {};
+    }
+    SECTION("throw", "Benchmark that throws an exception") {
+        BENCHMARK("Throwing benchmark") {
+            throw "just a plain literal, bleh";
+        };
+    }
+    SECTION("assert", "Benchmark that asserts inside") {
+        BENCHMARK("Asserting benchmark") {
+            REQUIRE(1 == 2);
+        };
+    }
+    SECTION("fail", "Benchmark that fails inside") {
+        BENCHMARK("FAIL'd benchmark") {
+            FAIL("This benchmark only fails, nothing else");
+        };
+    }
+}
+
+TEST_CASE( "Failing benchmark respects should-fail",
+           "[!shouldfail][!benchmark][.approvals]" ) {
+    BENCHMARK( "Asserting benchmark" ) { REQUIRE( 1 == 2 ); };
+}
diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp
index dbc8190b3706d3c073e17f42e287650674344d46..4b9cc0e3668690a52ee1c6228328de93e0e2612e 100644
--- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/String.tests.cpp
@@ -10,29 +10,23 @@ TEST_CASE( "StringRef", "[Strings][StringRef]" ) {
         StringRef empty;
         REQUIRE( empty.empty() );
         REQUIRE( empty.size() == 0 );
-        REQUIRE( empty.isNullTerminated() );
-        REQUIRE( std::strcmp( empty.c_str(), "" ) == 0 );
+        REQUIRE( std::strcmp( empty.data(), "" ) == 0 );
     }
 
     SECTION( "From string literal" ) {
         StringRef s = "hello";
         REQUIRE( s.empty() == false );
         REQUIRE( s.size() == 5 );
-        REQUIRE( s.isNullTerminated() );
 
         auto rawChars = s.data();
         REQUIRE( std::strcmp( rawChars, "hello" ) == 0 );
 
-        REQUIRE_NOTHROW(s.c_str());
-        REQUIRE(s.c_str() == rawChars);
         REQUIRE(s.data() == rawChars);
     }
     SECTION( "From sub-string" ) {
         StringRef original = StringRef( "original string" ).substr(0, 8);
         REQUIRE( original == "original" );
 
-        REQUIRE_FALSE(original.isNullTerminated());
-        REQUIRE_THROWS(original.c_str());
         REQUIRE_NOTHROW(original.data());
     }
 
@@ -51,7 +45,7 @@ TEST_CASE( "StringRef", "[Strings][StringRef]" ) {
         SECTION( "non-zero-based substring") {
             ss = s.substr( 6, 6 );
             REQUIRE( ss.size() == 6 );
-            REQUIRE( std::strcmp( ss.c_str(), "world!" ) == 0 );
+            REQUIRE( std::strcmp( ss.data(), "world!" ) == 0 );
         }
 
         SECTION( "Pointer values of full refs should match" ) {
@@ -69,7 +63,7 @@ TEST_CASE( "StringRef", "[Strings][StringRef]" ) {
 
         SECTION("Substring off the end are trimmed") {
             ss = s.substr(6, 123);
-            REQUIRE(std::strcmp(ss.c_str(), "world!") == 0);
+            REQUIRE(std::strcmp(ss.data(), "world!") == 0);
         }
         SECTION("substring start after the end is empty") {
             REQUIRE(s.substr(1'000'000, 1).empty());
@@ -147,7 +141,6 @@ TEST_CASE("StringRef at compilation time", "[Strings][StringRef][constexpr]") {
 
         constexpr StringRef stringref(abc, 3);
         STATIC_REQUIRE(stringref.size() == 3);
-        STATIC_REQUIRE(stringref.isNullTerminated());
         STATIC_REQUIRE(stringref.data() == abc);
         STATIC_REQUIRE(stringref.begin() == abc);
         STATIC_REQUIRE(stringref.begin() != stringref.end());
@@ -160,19 +153,15 @@ TEST_CASE("StringRef at compilation time", "[Strings][StringRef][constexpr]") {
         STATIC_REQUIRE(shortened.size() == 2);
         STATIC_REQUIRE(shortened.data() == abc);
         STATIC_REQUIRE(shortened.begin() != shortened.end());
-        STATIC_REQUIRE_FALSE(shortened.isNullTerminated());
-        STATIC_REQUIRE_FALSE(shortened.substr(1, 3).isNullTerminated());
     }
     SECTION("UDL construction") {
         constexpr auto sr1 = "abc"_catch_sr;
         STATIC_REQUIRE_FALSE(sr1.empty());
         STATIC_REQUIRE(sr1.size() == 3);
-        STATIC_REQUIRE(sr1.isNullTerminated());
 
         using Catch::operator"" _sr;
         constexpr auto sr2 = ""_sr;
         STATIC_REQUIRE(sr2.empty());
         STATIC_REQUIRE(sr2.size() == 0);
-        STATIC_REQUIRE(sr2.isNullTerminated());
     }
 }
diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp
index 15d37c98b67c9929fe95a75ccca0506886f55b4b..a74b31599e0e99f1ddeb030da14fc6a712d4c008 100644
--- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Tag.tests.cpp
@@ -39,14 +39,33 @@ TEST_CASE( "Tag alias can be registered against tag patterns" ) {
     }
 }
 
-TEST_CASE("shortened hide tags are split apart") {
+// Dummy line info for creating dummy test cases below
+constexpr Catch::SourceLineInfo dummySourceLineInfo = CATCH_INTERNAL_LINEINFO;
+
+TEST_CASE("shortened hide tags are split apart", "[tags]") {
     using Catch::StringRef;
     using Catch::Matchers::VectorContains;
-    auto testcase = Catch::makeTestCaseInfo("", {"fake test name", "[.magic-tag]"}, CATCH_INTERNAL_LINEINFO);
-    // Transform ...
+    Catch::TestCaseInfo testcase("", {"fake test name", "[.magic-tag]"}, dummySourceLineInfo);
+
+    // Extract parsed tags into strings
     std::vector<StringRef> tags;
-    for (auto const& tag : testcase->tags) {
-        tags.push_back(tag.original);
+    for (auto const& tag : testcase.tags) {
+        tags.push_back(tag.lowerCased);
     }
     REQUIRE_THAT(tags, VectorContains("magic-tag"_catch_sr) && VectorContains("."_catch_sr));
 }
+
+TEST_CASE("tags with dots in later positions are not parsed as hidden", "[tags]") {
+    using Catch::StringRef;
+    using Catch::Matchers::VectorContains;
+    Catch::TestCaseInfo testcase("", { "fake test name", "[magic.tag]" }, dummySourceLineInfo);
+
+    REQUIRE(testcase.tags.size() == 1);
+    REQUIRE(testcase.tags[0].original == "magic.tag"_catch_sr);
+}
+
+TEST_CASE( "empty tags are not allowed", "[tags]" ) {
+    REQUIRE_THROWS(
+        Catch::TestCaseInfo("", { "test with an empty tag", "[]" }, dummySourceLineInfo)
+    );
+}
diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/ToString.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/ToString.tests.cpp
index 47dfdd37f6ae9c1a71e17df297a66b59df2991d6..c0415dc329a13fff0c1d92670d1e0b89ca4fda32 100644
--- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/ToString.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/ToString.tests.cpp
@@ -1,6 +1,7 @@
 #include <catch2/internal/catch_enum_values_registry.hpp>
 #include <catch2/matchers/catch_matchers_vector.hpp>
 #include <catch2/catch_test_macros.hpp>
+#include <catch2/catch_template_test_macros.hpp>
 
 enum class EnumClass3 { Value1, Value2, Value3, Value4 };
 
@@ -50,4 +51,39 @@ TEST_CASE( "Directly creating an EnumInfo" ) {
 
 TEST_CASE("Range type with sentinel") {
     CHECK( Catch::Detail::stringify(UsesSentinel{}) == "{  }" );
-}
\ No newline at end of file
+}
+
+TEST_CASE("convertIntoString stringification helper", "[toString][approvals]") {
+    using namespace std::string_literals;
+    using Catch::Detail::convertIntoString;
+    using namespace Catch;
+
+    SECTION("No escaping") {
+        CHECK(convertIntoString(""_sr, false) == R"("")"s);
+        CHECK(convertIntoString("abcd"_sr, false) == R"("abcd")"s);
+        CHECK(convertIntoString("ab\ncd"_sr, false) == "\"ab\ncd\""s);
+        CHECK(convertIntoString("ab\r\ncd"_sr, false) == "\"ab\r\ncd\""s);
+        CHECK(convertIntoString("ab\"cd"_sr, false) == R"("ab"cd")"s);
+    }
+    SECTION("Escaping invisibles") {
+        CHECK(convertIntoString(""_sr, true) == R"("")"s);
+        CHECK(convertIntoString("ab\ncd"_sr, true) == R"("ab\ncd")"s);
+        CHECK(convertIntoString("ab\r\ncd"_sr, true) == R"("ab\r\ncd")"s);
+        CHECK(convertIntoString("ab\tcd"_sr, true) == R"("ab\tcd")"s);
+        CHECK(convertIntoString("ab\fcd"_sr, true) == R"("ab\fcd")"s);
+        CHECK(convertIntoString("ab\"cd"_sr, true) == R"("ab"cd")"s);
+    }
+}
+
+TEMPLATE_TEST_CASE( "Stringifying char arrays with statically known sizes",
+                    "[toString]",
+                    char,
+                    signed char,
+                    unsigned char ) {
+    using namespace std::string_literals;
+    TestType with_null_terminator[10] = "abc";
+    CHECK( ::Catch::Detail::stringify( with_null_terminator ) == R"("abc")"s );
+
+    TestType no_null_terminator[3] = { 'a', 'b', 'c' };
+    CHECK( ::Catch::Detail::stringify( no_null_terminator ) == R"("abc")"s );
+}
diff --git a/packages/Catch2/tests/SelfTest/IntrospectiveTests/Xml.tests.cpp b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Xml.tests.cpp
index 2d375a22918a69097a3a24df6eda569a18a00bb3..c98d66ef2a87ce52f439d7aa9fe86202c2863d25 100644
--- a/packages/Catch2/tests/SelfTest/IntrospectiveTests/Xml.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/IntrospectiveTests/Xml.tests.cpp
@@ -2,6 +2,9 @@
 #include <catch2/internal/catch_xmlwriter.hpp>
 
 #include <catch2/internal/catch_stream.hpp>
+#include <catch2/matchers/catch_matchers_string.hpp>
+
+#include <sstream>
 
 static std::string encode( std::string const& str, Catch::XmlEncode::ForWhat forWhat = Catch::XmlEncode::ForTextNodes ) {
     Catch::ReusableStringStream oss;
@@ -112,3 +115,19 @@ TEST_CASE("XmlEncode: UTF-8", "[XML][UTF-8][approvals]") {
     }
 #undef ESC
 }
+
+TEST_CASE("XmlWriter writes boolean attributes as true/false", "[XML][XmlWriter]") {
+    using Catch::Matchers::Contains;
+    std::stringstream stream;
+    {
+        Catch::XmlWriter xml(stream);
+
+        xml.scopedElement("Element1")
+            .writeAttribute("attr1", true)
+            .writeAttribute("attr2", false);
+    }
+
+    REQUIRE_THAT( stream.str(),
+                  Contains(R"(attr1="true")") &&
+                  Contains(R"(attr2="false")") );
+}
diff --git a/packages/Catch2/tests/SelfTest/TestRegistrations.cpp b/packages/Catch2/tests/SelfTest/TestRegistrations.cpp
index cb8376bdb51f399e79f3d7861e0d250c1d4e130e..45a01c027289f52d0c1da305ef45c9600057e983 100644
--- a/packages/Catch2/tests/SelfTest/TestRegistrations.cpp
+++ b/packages/Catch2/tests/SelfTest/TestRegistrations.cpp
@@ -1,14 +1,21 @@
-/*
- *  Distributed under the Boost Software License, Version 1.0. (See accompanying
- *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- */
+
+//              Copyright Catch2 Authors
+// Distributed under the Boost Software License, Version 1.0.
+//   (See accompanying file LICENSE_1_0.txt or copy at
+//        https://www.boost.org/LICENSE_1_0.txt)
+
+// SPDX-License-Identifier: BSL-1.0
 
 #include <catch2/catch_tag_alias_autoregistrar.hpp>
 #include <catch2/reporters/catch_reporter_event_listener.hpp>
+#include <catch2/internal/catch_enforce.hpp>
+#include <catch2/catch_test_case_info.hpp>
+#include <catch2/catch_reporter_registrars.hpp>
+
 
 // Some example tag aliases
-CATCH_REGISTER_TAG_ALIAS( "[@nhf]", "[failing]~[.]" )
-CATCH_REGISTER_TAG_ALIAS( "[@tricky]", "[tricky]~[.]" )
+CATCH_REGISTER_TAG_ALIAS("[@nhf]", "[failing]~[.]")
+CATCH_REGISTER_TAG_ALIAS("[@tricky]", "[tricky]~[.]")
 
 #ifdef __clang__
 #   pragma clang diagnostic ignored "-Wpadded"
@@ -16,11 +23,155 @@ CATCH_REGISTER_TAG_ALIAS( "[@tricky]", "[tricky]~[.]" )
 #   pragma clang diagnostic ignored "-Wc++98-compat"
 #endif
 
+/**
+ * Event listener that internally counts and validates received events.
+ *
+ * Currently only performs validation by counting received events, rather
+ * than performing full matching. This means that it won't fail if the *Ended
+ * events are provided in wrong order, as long as they come in the right amount
+ * and with the right nesting.
+ */
+class ValidatingTestListener : public Catch::EventListenerBase {
+    struct EventCounter {
+        int starting = 0;
+        int ended = 0;
+
+        bool hasActiveEvent() const {
+            return starting > ended;
+        }
+        bool hasSingleActiveEvent() const {
+            return starting - 1 == ended;
+        }
+        bool allEventsEnded() const {
+            return starting == ended;
+        }
+    };
+
+public:
+    ValidatingTestListener(Catch::ReporterConfig const& config) :
+        EventListenerBase(config) {
+        m_preferences.shouldReportAllAssertions = true;
+    }
+
+    void testRunStarting( Catch::TestRunInfo const& ) override {
+        CATCH_ENFORCE( m_testRunCounter.starting == 0,
+                       "Test run can only start once" );
+        ++m_testRunCounter.starting;
+    }
+    void testCaseStarting(Catch::TestCaseInfo const&) override {
+        CATCH_ENFORCE( m_testRunCounter.hasActiveEvent(),
+                       "Test case can only be started if the test run has already started" );
+        CATCH_ENFORCE( m_testCaseCounter.allEventsEnded(),
+                       "Test case cannot start if there is an unfinished one" );
+
+        ++m_testCaseCounter.starting;
+
+        // Reset the part tracking for partial test case events
+        m_lastSeenPartNumber = -1;
+    }
+
+    void testCasePartialStarting(Catch::TestCaseInfo const&,
+                                 uint64_t partNumber) override {
+        CATCH_ENFORCE( m_testCaseCounter.hasSingleActiveEvent(),
+                       "Test case can only be partially started if the test case has fully started already" );
+        CATCH_ENFORCE( m_lastSeenPartNumber + 1 == partNumber,
+                       "Partial test case started out of order" );
+
+        ++m_testCasePartialCounter.starting;
+        m_lastSeenPartNumber = partNumber;
+    }
+
+    void sectionStarting(Catch::SectionInfo const&) override {
+        CATCH_ENFORCE( m_testCaseCounter.hasSingleActiveEvent(),
+                       "Section can only start in a test case" );
+        CATCH_ENFORCE( m_testCasePartialCounter.hasSingleActiveEvent(),
+                       "Section can only start in a test case" );
+
+        ++m_sectionCounter.starting;
+    }
+
+    void assertionStarting(Catch::AssertionInfo const&) override {
+        CATCH_ENFORCE( m_testCaseCounter.hasSingleActiveEvent(),
+                       "Assertion can only start if test case is started" );
 
-struct TestListener : Catch::EventListenerBase {
-    using EventListenerBase::EventListenerBase;
+        ++m_assertionCounter.starting;
+    }
+    void assertionEnded(Catch::AssertionStats const&) override {
+        // todo:
+        //  * Check that assertions are balanced
+        //  * Check that assertions has started
+        ++m_assertionCounter.ended;
+    }
+
+    void sectionEnded(Catch::SectionStats const&) override {
+        CATCH_ENFORCE( m_sectionCounter.hasActiveEvent(),
+                       "Section ended without corresponding start" );
+        // TODO: Check that all assertions ended
+
+        ++m_sectionCounter.ended;
+    }
+
+
+    void testCasePartialEnded(Catch::TestCaseStats const&,
+                              uint64_t partNumber) override {
+        CATCH_ENFORCE( m_lastSeenPartNumber == partNumber,
+                       "Partial test case ended out of order" );
+        CATCH_ENFORCE( m_testCasePartialCounter.hasSingleActiveEvent(),
+                       "Partial test case ended without corresponding start" );
+        CATCH_ENFORCE( m_sectionCounter.allEventsEnded(),
+                       "Partial test case ended with unbalanced sections" );
+        // TODO: Check that all assertions ended
+
+        ++m_testCasePartialCounter.ended;
+    }
+
+
+    void testCaseEnded(Catch::TestCaseStats const&) override {
+        CATCH_ENFORCE( m_testCaseCounter.hasSingleActiveEvent(),
+                       "Test case end is not matched with test case start" );
+        CATCH_ENFORCE( m_testCasePartialCounter.allEventsEnded(),
+                       "A partial test case has not ended" );
+        CATCH_ENFORCE( m_sectionCounter.allEventsEnded(),
+                       "Test case ended with unbalanced sections" );
+
+        // TODO: Check that all assertions ended
+
+        ++m_testCaseCounter.ended;
+    }
+    void testRunEnded( Catch::TestRunStats const& ) override {
+        CATCH_ENFORCE( m_testRunCounter.hasSingleActiveEvent(),
+                       "Test run end is not matched with test run start" );
+        CATCH_ENFORCE( m_testRunCounter.ended == 0,
+                       "Test run can only end once" );
+
+        ++m_testRunCounter.ended;
+    }
+
+    ~ValidatingTestListener() override;
+
+private:
+    EventCounter m_testRunCounter;
+    EventCounter m_testCaseCounter;
+    EventCounter m_testCasePartialCounter;
+    uint64_t m_lastSeenPartNumber = 0;
+    EventCounter m_sectionCounter;
+    EventCounter m_assertionCounter;
 };
 
-#include <catch2/catch_reporter_registrars.hpp>
 
-CATCH_REGISTER_LISTENER( TestListener )
+ValidatingTestListener::~ValidatingTestListener() {
+    // Throwing from noexcept destructor terminates, but we don't mind
+    // because this is test-only check and we don't need to try and recover
+    // from assumption violation here.
+
+    CATCH_ENFORCE( m_testRunCounter.ended < 2,
+                   "Test run should be started at most once" );
+    CATCH_ENFORCE( m_testRunCounter.allEventsEnded(),
+                   "The test run has not finished" );
+    CATCH_ENFORCE( m_testCaseCounter.allEventsEnded(),
+                   "A test case did not finish" );
+
+    // TODO: other counters being balanced?
+}
+
+CATCH_REGISTER_LISTENER( ValidatingTestListener )
diff --git a/packages/Catch2/tests/SelfTest/TimingTests/Sleep.tests.cpp b/packages/Catch2/tests/SelfTest/TimingTests/Sleep.tests.cpp
index d8b958beda5b317be89a426fa2f663b5b6266875..1c82e0e912b1249c613b80118d7d19bff2da05c2 100644
--- a/packages/Catch2/tests/SelfTest/TimingTests/Sleep.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/TimingTests/Sleep.tests.cpp
@@ -16,8 +16,8 @@ TEST_CASE( "sleep_for_100ms", "[.min_duration_test][approvals]" )
   CHECK( true );
 }
 
-TEST_CASE( "sleep_for_250ms", "[.min_duration_test][approvals]" )
+TEST_CASE( "sleep_for_1000ms", "[.min_duration_test][approvals]" )
 {
-  std::this_thread::sleep_for( std::chrono::milliseconds( 250 ) );
+  std::this_thread::sleep_for( std::chrono::milliseconds( 1'000 ) );
   CHECK( true );
 }
diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Approx.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Approx.tests.cpp
index f0d8919c4f132ebfc7f52efb9c8fa86ac7f96e7b..1ae5fe04e6a903a01c337f954d04f655d12383df 100644
--- a/packages/Catch2/tests/SelfTest/UsageTests/Approx.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/UsageTests/Approx.tests.cpp
@@ -13,13 +13,9 @@
 
 using Catch::Approx;
 
-namespace { namespace ApproxTests {
-
-#ifndef APPROX_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
-#define APPROX_TEST_HELPERS_INCLUDED
-
-    inline double divide( double a, double b ) {
-        return a/b;
+namespace {
+    static double divide(double a, double b) {
+        return a / b;
     }
 
     class StrongDoubleTypedef {
@@ -30,11 +26,10 @@ namespace { namespace ApproxTests {
         explicit operator double() const { return d_; }
     };
 
-    inline std::ostream& operator<<( std::ostream& os, StrongDoubleTypedef td ) {
+    static std::ostream& operator<<(std::ostream& os, StrongDoubleTypedef td) {
         return os << "StrongDoubleTypedef(" << static_cast<double>(td) << ")";
     }
-
-#endif
+} // end unnamed namespace
 
 using namespace Catch::literals;
 
@@ -215,4 +210,9 @@ TEST_CASE( "Comparison with explicitly convertible types", "[Approx]" )
 
 }
 
-}} // namespace ApproxTests
+TEST_CASE("Approx::operator() is const correct", "[Approx][.approvals]") {
+    const Approx ap = Approx(0.0).margin(0.01);
+
+    // As long as this compiles, the test should be considered passing
+    REQUIRE(1.0 == ap(1.0));
+}
diff --git a/packages/Catch2/tests/SelfTest/UsageTests/BDD.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/BDD.tests.cpp
index 131340fa1906c1de8f634a60a69a1175568f8a71..eba58d382c16c00f99fededa8665631e42db5599 100644
--- a/packages/Catch2/tests/SelfTest/UsageTests/BDD.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/UsageTests/BDD.tests.cpp
@@ -5,103 +5,99 @@
 
 #include <catch2/catch_test_macros.hpp>
 
-namespace { namespace BDDTests {
+namespace {
 
-#ifndef BDD_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
-#define BDD_TEST_HELPERS_INCLUDED
+    static bool itDoesThis() { return true; }
 
-    inline bool itDoesThis() { return true; }
+    static bool itDoesThat() { return true; }
 
-    inline bool itDoesThat() { return true; }
+    // a trivial fixture example to support SCENARIO_METHOD tests
+    struct Fixture {
+        Fixture(): d_counter( 0 ) {}
 
-    namespace {
+        int counter() { return d_counter++; }
 
-// a trivial fixture example to support SCENARIO_METHOD tests
-        struct Fixture {
-            Fixture()
-                    : d_counter(0) {
-            }
+        int d_counter;
+    };
 
-            int counter() {
-                return d_counter++;
-            }
+}
 
-            int d_counter;
-        };
 
-    }
-#endif
-
-    SCENARIO("Do that thing with the thing", "[Tags]") {
-        GIVEN("This stuff exists") {
-            // make stuff exist
-            AND_GIVEN("And some assumption") {
-                // Validate assumption
-                WHEN("I do this") {
-                    // do this
-                    THEN("it should do this") {
-                        REQUIRE(itDoesThis());
-                        AND_THEN("do that")REQUIRE(itDoesThat());
+SCENARIO("Do that thing with the thing", "[Tags]") {
+    GIVEN("This stuff exists") {
+        // make stuff exist
+        AND_GIVEN("And some assumption") {
+            // Validate assumption
+            WHEN("I do this") {
+                // do this
+                THEN("it should do this") {
+                    REQUIRE(itDoesThis());
+                    AND_THEN("do that") {
+                        REQUIRE(itDoesThat());
                     }
                 }
             }
         }
     }
-
-    SCENARIO("Vector resizing affects size and capacity", "[vector][bdd][size][capacity]") {
-        GIVEN("an empty vector") {
-            std::vector<int> v;
-            REQUIRE(v.size() == 0);
-
-            WHEN("it is made larger") {
-                v.resize(10);
-                THEN("the size and capacity go up") {
-                    REQUIRE(v.size() == 10);
-                    REQUIRE(v.capacity() >= 10);
-
-                    AND_WHEN("it is made smaller again") {
-                        v.resize(5);
-                        THEN("the size goes down but the capacity stays the same") {
-                            REQUIRE(v.size() == 5);
-                            REQUIRE(v.capacity() >= 10);
-                        }
+}
+
+SCENARIO( "Vector resizing affects size and capacity",
+          "[vector][bdd][size][capacity]" ) {
+    GIVEN( "an empty vector" ) {
+        std::vector<int> v;
+        REQUIRE( v.size() == 0 );
+
+        WHEN( "it is made larger" ) {
+            v.resize( 10 );
+            THEN( "the size and capacity go up" ) {
+                REQUIRE( v.size() == 10 );
+                REQUIRE( v.capacity() >= 10 );
+
+                AND_WHEN( "it is made smaller again" ) {
+                    v.resize( 5 );
+                    THEN(
+                        "the size goes down but the capacity stays the same" ) {
+                        REQUIRE( v.size() == 5 );
+                        REQUIRE( v.capacity() >= 10 );
                     }
                 }
             }
+        }
 
-            WHEN("we reserve more space") {
-                v.reserve(10);
-                THEN("The capacity is increased but the size remains the same") {
-                    REQUIRE(v.capacity() >= 10);
-                    REQUIRE(v.size() == 0);
-                }
+        WHEN( "we reserve more space" ) {
+            v.reserve( 10 );
+            THEN( "The capacity is increased but the size remains the same" ) {
+                REQUIRE( v.capacity() >= 10 );
+                REQUIRE( v.size() == 0 );
             }
         }
     }
-
-    SCENARIO("This is a really long scenario name to see how the list command deals with wrapping",
-             "[very long tags][lots][long][tags][verbose]"
-                     "[one very long tag name that should cause line wrapping writing out using the list command]"
-                     "[anotherReallyLongTagNameButThisOneHasNoObviousWrapPointsSoShouldSplitWithinAWordUsingADashCharacter]") {
-        GIVEN("A section name that is so long that it cannot fit in a single console width")WHEN(
-                    "The test headers are printed as part of the normal running of the scenario")THEN(
-                        "The, deliberately very long and overly verbose (you see what I did there?) section names must wrap, along with an indent")SUCCEED(
-                            "boo!");
+}
+
+SCENARIO("This is a really long scenario name to see how the list command deals with wrapping",
+         "[very long tags][lots][long][tags][verbose]"
+                 "[one very long tag name that should cause line wrapping writing out using the list command]"
+                 "[anotherReallyLongTagNameButThisOneHasNoObviousWrapPointsSoShouldSplitWithinAWordUsingADashCharacter]") {
+    GIVEN("A section name that is so long that it cannot fit in a single console width") {
+        WHEN("The test headers are printed as part of the normal running of the scenario") {
+            THEN("The, deliberately very long and overly verbose (you see what I did there?) section names must wrap, along with an indent") {
+                SUCCEED("boo!");
+            }
+        }
     }
-
-    SCENARIO_METHOD(Fixture,
-                    "BDD tests requiring Fixtures to provide commonly-accessed data or methods",
-                    "[bdd][fixtures]") {
-        const int before(counter());
-        GIVEN("No operations precede me") {
-            REQUIRE(before == 0);
-            WHEN("We get the count") {
-                const int after(counter());
-                THEN("Subsequently values are higher") {
-                    REQUIRE(after > before);
-                }
+}
+
+SCENARIO_METHOD(Fixture,
+                "BDD tests requiring Fixtures to provide commonly-accessed data or methods",
+                "[bdd][fixtures]") {
+    const int before(counter());
+    GIVEN("No operations precede me") {
+        REQUIRE(before == 0);
+        WHEN("We get the count") {
+            const int after(counter());
+            THEN("Subsequently values are higher") {
+                REQUIRE(after > before);
             }
         }
     }
-
-}} // namespace BDDtests
+}
diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Class.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Class.tests.cpp
index b6009e0b7cbe369aa8a5539ec8306472624f5886..94e83051b75897c48d56fbbe4e4f5daab92454d1 100644
--- a/packages/Catch2/tests/SelfTest/UsageTests/Class.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/UsageTests/Class.tests.cpp
@@ -7,68 +7,47 @@
 #include <catch2/catch_template_test_macros.hpp>
 #include <array>
 
-namespace{ namespace ClassTests {
+namespace {
 
-#ifndef CLASS_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
-#define CLASS_TEST_HELPERS_INCLUDED
+    class TestClass {
+        std::string s;
 
-class TestClass
-{
-    std::string s;
+    public:
+        TestClass(): s( "hello" ) {}
 
-public:
-    TestClass()
-    : s( "hello" )
-    {}
+        void succeedingCase() { REQUIRE( s == "hello" ); }
+        void failingCase() { REQUIRE( s == "world" ); }
+    };
 
-    void succeedingCase()
-    {
-        REQUIRE( s == "hello" );
-    }
-    void failingCase()
-    {
-        REQUIRE( s == "world" );
-    }
-};
+    struct Fixture {
+        Fixture(): m_a( 1 ) {}
 
-struct Fixture
-{
-    Fixture() : m_a( 1 ) {}
+        int m_a;
+    };
 
-    int m_a;
-};
+    template <typename T> struct Template_Fixture {
+        Template_Fixture(): m_a( 1 ) {}
 
-template< typename T >
-struct Template_Fixture {
-    Template_Fixture(): m_a(1) {}
+        T m_a;
+    };
 
-    T m_a;
-};
+    template <typename T> struct Template_Fixture_2 {
+        Template_Fixture_2() {}
 
-template<typename T>
-struct Template_Fixture_2 {
-    Template_Fixture_2() {}
+        T m_a;
+    };
 
-    T m_a;
-};
+    template <typename T> struct Template_Foo {
+        size_t size() { return 0; }
+    };
 
-template< typename T>
-struct Template_Foo {
-    size_t size() { return 0; }
-};
-
-template< typename T, size_t V>
-struct Template_Foo_2 {
-    size_t size() { return V; }
-};
-
-template <int V>
-struct Nttp_Fixture{
-    int value = V;
-};
-#endif
+    template <typename T, size_t V> struct Template_Foo_2 {
+        size_t size() { return V; }
+    };
 
+    template <int V> struct Nttp_Fixture { int value = V; };
 
+} // end unnamed namespace
 
 METHOD_AS_TEST_CASE( TestClass::succeedingCase, "A METHOD_AS_TEST_CASE based test run that succeeds", "[class]" )
 METHOD_AS_TEST_CASE( TestClass::failingCase, "A METHOD_AS_TEST_CASE based test run that fails", "[.][class][failing]" )
@@ -129,7 +108,3 @@ namespace Inner
         REQUIRE(Template_Fixture_2<TestType>{}.m_a.size() < 2);
     }
 }
-
-
-
-}} // namespace ClassTests
diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp
index 0f339cb8d472e61faf40f89c2cf1b84807884fdf..c40c09f81ad700a277838569459e0dbdde544ca3 100644
--- a/packages/Catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/UsageTests/Compilation.tests.cpp
@@ -33,60 +33,44 @@ std::ostream& operator<<(std::ostream& out, foo::helper_1403 const&) {
 
 #include <cstring>
 
-namespace { namespace CompilationTests {
-
-#ifndef COMPILATION_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
-#define COMPILATION_TEST_HELPERS_INCLUDED
-
-    // Comparison operators can return non-booleans.
-    // This is unusual, but should be supported.
-    struct logic_t {
-        logic_t operator< (logic_t) const { return {}; }
-        logic_t operator<=(logic_t) const { return {}; }
-        logic_t operator> (logic_t) const { return {}; }
-        logic_t operator>=(logic_t) const { return {}; }
-        logic_t operator==(logic_t) const { return {}; }
-        logic_t operator!=(logic_t) const { return {}; }
-        explicit operator bool() const { return true; }
-    };
-
-
-// This is a minimal example for an issue we have found in 1.7.0
-    struct foo {
-        int i;
-    };
-
-    template<typename T>
-    bool operator==(const T &val, foo f) {
-        return val == f.i;
-    }
-
-    void throws_int(bool b) {
-        if (b) {
-            throw 1;
-        }
+// Comparison operators can return non-booleans.
+// This is unusual, but should be supported.
+struct logic_t {
+    logic_t operator< (logic_t) const { return {}; }
+    logic_t operator<=(logic_t) const { return {}; }
+    logic_t operator> (logic_t) const { return {}; }
+    logic_t operator>=(logic_t) const { return {}; }
+    logic_t operator==(logic_t) const { return {}; }
+    logic_t operator!=(logic_t) const { return {}; }
+    explicit operator bool() const { return true; }
+};
+
+
+void throws_int(bool b) {
+    if (b) {
+        throw 1;
     }
+}
 
-    template<typename T>
-    bool templated_tests(T t) {
-        int a = 3;
-        REQUIRE(a == t);
-        CHECK(a == t);
-        REQUIRE_THROWS(throws_int(true));
-        CHECK_THROWS_AS(throws_int(true), int);
-        REQUIRE_NOTHROW(throws_int(false));
-        REQUIRE_THAT("aaa", Catch::Matchers::EndsWith("aaa"));
-        return true;
-    }
+template<typename T>
+bool templated_tests(T t) {
+    int a = 3;
+    REQUIRE(a == t);
+    CHECK(a == t);
+    REQUIRE_THROWS(throws_int(true));
+    CHECK_THROWS_AS(throws_int(true), int);
+    REQUIRE_NOTHROW(throws_int(false));
+    REQUIRE_THAT("aaa", Catch::Matchers::EndsWith("aaa"));
+    return true;
+}
 
-    struct A {
-    };
+struct A {};
 
-    std::ostream &operator<<(std::ostream &o, const A &) { return o << 0; }
+std::ostream &operator<<(std::ostream &o, const A &) { return o << 0; }
 
-    struct B : private A {
-        bool operator==(int) const { return true; }
-    };
+struct B : private A {
+    bool operator==(int) const { return true; }
+};
 
 #ifdef __clang__
 #pragma clang diagnostic push
@@ -98,24 +82,32 @@ namespace { namespace CompilationTests {
 #pragma GCC diagnostic ignored "-Wunused-function"
 #endif
 
-    B f();
+B f();
 
-    std::ostream g();
+std::ostream g();
 
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
 
-    template <typename, typename>
-    struct Fixture_1245 {};
+template <typename, typename>
+struct Fixture_1245 {};
 
-#endif
+// This is a minimal example for an issue we have found in 1.7.0
+struct dummy_809 {
+    int i;
+};
 
-    TEST_CASE("#809") {
-        foo f;
-        f.i = 42;
-        REQUIRE(42 == f);
-    }
+template<typename T>
+bool operator==(const T& val, dummy_809 f) {
+    return val == f.i;
+}
+
+TEST_CASE("#809") {
+    dummy_809 f;
+    f.i = 42;
+    REQUIRE(42 == f);
+}
 
 
 // ------------------------------------------------------------------
@@ -129,65 +121,63 @@ namespace { namespace CompilationTests {
 
 
 // Test containing example where original stream insertable check breaks compilation
+TEST_CASE("#872") {
+    A dummy;
+    CAPTURE(dummy);
+    B x;
+    REQUIRE (x == 4);
+}
 
+TEST_CASE("#1027: Bitfields can be captured") {
+    struct Y {
+        uint32_t v : 1;
+    };
+    Y y{ 0 };
+    REQUIRE(y.v == 0);
+    REQUIRE(0 == y.v);
+}
 
-    TEST_CASE("#872") {
-        A dummy;
-        CAPTURE(dummy);
-        B x;
-        REQUIRE (x == 4);
-    }
-
-    TEST_CASE("#1027: Bitfields can be captured") {
-        struct Y {
-            uint32_t v : 1;
-        };
-        Y y{ 0 };
-        REQUIRE(y.v == 0);
-        REQUIRE(0 == y.v);
-    }
-
-    // Comparison operators can return non-booleans.
-    // This is unusual, but should be supported.
-    TEST_CASE("#1147") {
-        logic_t t1, t2;
-        REQUIRE(t1 == t2);
-        REQUIRE(t1 != t2);
-        REQUIRE(t1 <  t2);
-        REQUIRE(t1 >  t2);
-        REQUIRE(t1 <= t2);
-        REQUIRE(t1 >= t2);
-    }
+// Comparison operators can return non-booleans.
+// This is unusual, but should be supported.
+TEST_CASE("#1147") {
+    logic_t t1, t2;
+    REQUIRE(t1 == t2);
+    REQUIRE(t1 != t2);
+    REQUIRE(t1 <  t2);
+    REQUIRE(t1 >  t2);
+    REQUIRE(t1 <= t2);
+    REQUIRE(t1 >= t2);
+}
 
-    // unsigned array
-    TEST_CASE("#1238") {
-        unsigned char uarr[] = "123";
-        CAPTURE(uarr);
-        signed char sarr[] = "456";
-        CAPTURE(sarr);
+// unsigned array
+TEST_CASE("#1238") {
+    unsigned char uarr[] = "123";
+    CAPTURE(uarr);
+    signed char sarr[] = "456";
+    CAPTURE(sarr);
 
-        REQUIRE(std::memcmp(uarr, "123", sizeof(uarr)) == 0);
-        REQUIRE(std::memcmp(sarr, "456", sizeof(sarr)) == 0);
-    }
+    REQUIRE(std::memcmp(uarr, "123", sizeof(uarr)) == 0);
+    REQUIRE(std::memcmp(sarr, "456", sizeof(sarr)) == 0);
+}
 
-    TEST_CASE_METHOD((Fixture_1245<int, int>), "#1245", "[compilation]") {
-        SUCCEED();
-    }
+TEST_CASE_METHOD((Fixture_1245<int, int>), "#1245", "[compilation]") {
+    SUCCEED();
+}
 
-    TEST_CASE("#1403", "[compilation]") {
-        ::foo::helper_1403 h1, h2;
-        REQUIRE(h1 == h2);
-    }
+TEST_CASE("#1403", "[compilation]") {
+    ::foo::helper_1403 h1, h2;
+    REQUIRE(h1 == h2);
+}
 
-    TEST_CASE("Optionally static assertions", "[compilation]") {
-        STATIC_REQUIRE( std::is_void<void>::value );
-        STATIC_REQUIRE_FALSE( std::is_void<int>::value );
-    }
+TEST_CASE("Optionally static assertions", "[compilation]") {
+    STATIC_REQUIRE( std::is_void<void>::value );
+    STATIC_REQUIRE_FALSE( std::is_void<int>::value );
+}
 
-    TEST_CASE("#1548", "[compilation]") {
-        using namespace bar;
-        REQUIRE(std::is_same<TypeList<int>, TypeList<int>>::value);
-    }
+TEST_CASE("#1548", "[compilation]") {
+    using namespace bar;
+    REQUIRE(std::is_same<TypeList<int>, TypeList<int>>::value);
+}
 
     // #925
     using signal_t = void (*) (void*);
@@ -214,17 +204,16 @@ namespace { namespace CompilationTests {
 #pragma warning(pop)
 #endif
 
-    TEST_CASE("#1319: Sections can have description (even if it is not saved", "[compilation]") {
-        SECTION("SectionName", "This is a long form section description") {
-            SUCCEED();
-        }
-    }
-
-    TEST_CASE("Lambdas in assertions") {
-        REQUIRE([]() { return true; }());
+TEST_CASE( "#1319: Sections can have description (even if it is not saved",
+               "[compilation]" ) {
+    SECTION( "SectionName", "This is a long form section description" ) {
+        SUCCEED();
     }
+}
 
-}} // namespace CompilationTests
+TEST_CASE("Lambdas in assertions") {
+    REQUIRE([]() { return true; }());
+}
 
 namespace {
     struct HasBitOperators {
@@ -259,3 +248,60 @@ TEST_CASE("Assertion macros support bit operators and bool conversions", "[compi
     REQUIRE_FALSE(lhs ^ lhs);
 }
 
+namespace {
+    struct ImmovableType {
+        ImmovableType() = default;
+
+        ImmovableType(ImmovableType const&) = delete;
+        ImmovableType& operator=(ImmovableType const&) = delete;
+        ImmovableType(ImmovableType&&) = delete;
+        ImmovableType& operator=(ImmovableType&&) = delete;
+
+        friend bool operator==(ImmovableType const&, ImmovableType const&) {
+            return true;
+        }
+    };
+}
+
+TEST_CASE("Immovable types are supported in basic assertions", "[compilation][.approvals]") {
+    REQUIRE(ImmovableType{} == ImmovableType{});
+}
+
+namespace adl {
+
+struct always_true {
+    explicit operator bool() const { return true; }
+};
+
+#define COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(op) \
+template <class T, class U> \
+auto operator op (T&&, U&&) { \
+    return always_true{}; \
+}
+
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(==)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(!=)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(<)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(>)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(<=)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(>=)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(|)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(&)
+COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR(^)
+
+#undef COMPILATION_TEST_DEFINE_UNIVERSAL_OPERATOR
+
+}
+
+TEST_CASE("ADL universal operators don't hijack expression deconstruction", "[compilation][.approvals]") {
+    REQUIRE(adl::always_true{});
+    REQUIRE(0 == adl::always_true{});
+    REQUIRE(0 != adl::always_true{});
+    REQUIRE(0 < adl::always_true{});
+    REQUIRE(0 > adl::always_true{});
+    REQUIRE(0 <= adl::always_true{});
+    REQUIRE(0 >= adl::always_true{});
+    REQUIRE(0 | adl::always_true{});
+    REQUIRE(0 & adl::always_true{});
+    REQUIRE(0 ^ adl::always_true{});
+}
diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Condition.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Condition.tests.cpp
index 7c6a1d73ef073f118032065d919b9160e4bec217..167752e18d9a710847f8e326ecef9809efca51d8 100644
--- a/packages/Catch2/tests/SelfTest/UsageTests/Condition.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/UsageTests/Condition.tests.cpp
@@ -20,31 +20,28 @@ using Catch::Approx;
 #include <limits>
 #include <cstdint>
 
-namespace { namespace ConditionTests {
-
-#ifndef CONDITION_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
-#define CONDITION_TEST_HELPERS_INCLUDED
-
-struct TestData {
-    int int_seven = 7;
-    std::string str_hello = "hello";
-    float float_nine_point_one = 9.1f;
-    double double_pi = 3.1415926535;
-};
-
-struct TestDef {
-    TestDef& operator + ( const std::string& ) {
-        return *this;
-    }
-    TestDef& operator[]( const std::string& ) {
-        return *this;
-    }
-};
-
-inline const char* returnsConstNull(){ return nullptr; }
-inline char* returnsNull(){ return nullptr; }
+namespace {
 
-#endif
+    struct TestData {
+        int int_seven = 7;
+        std::string str_hello = "hello";
+        float float_nine_point_one = 9.1f;
+        double double_pi = 3.1415926535;
+    };
+
+    struct TestDef {
+        TestDef& operator + (const std::string&) {
+            return *this;
+        }
+        TestDef& operator[](const std::string&) {
+            return *this;
+        }
+    };
+
+    static const char* returnsConstNull() { return nullptr; }
+    static char* returnsNull() { return nullptr; }
+
+} // end unnamed namespace
 
 // The "failing" tests all use the CHECK macro, which continues if the specific test fails.
 // This allows us to see all results, even if an earlier check fails
@@ -330,5 +327,3 @@ TEST_CASE( "'Not' checks that should fail", "[.][failing]" )
     CHECK( !(1 == 1) );
     CHECK_FALSE( 1 == 1 );
 }
-
-}} // namespace ConditionTests
diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Exception.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Exception.tests.cpp
index 1f6b4fa263c30fa01a6be1351d373f59df380844..206bd9c8143810f28133bbc2b80afd1b35c3c48f 100644
--- a/packages/Catch2/tests/SelfTest/UsageTests/Exception.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/UsageTests/Exception.tests.cpp
@@ -20,55 +20,50 @@
 #pragma clang diagnostic ignored "-Wunreachable-code"
 #endif
 
-namespace { namespace ExceptionTests {
+namespace {
 
-#ifndef EXCEPTION_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
-#define EXCEPTION_TEST_HELPERS_INCLUDED
+    int thisThrows() {
+        throw std::domain_error("expected exception");
+        return 1;
+    }
 
-int thisThrows() {
-    throw std::domain_error( "expected exception" );
-    return 1;
-}
+    int thisDoesntThrow() {
+        return 0;
+    }
 
-int thisDoesntThrow() {
-    return 0;
-}
+    class CustomException {
+    public:
+        explicit CustomException(const std::string& msg)
+            : m_msg(msg) {}
 
-class CustomException {
-public:
-    explicit CustomException( const std::string& msg )
-    : m_msg( msg )
-    {}
+        std::string getMessage() const {
+            return m_msg;
+        }
 
-    std::string getMessage() const {
-        return m_msg;
-    }
+    private:
+        std::string m_msg;
+    };
 
-private:
-    std::string m_msg;
-};
+    class CustomStdException : public std::exception {
+    public:
+        explicit CustomStdException(const std::string& msg)
+            : m_msg(msg) {}
+        ~CustomStdException() noexcept override {}
 
-class CustomStdException : public std::exception {
-public:
-    explicit CustomStdException( const std::string& msg )
-    : m_msg( msg )
-    {}
-    ~CustomStdException() noexcept override {}
+        std::string getMessage() const {
+            return m_msg;
+        }
 
-    std::string getMessage() const {
-        return m_msg;
-    }
+    private:
+        std::string m_msg;
+    };
 
-private:
-    std::string m_msg;
-};
+    [[noreturn]] void throwCustom() {
+        throw CustomException("custom exception - not std");
+    }
 
-[[noreturn]] void throwCustom() {
-    throw CustomException( "custom exception - not std" );
 }
 
-#endif
-
 TEST_CASE( "When checked exceptions are thrown they can be expected or unexpected", "[!throws]" ) {
     REQUIRE_THROWS_AS( thisThrows(), std::domain_error );
     REQUIRE_NOTHROW( thisDoesntThrow() );
@@ -198,8 +193,6 @@ TEST_CASE( "#748 - captures with unexpected exceptions", "[.][failing][!throws][
     }
 }
 
-}} // namespace ExceptionTests
-
 #ifdef __clang__
 #pragma clang diagnostic pop
 #endif
diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Generators.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Generators.tests.cpp
index acfef8a9bff366a63007f029d61d197b3520f31e..b2154a1a164b89b63ea6b527a0c2c35fa65e6b65 100644
--- a/packages/Catch2/tests/SelfTest/UsageTests/Generators.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/UsageTests/Generators.tests.cpp
@@ -62,12 +62,12 @@ TEST_CASE("tables", "[generators]") {
 
 // Structured bindings make the table utility much nicer to use
 TEST_CASE( "strlen2", "[approvals][generators]" ) {
-    auto [test_input, expected] = GENERATE( table<std::string, size_t>({
-            {"one", 3},
-            {"two", 3},
-            {"three", 5},
-            {"four", 4}
-        }));
+    using tuple_type = std::tuple<std::string, int>; // see above workaround
+    auto [test_input, expected] =
+        GENERATE( table<std::string, size_t>( { tuple_type{ "one", 3 },
+                                                tuple_type{ "two", 3 },
+                                                tuple_type{ "three", 5 },
+                                                tuple_type{ "four", 4 } } ) );
 
     REQUIRE( test_input.size() == expected );
 }
@@ -103,11 +103,9 @@ TEST_CASE( "strlen3", "[generators]" ) {
 static auto eatCucumbers( int start, int eat ) -> int { return start-eat; }
 
 SCENARIO("Eating cucumbers", "[generators][approvals]") {
-
-    auto [start, eat, left] = GENERATE( table<int,int,int> ({
-            { 12, 5, 7 },
-            { 20, 5, 15 }
-        }));
+    using tuple_type = std::tuple<int, int, int>;
+    auto [start, eat, left] = GENERATE( table<int, int, int>(
+        { tuple_type{ 12, 5, 7 }, tuple_type{ 20, 5, 15 } } ) );
 
     GIVEN( "there are " << start << " cucumbers" )
     WHEN( "I eat " << eat << " cucumbers" )
diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp
index 935434344d22f70e507a297ece25640a648e833f..6f83279f33d3d2c71aa88fed7fce94fd51329f2a 100644
--- a/packages/Catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/UsageTests/Matchers.tests.cpp
@@ -4,6 +4,7 @@
  */
 
 #include <catch2/catch_test_macros.hpp>
+#include <catch2/catch_template_test_macros.hpp>
 #include <catch2/matchers/catch_matchers_exception.hpp>
 #include <catch2/matchers/catch_matchers_floating_point.hpp>
 #include <catch2/matchers/catch_matchers_predicate.hpp>
@@ -12,41 +13,37 @@
 #include <catch2/matchers/catch_matchers_templated.hpp>
 
 #include <algorithm>
+#include <exception>
 #include <cmath>
 #include <list>
 #include <sstream>
 
 #ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wweak-vtables"
-#pragma clang diagnostic ignored "-Wpadded"
+#    pragma clang diagnostic push
+#    pragma clang diagnostic ignored "-Wweak-vtables"
+#    pragma clang diagnostic ignored "-Wpadded"
 #endif
 
-namespace { namespace MatchersTests {
-
-#ifndef MATCHERS_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
-#define MATCHERS_TEST_HELPERS_INCLUDED
+namespace {
 
-    inline const char *testStringForMatching() {
+    static const char* testStringForMatching() {
         return "this string contains 'abc' as a substring";
     }
 
-    inline const char *testStringForMatching2() {
+    static const char* testStringForMatching2() {
         return "some completely different text that contains one common word";
     }
 
-    inline bool alwaysTrue(int) { return true; }
-    inline bool alwaysFalse(int) { return false; }
-
+    static bool alwaysTrue( int ) { return true; }
+    static bool alwaysFalse( int ) { return false; }
 
 #ifdef _MSC_VER
-#pragma warning(disable:4702) // Unreachable code -- MSVC 19 (VS 2015) sees right through the indirection
+#    pragma warning( disable : 4702 ) // Unreachable code -- MSVC 19 (VS 2015)
+                                      // sees right through the indirection
 #endif
 
-#include <exception>
-
     struct SpecialException : std::exception {
-        SpecialException(int i_) : i(i_) {}
+        SpecialException( int i_ ): i( i_ ) {}
 
         char const* what() const noexcept override {
             return "SpecialException::what";
@@ -61,29 +58,26 @@ namespace { namespace MatchersTests {
         }
     };
 
-    void doesNotThrow() {}
+    static void doesNotThrow() {}
 
-    [[noreturn]]
-    void throwsSpecialException(int i) {
-        throw SpecialException{i};
+    [[noreturn]] static void throwsSpecialException( int i ) {
+        throw SpecialException{ i };
     }
 
-    [[noreturn]]
-    void throwsAsInt(int i) {
-        throw i;
-    }
+    [[noreturn]] static void throwsAsInt( int i ) { throw i; }
 
-    [[noreturn]]
-    void throwsDerivedException() {
+    [[noreturn]] static void throwsDerivedException() {
         throw DerivedException{};
     }
 
-    class ExceptionMatcher : public Catch::Matchers::MatcherBase<SpecialException> {
+    class ExceptionMatcher
+        : public Catch::Matchers::MatcherBase<SpecialException> {
         int m_expected;
+
     public:
-        ExceptionMatcher(int i) : m_expected(i) {}
+        ExceptionMatcher( int i ): m_expected( i ) {}
 
-        bool match(SpecialException const &se) const override {
+        bool match( SpecialException const& se ) const override {
             return se.i == m_expected;
         }
 
@@ -94,938 +88,1012 @@ namespace { namespace MatchersTests {
         }
     };
 
-#endif
-
     using namespace Catch::Matchers;
 
 #ifdef __DJGPP__
-    float nextafter(float from, float to)
-    {
-        return ::nextafterf(from, to);
+    static float nextafter( float from, float to ) {
+        return ::nextafterf( from, to );
     }
 
-    double nextafter(double from, double to)
-    {
-        return ::nextafter(from, to);
+    static double nextafter( double from, double to ) {
+        return ::nextafter( from, to );
     }
 #else
     using std::nextafter;
 #endif
 
-    TEST_CASE("String matchers", "[matchers]") {
-        REQUIRE_THAT(testStringForMatching(), Contains("string"));
-        REQUIRE_THAT(testStringForMatching(), Contains("string", Catch::CaseSensitive::No));
-        CHECK_THAT(testStringForMatching(), Contains("abc"));
-        CHECK_THAT(testStringForMatching(), Contains("aBC", Catch::CaseSensitive::No));
-
-        CHECK_THAT(testStringForMatching(), StartsWith("this"));
-        CHECK_THAT(testStringForMatching(), StartsWith("THIS", Catch::CaseSensitive::No));
-        CHECK_THAT(testStringForMatching(), EndsWith("substring"));
-        CHECK_THAT(testStringForMatching(), EndsWith(" SuBsTrInG", Catch::CaseSensitive::No));
-    }
-
-    TEST_CASE("Contains string matcher", "[.][failing][matchers]") {
-        CHECK_THAT(testStringForMatching(), Contains("not there", Catch::CaseSensitive::No));
-        CHECK_THAT(testStringForMatching(), Contains("STRING"));
-    }
-
-    TEST_CASE("StartsWith string matcher", "[.][failing][matchers]") {
-        CHECK_THAT(testStringForMatching(), StartsWith("This String"));
-        CHECK_THAT(testStringForMatching(), StartsWith("string", Catch::CaseSensitive::No));
-    }
+} // end unnamed namespace
+
+TEST_CASE( "String matchers", "[matchers]" ) {
+    REQUIRE_THAT( testStringForMatching(), Contains( "string" ) );
+    REQUIRE_THAT( testStringForMatching(),
+                  Contains( "string", Catch::CaseSensitive::No ) );
+    CHECK_THAT( testStringForMatching(), Contains( "abc" ) );
+    CHECK_THAT( testStringForMatching(),
+                Contains( "aBC", Catch::CaseSensitive::No ) );
+
+    CHECK_THAT( testStringForMatching(), StartsWith( "this" ) );
+    CHECK_THAT( testStringForMatching(),
+                StartsWith( "THIS", Catch::CaseSensitive::No ) );
+    CHECK_THAT( testStringForMatching(), EndsWith( "substring" ) );
+    CHECK_THAT( testStringForMatching(),
+                EndsWith( " SuBsTrInG", Catch::CaseSensitive::No ) );
+}
+
+TEST_CASE( "Contains string matcher", "[.][failing][matchers]" ) {
+    CHECK_THAT( testStringForMatching(),
+                Contains( "not there", Catch::CaseSensitive::No ) );
+    CHECK_THAT( testStringForMatching(), Contains( "STRING" ) );
+}
+
+TEST_CASE( "StartsWith string matcher", "[.][failing][matchers]" ) {
+    CHECK_THAT( testStringForMatching(), StartsWith( "This String" ) );
+    CHECK_THAT( testStringForMatching(),
+                StartsWith( "string", Catch::CaseSensitive::No ) );
+}
+
+TEST_CASE( "EndsWith string matcher", "[.][failing][matchers]" ) {
+    CHECK_THAT( testStringForMatching(), EndsWith( "Substring" ) );
+    CHECK_THAT( testStringForMatching(),
+                EndsWith( "this", Catch::CaseSensitive::No ) );
+}
+
+TEST_CASE( "Equals string matcher", "[.][failing][matchers]" ) {
+    CHECK_THAT( testStringForMatching(),
+                Equals( "this string contains 'ABC' as a substring" ) );
+    CHECK_THAT( testStringForMatching(),
+                Equals( "something else", Catch::CaseSensitive::No ) );
+}
+
+TEST_CASE( "Equals", "[matchers]" ) {
+    CHECK_THAT( testStringForMatching(),
+                Equals( "this string contains 'abc' as a substring" ) );
+    CHECK_THAT( testStringForMatching(),
+                Equals( "this string contains 'ABC' as a substring",
+                        Catch::CaseSensitive::No ) );
+}
+
+TEST_CASE( "Regex string matcher -- libstdc++-4.8 workaround",
+           "[matchers][approvals]" ) {
+// DJGPP has similar problem with its regex support as libstdc++ 4.8
+#ifndef __DJGPP__
+    REQUIRE_THAT( testStringForMatching(),
+                  Matches( "this string contains 'abc' as a substring" ) );
+    REQUIRE_THAT( testStringForMatching(),
+                  Matches( "this string CONTAINS 'abc' as a substring",
+                           Catch::CaseSensitive::No ) );
+    REQUIRE_THAT( testStringForMatching(),
+                  Matches( "^this string contains 'abc' as a substring$" ) );
+    REQUIRE_THAT( testStringForMatching(), Matches( "^.* 'abc' .*$" ) );
+    REQUIRE_THAT( testStringForMatching(),
+                  Matches( "^.* 'ABC' .*$", Catch::CaseSensitive::No ) );
+#endif
 
-    TEST_CASE("EndsWith string matcher", "[.][failing][matchers]") {
-        CHECK_THAT(testStringForMatching(), EndsWith("Substring"));
-        CHECK_THAT(testStringForMatching(), EndsWith("this", Catch::CaseSensitive::No));
+    REQUIRE_THAT( testStringForMatching2(),
+                  !Matches( "this string contains 'abc' as a substring" ) );
+}
+
+TEST_CASE( "Regex string matcher", "[matchers][.failing]" ) {
+    CHECK_THAT( testStringForMatching(),
+                Matches( "this STRING contains 'abc' as a substring" ) );
+    CHECK_THAT( testStringForMatching(),
+                Matches( "contains 'abc' as a substring" ) );
+    CHECK_THAT( testStringForMatching(),
+                Matches( "this string contains 'abc' as a" ) );
+}
+
+TEST_CASE( "Matchers can be (AllOf) composed with the && operator",
+           "[matchers][operators][operator&&]" ) {
+    CHECK_THAT( testStringForMatching(),
+                Contains( "string" ) && Contains( "abc" ) &&
+                    Contains( "substring" ) && Contains( "contains" ) );
+}
+
+TEST_CASE( "Matchers can be (AnyOf) composed with the || operator",
+           "[matchers][operators][operator||]" ) {
+    CHECK_THAT( testStringForMatching(),
+                Contains( "string" ) || Contains( "different" ) ||
+                    Contains( "random" ) );
+    CHECK_THAT( testStringForMatching2(),
+                Contains( "string" ) || Contains( "different" ) ||
+                    Contains( "random" ) );
+}
+
+TEST_CASE( "Matchers can be composed with both && and ||",
+           "[matchers][operators][operator||][operator&&]" ) {
+    CHECK_THAT( testStringForMatching(),
+                ( Contains( "string" ) || Contains( "different" ) ) &&
+                    Contains( "substring" ) );
+}
+
+TEST_CASE( "Matchers can be composed with both && and || - failing",
+           "[matchers][operators][operator||][operator&&][.failing]" ) {
+    CHECK_THAT( testStringForMatching(),
+                ( Contains( "string" ) || Contains( "different" ) ) &&
+                    Contains( "random" ) );
+}
+
+TEST_CASE( "Matchers can be negated (Not) with the ! operator",
+           "[matchers][operators][not]" ) {
+    CHECK_THAT( testStringForMatching(), !Contains( "different" ) );
+}
+
+TEST_CASE( "Matchers can be negated (Not) with the ! operator - failing",
+           "[matchers][operators][not][.failing]" ) {
+    CHECK_THAT( testStringForMatching(), !Contains( "substring" ) );
+}
+
+template <typename T> struct CustomAllocator : private std::allocator<T> {
+    using size_type = size_t;
+    using difference_type = ptrdiff_t;
+    using pointer = T*;
+    using const_pointer = const T*;
+    using reference = T&;
+    using const_reference = const T&;
+    using value_type = T;
+
+    template <typename U> struct rebind { using other = CustomAllocator<U>; };
+
+    using propagate_on_container_move_assignment = std::true_type;
+    using is_always_equal = std::true_type;
+
+    CustomAllocator() = default;
+
+    CustomAllocator( const CustomAllocator& other ):
+        std::allocator<T>( other ) {}
+
+    template <typename U> CustomAllocator( const CustomAllocator<U>& ) {}
+
+    ~CustomAllocator() = default;
+
+    using std::allocator<T>::allocate;
+    using std::allocator<T>::deallocate;
+};
+
+TEST_CASE( "Vector matchers", "[matchers][vector]" ) {
+    std::vector<int> v;
+    v.push_back( 1 );
+    v.push_back( 2 );
+    v.push_back( 3 );
+
+    std::vector<int> v2;
+    v2.push_back( 1 );
+    v2.push_back( 2 );
+
+    std::vector<double> v3;
+    v3.push_back( 1 );
+    v3.push_back( 2 );
+    v3.push_back( 3 );
+
+    std::vector<double> v4;
+    v4.push_back( 1 + 1e-8 );
+    v4.push_back( 2 + 1e-8 );
+    v4.push_back( 3 + 1e-8 );
+
+    std::vector<int, CustomAllocator<int>> v5;
+    v5.push_back( 1 );
+    v5.push_back( 2 );
+    v5.push_back( 3 );
+
+    std::vector<int, CustomAllocator<int>> v6;
+    v6.push_back( 1 );
+    v6.push_back( 2 );
+
+    std::vector<int> empty;
+
+    SECTION( "Contains (element)" ) {
+        CHECK_THAT( v, VectorContains( 1 ) );
+        CHECK_THAT( v, VectorContains( 2 ) );
+        CHECK_THAT( v5, ( VectorContains<int, CustomAllocator<int>>( 2 ) ) );
     }
-
-    TEST_CASE("Equals string matcher", "[.][failing][matchers]") {
-        CHECK_THAT(testStringForMatching(), Equals("this string contains 'ABC' as a substring"));
-        CHECK_THAT(testStringForMatching(), Equals("something else", Catch::CaseSensitive::No));
+    SECTION( "Contains (vector)" ) {
+        CHECK_THAT( v, Contains( v2 ) );
+        CHECK_THAT( v, Contains<int>( { 1, 2 } ) );
+        CHECK_THAT( v5,
+                    ( Contains<int, std::allocator<int>, CustomAllocator<int>>(
+                        v2 ) ) );
+
+        v2.push_back( 3 ); // now exactly matches
+        CHECK_THAT( v, Contains( v2 ) );
+
+        CHECK_THAT( v, Contains( empty ) );
+        CHECK_THAT( empty, Contains( empty ) );
+
+        CHECK_THAT( v5,
+                    ( Contains<int, std::allocator<int>, CustomAllocator<int>>(
+                        v2 ) ) );
+        CHECK_THAT( v5, Contains( v6 ) );
     }
-
-    TEST_CASE("Equals", "[matchers]") {
-        CHECK_THAT(testStringForMatching(), Equals("this string contains 'abc' as a substring"));
-        CHECK_THAT(testStringForMatching(),
-                   Equals("this string contains 'ABC' as a substring", Catch::CaseSensitive::No));
+    SECTION( "Contains (element), composed" ) {
+        CHECK_THAT( v, VectorContains( 1 ) && VectorContains( 2 ) );
     }
 
-// <regex> does not work in libstdc++ 4.8, so we have to enable these tests only when they
-// are expected to pass and cannot have them in baselines
-    TEST_CASE("Regex string matcher -- libstdc++-4.8 workaround", "[matchers][approvals]") {
-
-// This is fiiiine
-// Taken from an answer at
-// https://stackoverflow.com/questions/12530406/is-gcc-4-8-or-earlier-buggy-about-regular-expressions
-#if (!defined(__GNUC__)) || \
-      (__cplusplus >= 201103L && \
-      (!defined(__GLIBCXX__) || (__cplusplus >= 201402L) || \
-        (defined(_GLIBCXX_REGEX_DFS_QUANTIFIERS_LIMIT) || \
-          defined(_GLIBCXX_REGEX_STATE_LIMIT) || \
-             (defined(_GLIBCXX_RELEASE) && \
-             _GLIBCXX_RELEASE > 4))))
-
-// DJGPP meets the above condition but <regex> does not work properly anyway
-#ifndef __DJGPP__
-            REQUIRE_THAT(testStringForMatching(), Matches("this string contains 'abc' as a substring"));
-            REQUIRE_THAT(testStringForMatching(),
-                         Matches("this string CONTAINS 'abc' as a substring", Catch::CaseSensitive::No));
-            REQUIRE_THAT(testStringForMatching(), Matches("^this string contains 'abc' as a substring$"));
-            REQUIRE_THAT(testStringForMatching(), Matches("^.* 'abc' .*$"));
-            REQUIRE_THAT(testStringForMatching(), Matches("^.* 'ABC' .*$", Catch::CaseSensitive::No));
-#endif
-
-#endif
-
-            REQUIRE_THAT(testStringForMatching2(), !Matches("this string contains 'abc' as a substring"));
-        }
-
-        TEST_CASE("Regex string matcher", "[matchers][.failing]") {
-            CHECK_THAT(testStringForMatching(), Matches("this STRING contains 'abc' as a substring"));
-            CHECK_THAT(testStringForMatching(), Matches("contains 'abc' as a substring"));
-            CHECK_THAT(testStringForMatching(), Matches("this string contains 'abc' as a"));
-        }
-
-        TEST_CASE("Matchers can be (AllOf) composed with the && operator", "[matchers][operators][operator&&]") {
-            CHECK_THAT(testStringForMatching(),
-                       Contains("string") &&
-                       Contains("abc") &&
-                       Contains("substring") &&
-                       Contains("contains"));
-        }
-
-        TEST_CASE("Matchers can be (AnyOf) composed with the || operator", "[matchers][operators][operator||]") {
-            CHECK_THAT(testStringForMatching(), Contains("string") || Contains("different") || Contains("random"));
-            CHECK_THAT(testStringForMatching2(), Contains("string") || Contains("different") || Contains("random"));
-        }
-
-        TEST_CASE("Matchers can be composed with both && and ||", "[matchers][operators][operator||][operator&&]") {
-            CHECK_THAT(testStringForMatching(), (Contains("string") || Contains("different")) && Contains("substring"));
-        }
-
-        TEST_CASE("Matchers can be composed with both && and || - failing",
-                  "[matchers][operators][operator||][operator&&][.failing]") {
-            CHECK_THAT(testStringForMatching(), (Contains("string") || Contains("different")) && Contains("random"));
-        }
-
-        TEST_CASE("Matchers can be negated (Not) with the ! operator", "[matchers][operators][not]") {
-            CHECK_THAT(testStringForMatching(), !Contains("different"));
-        }
+    SECTION( "Equals" ) {
 
-        TEST_CASE("Matchers can be negated (Not) with the ! operator - failing",
-                  "[matchers][operators][not][.failing]") {
-            CHECK_THAT(testStringForMatching(), !Contains("substring"));
-        }
-
-        template<typename T>
-        struct CustomAllocator : private std::allocator<T>
-        {
-            using size_type = size_t;
-            using difference_type = ptrdiff_t;
-            using pointer = T*;
-            using const_pointer = const T*;
-            using reference = T&;
-            using const_reference = const T&;
-            using value_type = T;
-
-            template<typename U>
-            struct rebind
-            { using other = CustomAllocator<U>; };
-
-            using propagate_on_container_move_assignment = std::true_type;
-            using is_always_equal = std::true_type;
-
-            CustomAllocator() = default;
-
-            CustomAllocator(const CustomAllocator& other)
-                    : std::allocator<T>(other) { }
-
-            template<typename U>
-            CustomAllocator(const CustomAllocator<U>&) { }
-
-            ~CustomAllocator() = default;
-
-            using std::allocator<T>::address;
-            using std::allocator<T>::allocate;
-            using std::allocator<T>::construct;
-            using std::allocator<T>::deallocate;
-            using std::allocator<T>::max_size;
-            using std::allocator<T>::destroy;
-        };
-
-        TEST_CASE("Vector matchers", "[matchers][vector]") {
-            std::vector<int> v;
-            v.push_back(1);
-            v.push_back(2);
-            v.push_back(3);
-
-            std::vector<int> v2;
-            v2.push_back(1);
-            v2.push_back(2);
-
-            std::vector<double> v3;
-            v3.push_back(1);
-            v3.push_back(2);
-            v3.push_back(3);
-
-            std::vector<double> v4;
-            v4.push_back(1 + 1e-8);
-            v4.push_back(2 + 1e-8);
-            v4.push_back(3 + 1e-8);
-
-            std::vector<int, CustomAllocator<int>> v5;
-            v5.push_back(1);
-            v5.push_back(2);
-            v5.push_back(3);
-
-            std::vector<int, CustomAllocator<int>> v6;
-            v6.push_back(1);
-            v6.push_back(2);
-
-            std::vector<int> empty;
-
-            SECTION("Contains (element)") {
-                CHECK_THAT(v, VectorContains(1));
-                CHECK_THAT(v, VectorContains(2));
-                CHECK_THAT(v5, (VectorContains<int, CustomAllocator<int>>(2)));
-            }
-            SECTION("Contains (vector)") {
-                CHECK_THAT(v, Contains(v2));
-                CHECK_THAT(v, Contains<int>({ 1, 2 }));
-                CHECK_THAT(v5, (Contains<int, std::allocator<int>, CustomAllocator<int>>(v2)));
-
-                v2.push_back(3); // now exactly matches
-                CHECK_THAT(v, Contains(v2));
-
-                CHECK_THAT(v, Contains(empty));
-                CHECK_THAT(empty, Contains(empty));
-
-                CHECK_THAT(v5, (Contains<int, std::allocator<int>, CustomAllocator<int>>(v2)));
-                CHECK_THAT(v5, Contains(v6));
-            }
-            SECTION("Contains (element), composed") {
-                CHECK_THAT(v, VectorContains(1) && VectorContains(2));
-            }
-
-            SECTION("Equals") {
-
-                // Same vector
-                CHECK_THAT(v, Equals(v));
-
-                CHECK_THAT(empty, Equals(empty));
-
-                // Different vector with same elements
-                CHECK_THAT(v, Equals<int>({ 1, 2, 3 }));
-                v2.push_back(3);
-                CHECK_THAT(v, Equals(v2));
-
-                CHECK_THAT(v5, (Equals<int, std::allocator<int>, CustomAllocator<int>>(v2)));
-
-                v6.push_back(3);
-                CHECK_THAT(v5, Equals(v6));
-            }
-            SECTION("UnorderedEquals") {
-                CHECK_THAT(v, UnorderedEquals(v));
-                CHECK_THAT(v, UnorderedEquals<int>({ 3, 2, 1 }));
-                CHECK_THAT(empty, UnorderedEquals(empty));
-
-                auto permuted = v;
-                std::next_permutation(begin(permuted), end(permuted));
-                REQUIRE_THAT(permuted, UnorderedEquals(v));
-
-                std::reverse(begin(permuted), end(permuted));
-                REQUIRE_THAT(permuted, UnorderedEquals(v));
-
-                CHECK_THAT(v5, (UnorderedEquals<int, std::allocator<int>, CustomAllocator<int>>(permuted)));
-
-                auto v5_permuted = v5;
-                std::next_permutation(begin(v5_permuted), end(v5_permuted));
-                CHECK_THAT(v5_permuted, UnorderedEquals(v5));
-            }
-        }
+        // Same vector
+        CHECK_THAT( v, Equals( v ) );
 
-        TEST_CASE("Vector matchers that fail", "[matchers][vector][.][failing]") {
-            std::vector<int> v;
-            v.push_back(1);
-            v.push_back(2);
-            v.push_back(3);
+        CHECK_THAT( empty, Equals( empty ) );
 
-            std::vector<int> v2;
-            v2.push_back(1);
-            v2.push_back(2);
+        // Different vector with same elements
+        CHECK_THAT( v, Equals<int>( { 1, 2, 3 } ) );
+        v2.push_back( 3 );
+        CHECK_THAT( v, Equals( v2 ) );
 
-            std::vector<double> v3;
-            v3.push_back(1);
-            v3.push_back(2);
-            v3.push_back(3);
+        CHECK_THAT(
+            v5,
+            ( Equals<int, std::allocator<int>, CustomAllocator<int>>( v2 ) ) );
 
-            std::vector<double> v4;
-            v4.push_back(1.1);
-            v4.push_back(2.1);
-            v4.push_back(3.1);
-
-            std::vector<int> empty;
+        v6.push_back( 3 );
+        CHECK_THAT( v5, Equals( v6 ) );
+    }
+    SECTION( "UnorderedEquals" ) {
+        CHECK_THAT( v, UnorderedEquals( v ) );
+        CHECK_THAT( v, UnorderedEquals<int>( { 3, 2, 1 } ) );
+        CHECK_THAT( empty, UnorderedEquals( empty ) );
+
+        auto permuted = v;
+        std::next_permutation( begin( permuted ), end( permuted ) );
+        REQUIRE_THAT( permuted, UnorderedEquals( v ) );
+
+        std::reverse( begin( permuted ), end( permuted ) );
+        REQUIRE_THAT( permuted, UnorderedEquals( v ) );
+
+        CHECK_THAT(
+            v5,
+            ( UnorderedEquals<int, std::allocator<int>, CustomAllocator<int>>(
+                permuted ) ) );
+
+        auto v5_permuted = v5;
+        std::next_permutation( begin( v5_permuted ), end( v5_permuted ) );
+        CHECK_THAT( v5_permuted, UnorderedEquals( v5 ) );
+    }
+}
 
-            SECTION("Contains (element)") {
-                CHECK_THAT(v, VectorContains(-1));
-                CHECK_THAT(empty, VectorContains(1));
-            }
-            SECTION("Contains (vector)") {
-                CHECK_THAT(empty, Contains(v));
-                v2.push_back(4);
-                CHECK_THAT(v, Contains(v2));
-            }
+TEST_CASE( "Vector matchers that fail", "[matchers][vector][.][failing]" ) {
+    std::vector<int> v;
+    v.push_back( 1 );
+    v.push_back( 2 );
+    v.push_back( 3 );
 
-            SECTION("Equals") {
+    std::vector<int> v2;
+    v2.push_back( 1 );
+    v2.push_back( 2 );
 
-                CHECK_THAT(v, Equals(v2));
-                CHECK_THAT(v2, Equals(v));
-                CHECK_THAT(empty, Equals(v));
-                CHECK_THAT(v, Equals(empty));
-            }
-            SECTION("UnorderedEquals") {
-                CHECK_THAT(v, UnorderedEquals(empty));
-                CHECK_THAT(empty, UnorderedEquals(v));
+    std::vector<double> v3;
+    v3.push_back( 1 );
+    v3.push_back( 2 );
+    v3.push_back( 3 );
 
-                auto permuted = v;
-                std::next_permutation(begin(permuted), end(permuted));
-                permuted.pop_back();
-                CHECK_THAT(permuted, UnorderedEquals(v));
+    std::vector<double> v4;
+    v4.push_back( 1.1 );
+    v4.push_back( 2.1 );
+    v4.push_back( 3.1 );
 
-                std::reverse(begin(permuted), end(permuted));
-                CHECK_THAT(permuted, UnorderedEquals(v));
-            }
-        }
-
-        TEST_CASE("Exception matchers that succeed", "[matchers][exceptions][!throws]") {
-            CHECK_THROWS_MATCHES(throwsSpecialException(1), SpecialException, ExceptionMatcher{1});
-            REQUIRE_THROWS_MATCHES(throwsSpecialException(2), SpecialException, ExceptionMatcher{2});
-        }
-
-        TEST_CASE("Exception matchers that fail", "[matchers][exceptions][!throws][.failing]") {
-            SECTION("No exception") {
-                CHECK_THROWS_MATCHES(doesNotThrow(), SpecialException, ExceptionMatcher{1});
-                REQUIRE_THROWS_MATCHES(doesNotThrow(), SpecialException, ExceptionMatcher{1});
-            }
-            SECTION("Type mismatch") {
-                CHECK_THROWS_MATCHES(throwsAsInt(1), SpecialException, ExceptionMatcher{1});
-                REQUIRE_THROWS_MATCHES(throwsAsInt(1), SpecialException, ExceptionMatcher{1});
-            }
-            SECTION("Contents are wrong") {
-                CHECK_THROWS_MATCHES(throwsSpecialException(3), SpecialException, ExceptionMatcher{1});
-                REQUIRE_THROWS_MATCHES(throwsSpecialException(4), SpecialException, ExceptionMatcher{1});
-            }
-        }
+    std::vector<int> empty;
 
-        TEST_CASE("Floating point matchers: float", "[matchers][floating-point]") {
-            SECTION("Relative") {
-                REQUIRE_THAT(10.f,  WithinRel(11.1f, 0.1f));
-                REQUIRE_THAT(10.f, !WithinRel(11.2f, 0.1f));
-                REQUIRE_THAT( 1.f, !WithinRel(0.f, 0.99f));
-                REQUIRE_THAT(-0.f,  WithinRel(0.f));
-                SECTION("Some subnormal values") {
-                    auto v1 = std::numeric_limits<float>::min();
-                    auto v2 = v1;
-                    for (int i = 0; i < 5; ++i) {
-                        v2 = std::nextafter(v1, 0.f);
-                    }
-                    REQUIRE_THAT(v1, WithinRel(v2));
-                }
-            }
-            SECTION("Margin") {
-                REQUIRE_THAT(1.f, WithinAbs(1.f, 0));
-                REQUIRE_THAT(0.f, WithinAbs(1.f, 1));
-
-                REQUIRE_THAT(0.f, !WithinAbs(1.f, 0.99f));
-                REQUIRE_THAT(0.f, !WithinAbs(1.f, 0.99f));
-
-                REQUIRE_THAT(0.f, WithinAbs(-0.f, 0));
-
-                REQUIRE_THAT(11.f, !WithinAbs(10.f, 0.5f));
-                REQUIRE_THAT(10.f, !WithinAbs(11.f, 0.5f));
-                REQUIRE_THAT(-10.f, WithinAbs(-10.f, 0.5f));
-                REQUIRE_THAT(-10.f, WithinAbs(-9.6f, 0.5f));
-            }
-            SECTION("ULPs") {
-                REQUIRE_THAT(1.f, WithinULP(1.f, 0));
+    SECTION( "Contains (element)" ) {
+        CHECK_THAT( v, VectorContains( -1 ) );
+        CHECK_THAT( empty, VectorContains( 1 ) );
+    }
+    SECTION( "Contains (vector)" ) {
+        CHECK_THAT( empty, Contains( v ) );
+        v2.push_back( 4 );
+        CHECK_THAT( v, Contains( v2 ) );
+    }
 
-                REQUIRE_THAT(nextafter(1.f, 2.f), WithinULP(1.f, 1));
-                REQUIRE_THAT(0.f, WithinULP(nextafter(0.f, 1.f), 1));
-                REQUIRE_THAT(1.f, WithinULP(nextafter(1.f, 0.f), 1));
-                REQUIRE_THAT(1.f, !WithinULP(nextafter(1.f, 2.f), 0));
+    SECTION( "Equals" ) {
 
-                REQUIRE_THAT(1.f, WithinULP(1.f, 0));
-                REQUIRE_THAT(-0.f, WithinULP(0.f, 0));
-            }
-            SECTION("Composed") {
-                REQUIRE_THAT(1.f, WithinAbs(1.f, 0.5) || WithinULP(1.f, 1));
-                REQUIRE_THAT(1.f, WithinAbs(2.f, 0.5) || WithinULP(1.f, 0));
-                REQUIRE_THAT(0.0001f, WithinAbs(0.f, 0.001f) || WithinRel(0.f, 0.1f));
-            }
-            SECTION("Constructor validation") {
-                REQUIRE_NOTHROW(WithinAbs(1.f, 0.f));
-                REQUIRE_THROWS_AS(WithinAbs(1.f, -1.f), std::domain_error);
+        CHECK_THAT( v, Equals( v2 ) );
+        CHECK_THAT( v2, Equals( v ) );
+        CHECK_THAT( empty, Equals( v ) );
+        CHECK_THAT( v, Equals( empty ) );
+    }
+    SECTION( "UnorderedEquals" ) {
+        CHECK_THAT( v, UnorderedEquals( empty ) );
+        CHECK_THAT( empty, UnorderedEquals( v ) );
 
-                REQUIRE_NOTHROW(WithinULP(1.f, 0));
-                REQUIRE_THROWS_AS(WithinULP(1.f, static_cast<uint64_t>(-1)), std::domain_error);
+        auto permuted = v;
+        std::next_permutation( begin( permuted ), end( permuted ) );
+        permuted.pop_back();
+        CHECK_THAT( permuted, UnorderedEquals( v ) );
 
-                REQUIRE_NOTHROW(WithinRel(1.f, 0.f));
-                REQUIRE_THROWS_AS(WithinRel(1.f, -0.2f), std::domain_error);
-                REQUIRE_THROWS_AS(WithinRel(1.f, 1.f), std::domain_error);
+        std::reverse( begin( permuted ), end( permuted ) );
+        CHECK_THAT( permuted, UnorderedEquals( v ) );
+    }
+}
+
+TEST_CASE( "Exception matchers that succeed",
+           "[matchers][exceptions][!throws]" ) {
+    CHECK_THROWS_MATCHES(
+        throwsSpecialException( 1 ), SpecialException, ExceptionMatcher{ 1 } );
+    REQUIRE_THROWS_MATCHES(
+        throwsSpecialException( 2 ), SpecialException, ExceptionMatcher{ 2 } );
+}
+
+TEST_CASE( "Exception matchers that fail",
+           "[matchers][exceptions][!throws][.failing]" ) {
+    SECTION( "No exception" ) {
+        CHECK_THROWS_MATCHES(
+            doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } );
+        REQUIRE_THROWS_MATCHES(
+            doesNotThrow(), SpecialException, ExceptionMatcher{ 1 } );
+    }
+    SECTION( "Type mismatch" ) {
+        CHECK_THROWS_MATCHES(
+            throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } );
+        REQUIRE_THROWS_MATCHES(
+            throwsAsInt( 1 ), SpecialException, ExceptionMatcher{ 1 } );
+    }
+    SECTION( "Contents are wrong" ) {
+        CHECK_THROWS_MATCHES( throwsSpecialException( 3 ),
+                              SpecialException,
+                              ExceptionMatcher{ 1 } );
+        REQUIRE_THROWS_MATCHES( throwsSpecialException( 4 ),
+                                SpecialException,
+                                ExceptionMatcher{ 1 } );
+    }
+}
+
+TEST_CASE( "Floating point matchers: float", "[matchers][floating-point]" ) {
+    SECTION( "Relative" ) {
+        REQUIRE_THAT( 10.f, WithinRel( 11.1f, 0.1f ) );
+        REQUIRE_THAT( 10.f, !WithinRel( 11.2f, 0.1f ) );
+        REQUIRE_THAT( 1.f, !WithinRel( 0.f, 0.99f ) );
+        REQUIRE_THAT( -0.f, WithinRel( 0.f ) );
+        SECTION( "Some subnormal values" ) {
+            auto v1 = std::numeric_limits<float>::min();
+            auto v2 = v1;
+            for ( int i = 0; i < 5; ++i ) {
+                v2 = std::nextafter( v1, 0.f );
             }
+            REQUIRE_THAT( v1, WithinRel( v2 ) );
         }
+    }
+    SECTION( "Margin" ) {
+        REQUIRE_THAT( 1.f, WithinAbs( 1.f, 0 ) );
+        REQUIRE_THAT( 0.f, WithinAbs( 1.f, 1 ) );
 
-        TEST_CASE("Floating point matchers: double", "[matchers][floating-point]") {
-            SECTION("Relative") {
-                REQUIRE_THAT(10., WithinRel(11.1, 0.1));
-                REQUIRE_THAT(10., !WithinRel(11.2, 0.1));
-                REQUIRE_THAT(1., !WithinRel(0., 0.99));
-                REQUIRE_THAT(-0., WithinRel(0.));
-                SECTION("Some subnormal values") {
-                    auto v1 = std::numeric_limits<double>::min();
-                    auto v2 = v1;
-                    for (int i = 0; i < 5; ++i) {
-                        v2 = std::nextafter(v1, 0);
-                    }
-                    REQUIRE_THAT(v1, WithinRel(v2));
-                }
-            }
-            SECTION("Margin") {
-                REQUIRE_THAT(1., WithinAbs(1., 0));
-                REQUIRE_THAT(0., WithinAbs(1., 1));
-
-                REQUIRE_THAT(0., !WithinAbs(1., 0.99));
-                REQUIRE_THAT(0., !WithinAbs(1., 0.99));
+        REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) );
+        REQUIRE_THAT( 0.f, !WithinAbs( 1.f, 0.99f ) );
 
-                REQUIRE_THAT(11., !WithinAbs(10., 0.5));
-                REQUIRE_THAT(10., !WithinAbs(11., 0.5));
-                REQUIRE_THAT(-10., WithinAbs(-10., 0.5));
-                REQUIRE_THAT(-10., WithinAbs(-9.6, 0.5));
-            }
-            SECTION("ULPs") {
-                REQUIRE_THAT(1., WithinULP(1., 0));
+        REQUIRE_THAT( 0.f, WithinAbs( -0.f, 0 ) );
 
-                REQUIRE_THAT(nextafter(1., 2.), WithinULP(1., 1));
-                REQUIRE_THAT(0.,  WithinULP(nextafter(0., 1.), 1));
-                REQUIRE_THAT(1.,  WithinULP(nextafter(1., 0.), 1));
-                REQUIRE_THAT(1., !WithinULP(nextafter(1., 2.), 0));
-
-                REQUIRE_THAT(1., WithinULP(1., 0));
-                REQUIRE_THAT(-0., WithinULP(0., 0));
-            }
-            SECTION("Composed") {
-                REQUIRE_THAT(1., WithinAbs(1., 0.5) || WithinULP(2., 1));
-                REQUIRE_THAT(1., WithinAbs(2., 0.5) || WithinULP(1., 0));
-                REQUIRE_THAT(0.0001, WithinAbs(0., 0.001) || WithinRel(0., 0.1));
-            }
-            SECTION("Constructor validation") {
-                REQUIRE_NOTHROW(WithinAbs(1., 0.));
-                REQUIRE_THROWS_AS(WithinAbs(1., -1.), std::domain_error);
+        REQUIRE_THAT( 11.f, !WithinAbs( 10.f, 0.5f ) );
+        REQUIRE_THAT( 10.f, !WithinAbs( 11.f, 0.5f ) );
+        REQUIRE_THAT( -10.f, WithinAbs( -10.f, 0.5f ) );
+        REQUIRE_THAT( -10.f, WithinAbs( -9.6f, 0.5f ) );
+    }
+    SECTION( "ULPs" ) {
+        REQUIRE_THAT( 1.f, WithinULP( 1.f, 0 ) );
+        REQUIRE_THAT(-1.f, WithinULP( -1.f, 0 ) );
 
-                REQUIRE_NOTHROW(WithinULP(1., 0));
+        REQUIRE_THAT( nextafter( 1.f, 2.f ), WithinULP( 1.f, 1 ) );
+        REQUIRE_THAT( 0.f, WithinULP( nextafter( 0.f, 1.f ), 1 ) );
+        REQUIRE_THAT( 1.f, WithinULP( nextafter( 1.f, 0.f ), 1 ) );
+        REQUIRE_THAT( 1.f, !WithinULP( nextafter( 1.f, 2.f ), 0 ) );
 
-                REQUIRE_NOTHROW(WithinRel(1., 0.));
-                REQUIRE_THROWS_AS(WithinRel(1., -0.2), std::domain_error);
-                REQUIRE_THROWS_AS(WithinRel(1., 1.), std::domain_error);
-            }
-        }
+        REQUIRE_THAT( 1.f, WithinULP( 1.f, 0 ) );
+        REQUIRE_THAT( -0.f, WithinULP( 0.f, 0 ) );
+    }
+    SECTION( "Composed" ) {
+        REQUIRE_THAT( 1.f, WithinAbs( 1.f, 0.5 ) || WithinULP( 1.f, 1 ) );
+        REQUIRE_THAT( 1.f, WithinAbs( 2.f, 0.5 ) || WithinULP( 1.f, 0 ) );
+        REQUIRE_THAT( 0.0001f,
+                      WithinAbs( 0.f, 0.001f ) || WithinRel( 0.f, 0.1f ) );
+    }
+    SECTION( "Constructor validation" ) {
+        REQUIRE_NOTHROW( WithinAbs( 1.f, 0.f ) );
+        REQUIRE_THROWS_AS( WithinAbs( 1.f, -1.f ), std::domain_error );
 
-        TEST_CASE("Floating point matchers that are problematic in approvals", "[approvals][matchers][floating-point]") {
-            REQUIRE_THAT(NAN, !WithinAbs(NAN, 0));
-            REQUIRE_THAT(NAN, !(WithinAbs(NAN, 100) || WithinULP(NAN, 123)));
-            REQUIRE_THAT(NAN, !WithinULP(NAN, 123));
-            REQUIRE_THAT(INFINITY, WithinRel(INFINITY));
-            REQUIRE_THAT(-INFINITY, !WithinRel(INFINITY));
-            REQUIRE_THAT(1., !WithinRel(INFINITY));
-            REQUIRE_THAT(INFINITY, !WithinRel(1.));
-            REQUIRE_THAT(NAN, !WithinRel(NAN));
-            REQUIRE_THAT(1., !WithinRel(NAN));
-            REQUIRE_THAT(NAN, !WithinRel(1.));
-        }
+        REQUIRE_NOTHROW( WithinULP( 1.f, 0 ) );
+        REQUIRE_THROWS_AS( WithinULP( 1.f, static_cast<uint64_t>( -1 ) ),
+                           std::domain_error );
 
-        TEST_CASE("Arbitrary predicate matcher", "[matchers][generic]") {
-            SECTION("Function pointer") {
-                REQUIRE_THAT(1,  Predicate<int>(alwaysTrue, "always true"));
-                REQUIRE_THAT(1, !Predicate<int>(alwaysFalse, "always false"));
-            }
-            SECTION("Lambdas + different type") {
-                REQUIRE_THAT("Hello olleH",
-                             Predicate<std::string>(
-                                 [] (std::string const& str) -> bool { return str.front() == str.back(); },
-                                 "First and last character should be equal")
-                );
-
-                REQUIRE_THAT("This wouldn't pass",
-                             !Predicate<std::string>(
-                                 [] (std::string const& str) -> bool { return str.front() == str.back(); }
-                             )
-                );
+        REQUIRE_NOTHROW( WithinRel( 1.f, 0.f ) );
+        REQUIRE_THROWS_AS( WithinRel( 1.f, -0.2f ), std::domain_error );
+        REQUIRE_THROWS_AS( WithinRel( 1.f, 1.f ), std::domain_error );
+    }
+}
+
+TEST_CASE( "Floating point matchers: double", "[matchers][floating-point]" ) {
+    SECTION( "Relative" ) {
+        REQUIRE_THAT( 10., WithinRel( 11.1, 0.1 ) );
+        REQUIRE_THAT( 10., !WithinRel( 11.2, 0.1 ) );
+        REQUIRE_THAT( 1., !WithinRel( 0., 0.99 ) );
+        REQUIRE_THAT( -0., WithinRel( 0. ) );
+        SECTION( "Some subnormal values" ) {
+            auto v1 = std::numeric_limits<double>::min();
+            auto v2 = v1;
+            for ( int i = 0; i < 5; ++i ) {
+                v2 = std::nextafter( v1, 0 );
             }
+            REQUIRE_THAT( v1, WithinRel( v2 ) );
         }
+    }
+    SECTION( "Margin" ) {
+        REQUIRE_THAT( 1., WithinAbs( 1., 0 ) );
+        REQUIRE_THAT( 0., WithinAbs( 1., 1 ) );
 
-        TEST_CASE("Regression test #1", "[matchers][vector]") {
-            // At some point, UnorderedEqualsMatcher skipped
-            // mismatched prefixed before doing the comparison itself
-            std::vector<char> actual = { 'a', 'b' };
-            std::vector<char> expected = { 'c', 'b' };
+        REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) );
+        REQUIRE_THAT( 0., !WithinAbs( 1., 0.99 ) );
 
-            CHECK_THAT(actual, !UnorderedEquals(expected));
-        }
+        REQUIRE_THAT( 11., !WithinAbs( 10., 0.5 ) );
+        REQUIRE_THAT( 10., !WithinAbs( 11., 0.5 ) );
+        REQUIRE_THAT( -10., WithinAbs( -10., 0.5 ) );
+        REQUIRE_THAT( -10., WithinAbs( -9.6, 0.5 ) );
+    }
+    SECTION( "ULPs" ) {
+        REQUIRE_THAT( 1., WithinULP( 1., 0 ) );
 
-        TEST_CASE("Predicate matcher can accept const char*", "[matchers][compilation]") {
-            REQUIRE_THAT("foo", Predicate<const char*>([] (const char* const&) { return true; }));
-        }
+        REQUIRE_THAT( nextafter( 1., 2. ), WithinULP( 1., 1 ) );
+        REQUIRE_THAT( 0., WithinULP( nextafter( 0., 1. ), 1 ) );
+        REQUIRE_THAT( 1., WithinULP( nextafter( 1., 0. ), 1 ) );
+        REQUIRE_THAT( 1., !WithinULP( nextafter( 1., 2. ), 0 ) );
 
-        TEST_CASE("Vector Approx matcher", "[matchers][approx][vector]") {
-            using Catch::Matchers::Approx;
-            SECTION("Empty vector is roughly equal to an empty vector") {
-                std::vector<double> empty;
-                REQUIRE_THAT(empty, Approx(empty));
-            }
-            SECTION("Vectors with elements") {
-                std::vector<double> v1({1., 2., 3.});
-                SECTION("A vector is approx equal to itself") {
-                    REQUIRE_THAT(v1, Approx(v1));
-                    REQUIRE_THAT(v1, Approx<double>({ 1., 2., 3. }));
-                }
-                std::vector<double> v2({1.5, 2.5, 3.5});
-                SECTION("Different length") {
-                    auto temp(v1);
-                    temp.push_back(4);
-                    REQUIRE_THAT(v1, !Approx(temp));
-                }
-                SECTION("Same length, different elements") {
-                    REQUIRE_THAT(v1, !Approx(v2));
-                    REQUIRE_THAT(v1, Approx(v2).margin(0.5));
-                    REQUIRE_THAT(v1, Approx(v2).epsilon(0.5));
-                    REQUIRE_THAT(v1, Approx(v2).epsilon(0.1).scale(500));
-                }
-            }
-        }
+        REQUIRE_THAT( 1., WithinULP( 1., 0 ) );
+        REQUIRE_THAT( -0., WithinULP( 0., 0 ) );
+    }
+    SECTION( "Composed" ) {
+        REQUIRE_THAT( 1., WithinAbs( 1., 0.5 ) || WithinULP( 2., 1 ) );
+        REQUIRE_THAT( 1., WithinAbs( 2., 0.5 ) || WithinULP( 1., 0 ) );
+        REQUIRE_THAT( 0.0001, WithinAbs( 0., 0.001 ) || WithinRel( 0., 0.1 ) );
+    }
+    SECTION( "Constructor validation" ) {
+        REQUIRE_NOTHROW( WithinAbs( 1., 0. ) );
+        REQUIRE_THROWS_AS( WithinAbs( 1., -1. ), std::domain_error );
 
-        TEST_CASE("Vector Approx matcher -- failing", "[matchers][approx][vector][.failing]") {
-            using Catch::Matchers::Approx;
-            SECTION("Empty and non empty vectors are not approx equal") {
-                std::vector<double> empty, t1({1, 2});
-                CHECK_THAT(empty, Approx(t1));
-            }
-            SECTION("Just different vectors") {
-                std::vector<double> v1({2., 4., 6.}), v2({1., 3., 5.});
-                CHECK_THAT(v1, Approx(v2));
-            }
-        }
+        REQUIRE_NOTHROW( WithinULP( 1., 0 ) );
 
-        TEST_CASE("Exceptions matchers", "[matchers][exceptions][!throws]") {
-            REQUIRE_THROWS_MATCHES(throwsDerivedException(),  DerivedException,  Message("DerivedException::what"));
-            REQUIRE_THROWS_MATCHES(throwsDerivedException(),  DerivedException, !Message("derivedexception::what"));
-            REQUIRE_THROWS_MATCHES(throwsSpecialException(2), SpecialException, !Message("DerivedException::what"));
-            REQUIRE_THROWS_MATCHES(throwsSpecialException(2), SpecialException,  Message("SpecialException::what"));
+        REQUIRE_NOTHROW( WithinRel( 1., 0. ) );
+        REQUIRE_THROWS_AS( WithinRel( 1., -0.2 ), std::domain_error );
+        REQUIRE_THROWS_AS( WithinRel( 1., 1. ), std::domain_error );
+    }
+}
+
+TEST_CASE( "Floating point matchers that are problematic in approvals",
+           "[approvals][matchers][floating-point]" ) {
+    REQUIRE_THAT( NAN, !WithinAbs( NAN, 0 ) );
+    REQUIRE_THAT( NAN, !( WithinAbs( NAN, 100 ) || WithinULP( NAN, 123 ) ) );
+    REQUIRE_THAT( NAN, !WithinULP( NAN, 123 ) );
+    REQUIRE_THAT( INFINITY, WithinRel( INFINITY ) );
+    REQUIRE_THAT( -INFINITY, !WithinRel( INFINITY ) );
+    REQUIRE_THAT( 1., !WithinRel( INFINITY ) );
+    REQUIRE_THAT( INFINITY, !WithinRel( 1. ) );
+    REQUIRE_THAT( NAN, !WithinRel( NAN ) );
+    REQUIRE_THAT( 1., !WithinRel( NAN ) );
+    REQUIRE_THAT( NAN, !WithinRel( 1. ) );
+}
+
+TEST_CASE( "Arbitrary predicate matcher", "[matchers][generic]" ) {
+    SECTION( "Function pointer" ) {
+        REQUIRE_THAT( 1, Predicate<int>( alwaysTrue, "always true" ) );
+        REQUIRE_THAT( 1, !Predicate<int>( alwaysFalse, "always false" ) );
+    }
+    SECTION( "Lambdas + different type" ) {
+        REQUIRE_THAT( "Hello olleH",
+                      Predicate<std::string>(
+                          []( std::string const& str ) -> bool {
+                              return str.front() == str.back();
+                          },
+                          "First and last character should be equal" ) );
+
+        REQUIRE_THAT(
+            "This wouldn't pass",
+            !Predicate<std::string>( []( std::string const& str ) -> bool {
+                return str.front() == str.back();
+            } ) );
+    }
+}
+
+TEST_CASE( "Regression test #1", "[matchers][vector]" ) {
+    // At some point, UnorderedEqualsMatcher skipped
+    // mismatched prefixed before doing the comparison itself
+    std::vector<char> actual = { 'a', 'b' };
+    std::vector<char> expected = { 'c', 'b' };
+
+    CHECK_THAT( actual, !UnorderedEquals( expected ) );
+}
+
+TEST_CASE( "Predicate matcher can accept const char*",
+           "[matchers][compilation]" ) {
+    REQUIRE_THAT( "foo", Predicate<const char*>( []( const char* const& ) {
+                      return true;
+                  } ) );
+}
+
+TEST_CASE( "Vector Approx matcher", "[matchers][approx][vector]" ) {
+    using Catch::Matchers::Approx;
+    SECTION( "Empty vector is roughly equal to an empty vector" ) {
+        std::vector<double> empty;
+        REQUIRE_THAT( empty, Approx( empty ) );
+    }
+    SECTION( "Vectors with elements" ) {
+        std::vector<double> v1( { 1., 2., 3. } );
+        SECTION( "A vector is approx equal to itself" ) {
+            REQUIRE_THAT( v1, Approx( v1 ) );
+            REQUIRE_THAT( v1, Approx<double>( { 1., 2., 3. } ) );
         }
-
-        struct CheckedTestingMatcher : Catch::Matchers::MatcherBase<int> {
-            mutable bool matchCalled = false;
-            bool matchSucceeds = false;
-
-            bool match(int const&) const override {
-                matchCalled = true;
-                return matchSucceeds;
-            }
-            std::string describe() const override {
-                return "CheckedTestingMatcher set to " + (matchSucceeds ? std::string("succeed") : std::string("fail"));
-            }
-        };
-
-        TEST_CASE("Composed matchers shortcircuit", "[matchers][composed]") {
-            // Check that if first returns false, second is not touched
-            CheckedTestingMatcher first, second;
-            SECTION("MatchAllOf") {
-                first.matchSucceeds = false;
-
-                Detail::MatchAllOf<int> matcher =
-                    Detail::MatchAllOf<int>{} && first && second;
-                CHECK_FALSE( matcher.match( 1 ) );
-
-                // These two assertions are the important ones
-                REQUIRE(first.matchCalled);
-                REQUIRE(!second.matchCalled);
-            }
-            // Check that if first returns true, second is not touched
-            SECTION("MatchAnyOf") {
-                first.matchSucceeds = true;
-
-                Detail::MatchAnyOf<int> matcher =
-                    Detail::MatchAnyOf<int>{} || first || second;
-                CHECK( matcher.match( 1 ) );
-
-                // These two assertions are the important ones
-                REQUIRE(first.matchCalled);
-                REQUIRE(!second.matchCalled);
-            }
+        std::vector<double> v2( { 1.5, 2.5, 3.5 } );
+        SECTION( "Different length" ) {
+            auto temp( v1 );
+            temp.push_back( 4 );
+            REQUIRE_THAT( v1, !Approx( temp ) );
         }
-
-        struct CheckedTestingGenericMatcher : Catch::Matchers::MatcherGenericBase {
-            mutable bool matchCalled = false;
-            bool matchSucceeds = false;
-
-            bool match(int const&) const {
-                matchCalled = true;
-                return matchSucceeds;
-            }
-            std::string describe() const override {
-                return "CheckedTestingGenericMatcher set to " + (matchSucceeds ? std::string("succeed") : std::string("fail"));
-            }
-        };
-
-        TEST_CASE("Composed generic matchers shortcircuit", "[matchers][composed][generic]") {
-            // Check that if first returns false, second is not touched
-            CheckedTestingGenericMatcher first, second;
-            SECTION("MatchAllOf") {
-                first.matchSucceeds = false;
-
-                Detail::MatchAllOfGeneric<CheckedTestingGenericMatcher,
-                                          CheckedTestingGenericMatcher>
-                    matcher{ first, second };
-
-                CHECK_FALSE( matcher.match( 1 ) );
-
-                // These two assertions are the important ones
-                REQUIRE(first.matchCalled);
-                REQUIRE(!second.matchCalled);
-            }
-            // Check that if first returns true, second is not touched
-            SECTION("MatchAnyOf") {
-                first.matchSucceeds = true;
-
-                Detail::MatchAnyOfGeneric<CheckedTestingGenericMatcher,
-                                          CheckedTestingGenericMatcher>
-                    matcher{ first, second };
-                CHECK(matcher.match(1));
-
-                // These two assertions are the important ones
-                REQUIRE(first.matchCalled);
-                REQUIRE(!second.matchCalled);
-            }
+        SECTION( "Same length, different elements" ) {
+            REQUIRE_THAT( v1, !Approx( v2 ) );
+            REQUIRE_THAT( v1, Approx( v2 ).margin( 0.5 ) );
+            REQUIRE_THAT( v1, Approx( v2 ).epsilon( 0.5 ) );
+            REQUIRE_THAT( v1, Approx( v2 ).epsilon( 0.1 ).scale( 500 ) );
         }
+    }
+}
+
+TEST_CASE( "Vector Approx matcher -- failing",
+           "[matchers][approx][vector][.failing]" ) {
+    using Catch::Matchers::Approx;
+    SECTION( "Empty and non empty vectors are not approx equal" ) {
+        std::vector<double> empty, t1( { 1, 2 } );
+        CHECK_THAT( empty, Approx( t1 ) );
+    }
+    SECTION( "Just different vectors" ) {
+        std::vector<double> v1( { 2., 4., 6. } ), v2( { 1., 3., 5. } );
+        CHECK_THAT( v1, Approx( v2 ) );
+    }
+}
+
+TEST_CASE( "Exceptions matchers", "[matchers][exceptions][!throws]" ) {
+    REQUIRE_THROWS_MATCHES( throwsDerivedException(),
+                            DerivedException,
+                            Message( "DerivedException::what" ) );
+    REQUIRE_THROWS_MATCHES( throwsDerivedException(),
+                            DerivedException,
+                            !Message( "derivedexception::what" ) );
+    REQUIRE_THROWS_MATCHES( throwsSpecialException( 2 ),
+                            SpecialException,
+                            !Message( "DerivedException::what" ) );
+    REQUIRE_THROWS_MATCHES( throwsSpecialException( 2 ),
+                            SpecialException,
+                            Message( "SpecialException::what" ) );
+}
+
+struct CheckedTestingMatcher : Catch::Matchers::MatcherBase<int> {
+    mutable bool matchCalled = false;
+    bool matchSucceeds = false;
+
+    bool match( int const& ) const override {
+        matchCalled = true;
+        return matchSucceeds;
+    }
+    std::string describe() const override {
+        return "CheckedTestingMatcher set to " +
+               ( matchSucceeds ? std::string( "succeed" )
+                               : std::string( "fail" ) );
+    }
+};
 
+TEST_CASE( "Composed matchers shortcircuit", "[matchers][composed]" ) {
+    // Check that if first returns false, second is not touched
+    CheckedTestingMatcher first, second;
+    SECTION( "MatchAllOf" ) {
+        first.matchSucceeds = false;
 
-    template<typename Range>
-    struct EqualsRangeMatcher : Catch::Matchers::MatcherGenericBase {
-
-        EqualsRangeMatcher(Range const& range) : m_range{ range } {}
-
-        template<typename OtherRange>
-        bool match(OtherRange const& other) const {
-            using std::begin;
-            using std::end;
-
-            return std::equal(begin(m_range), end(m_range), begin(other), end(other));
-        }
+        Detail::MatchAllOf<int> matcher =
+            Detail::MatchAllOf<int>{} && first && second;
+        CHECK_FALSE( matcher.match( 1 ) );
 
-        std::string describe() const override {
-            return "Equals: " + Catch::rangeToString(m_range);
-        }
+        // These two assertions are the important ones
+        REQUIRE( first.matchCalled );
+        REQUIRE( !second.matchCalled );
+    }
+    // Check that if first returns true, second is not touched
+    SECTION( "MatchAnyOf" ) {
+        first.matchSucceeds = true;
 
-    private:
-        Range const& m_range;
-    };
+        Detail::MatchAnyOf<int> matcher =
+            Detail::MatchAnyOf<int>{} || first || second;
+        CHECK( matcher.match( 1 ) );
 
-    template<typename Range>
-    auto EqualsRange(const Range& range) -> EqualsRangeMatcher<Range> {
-        return EqualsRangeMatcher<Range>{range};
+        // These two assertions are the important ones
+        REQUIRE( first.matchCalled );
+        REQUIRE( !second.matchCalled );
     }
+}
 
-    TEST_CASE("Combining templated matchers", "[matchers][templated]") {
-        std::array<int, 3> container{{ 1,2,3 }};
-
-        std::array<int, 3> a{{ 1,2,3 }};
-        std::vector<int> b{ 0,1,2 };
-        std::list<int> c{ 4,5,6 };
+struct CheckedTestingGenericMatcher : Catch::Matchers::MatcherGenericBase {
+    mutable bool matchCalled = false;
+    bool matchSucceeds = false;
 
-        REQUIRE_THAT(container, EqualsRange(a) || EqualsRange(b) || EqualsRange(c));
+    bool match( int const& ) const {
+        matchCalled = true;
+        return matchSucceeds;
     }
+    std::string describe() const override {
+        return "CheckedTestingGenericMatcher set to " +
+               ( matchSucceeds ? std::string( "succeed" )
+                               : std::string( "fail" ) );
+    }
+};
 
-    TEST_CASE("Combining templated and concrete matchers", "[matchers][templated]") {
-        std::vector<int> vec{ 1, 3, 5 };
+TEST_CASE( "Composed generic matchers shortcircuit",
+           "[matchers][composed][generic]" ) {
+    // Check that if first returns false, second is not touched
+    CheckedTestingGenericMatcher first, second;
+    SECTION( "MatchAllOf" ) {
+        first.matchSucceeds = false;
 
-        std::array<int, 3> a{{ 5, 3, 1 }};
+        Detail::MatchAllOfGeneric<CheckedTestingGenericMatcher,
+                                  CheckedTestingGenericMatcher>
+            matcher{ first, second };
 
-        REQUIRE_THAT(vec,
-                    Predicate<std::vector<int>>([](auto const& v) {
-                        return std::all_of(v.begin(), v.end(), [](int elem) {
-                            return elem % 2 == 1;
-                        });
-                    }, "All elements are odd") &&
-                    !EqualsRange(a));
+        CHECK_FALSE( matcher.match( 1 ) );
 
-        const std::string str = "foobar";
-        const std::array<char, 6> arr{{ 'f', 'o', 'o', 'b', 'a', 'r' }};
-        const std::array<char, 6> bad_arr{{ 'o', 'o', 'f', 'b', 'a', 'r' }};
+        // These two assertions are the important ones
+        REQUIRE( first.matchCalled );
+        REQUIRE( !second.matchCalled );
+    }
+    // Check that if first returns true, second is not touched
+    SECTION( "MatchAnyOf" ) {
+        first.matchSucceeds = true;
+
+        Detail::MatchAnyOfGeneric<CheckedTestingGenericMatcher,
+                                  CheckedTestingGenericMatcher>
+            matcher{ first, second };
+        CHECK( matcher.match( 1 ) );
+
+        // These two assertions are the important ones
+        REQUIRE( first.matchCalled );
+        REQUIRE( !second.matchCalled );
+    }
+}
 
-        using Catch::Matchers::StartsWith;
-        using Catch::Matchers::EndsWith;
+template <typename Range>
+struct EqualsRangeMatcher : Catch::Matchers::MatcherGenericBase {
 
-        REQUIRE_THAT(str, StartsWith("foo") && EqualsRange(arr) && EndsWith("bar"));
-        REQUIRE_THAT(str, StartsWith("foo") && !EqualsRange(bad_arr) && EndsWith("bar"));
+    EqualsRangeMatcher( Range const& range ): m_range{ range } {}
 
-        REQUIRE_THAT(str, EqualsRange(arr) && StartsWith("foo") && EndsWith("bar"));
-        REQUIRE_THAT(str, !EqualsRange(bad_arr) && StartsWith("foo") && EndsWith("bar"));
+    template <typename OtherRange> bool match( OtherRange const& other ) const {
+        using std::begin;
+        using std::end;
 
-        REQUIRE_THAT(str, EqualsRange(bad_arr) || (StartsWith("foo") && EndsWith("bar")));
-        REQUIRE_THAT(str, (StartsWith("foo") && EndsWith("bar")) || EqualsRange(bad_arr));
+        return std::equal(
+            begin( m_range ), end( m_range ), begin( other ), end( other ) );
     }
 
-    TEST_CASE("Combining concrete matchers does not use templated matchers", "[matchers][templated]") {
-        using Catch::Matchers::StartsWith;
-        using Catch::Matchers::EndsWith;
+    std::string describe() const override {
+        return "Equals: " + Catch::rangeToString( m_range );
+    }
 
-        STATIC_REQUIRE(std::is_same<
-            decltype(StartsWith("foo") || (StartsWith("bar") && EndsWith("bar") && !EndsWith("foo"))),
-            Catch::Matchers::Detail::MatchAnyOf<std::string>
-        >::value);
+private:
+    Range const& m_range;
+};
+
+template <typename Range>
+auto EqualsRange( const Range& range ) -> EqualsRangeMatcher<Range> {
+    return EqualsRangeMatcher<Range>{ range };
+}
+
+TEST_CASE( "Combining templated matchers", "[matchers][templated]" ) {
+    std::array<int, 3> container{ { 1, 2, 3 } };
+
+    std::array<int, 3> a{ { 1, 2, 3 } };
+    std::vector<int> b{ 0, 1, 2 };
+    std::list<int> c{ 4, 5, 6 };
+
+    REQUIRE_THAT( container,
+                  EqualsRange( a ) || EqualsRange( b ) || EqualsRange( c ) );
+}
+
+TEST_CASE( "Combining templated and concrete matchers",
+           "[matchers][templated]" ) {
+    std::vector<int> vec{ 1, 3, 5 };
+
+    std::array<int, 3> a{ { 5, 3, 1 } };
+
+    REQUIRE_THAT( vec,
+                  Predicate<std::vector<int>>(
+                      []( auto const& v ) {
+                          return std::all_of(
+                              v.begin(), v.end(), []( int elem ) {
+                                  return elem % 2 == 1;
+                              } );
+                      },
+                      "All elements are odd" ) &&
+                      !EqualsRange( a ) );
+
+    const std::string str = "foobar";
+    const std::array<char, 6> arr{ { 'f', 'o', 'o', 'b', 'a', 'r' } };
+    const std::array<char, 6> bad_arr{ { 'o', 'o', 'f', 'b', 'a', 'r' } };
+
+    using Catch::Matchers::EndsWith;
+    using Catch::Matchers::StartsWith;
+
+    REQUIRE_THAT(
+        str, StartsWith( "foo" ) && EqualsRange( arr ) && EndsWith( "bar" ) );
+    REQUIRE_THAT( str,
+                  StartsWith( "foo" ) && !EqualsRange( bad_arr ) &&
+                      EndsWith( "bar" ) );
+
+    REQUIRE_THAT(
+        str, EqualsRange( arr ) && StartsWith( "foo" ) && EndsWith( "bar" ) );
+    REQUIRE_THAT( str,
+                  !EqualsRange( bad_arr ) && StartsWith( "foo" ) &&
+                      EndsWith( "bar" ) );
+
+    REQUIRE_THAT( str,
+                  EqualsRange( bad_arr ) ||
+                      ( StartsWith( "foo" ) && EndsWith( "bar" ) ) );
+    REQUIRE_THAT( str,
+                  ( StartsWith( "foo" ) && EndsWith( "bar" ) ) ||
+                      EqualsRange( bad_arr ) );
+}
+
+TEST_CASE( "Combining concrete matchers does not use templated matchers",
+           "[matchers][templated]" ) {
+    using Catch::Matchers::EndsWith;
+    using Catch::Matchers::StartsWith;
+
+    STATIC_REQUIRE(
+        std::is_same<decltype( StartsWith( "foo" ) ||
+                               ( StartsWith( "bar" ) && EndsWith( "bar" ) &&
+                                 !EndsWith( "foo" ) ) ),
+                     Catch::Matchers::Detail::MatchAnyOf<std::string>>::value );
+}
+
+struct MatcherA : Catch::Matchers::MatcherGenericBase {
+    std::string describe() const override {
+        return "equals: (int) 1 or (float) 1.0f";
+    }
+    bool match( int i ) const { return i == 1; }
+    bool match( float f ) const { return f == 1.0f; }
+};
+
+struct MatcherB : Catch::Matchers::MatcherGenericBase {
+    std::string describe() const override { return "equals: (long long) 1"; }
+    bool match( long long l ) const { return l == 1ll; }
+};
+
+struct MatcherC : Catch::Matchers::MatcherGenericBase {
+    std::string describe() const override { return "equals: (T) 1"; }
+    template <typename T> bool match( T t ) const { return t == T{ 1 }; }
+};
+
+struct MatcherD : Catch::Matchers::MatcherGenericBase {
+    std::string describe() const override { return "equals: true"; }
+    bool match( bool b ) const { return b == true; }
+};
+
+TEST_CASE( "Combining only templated matchers", "[matchers][templated]" ) {
+    STATIC_REQUIRE(
+        std::is_same<decltype( MatcherA() || MatcherB() ),
+                     Catch::Matchers::Detail::
+                         MatchAnyOfGeneric<MatcherA, MatcherB>>::value );
+
+    REQUIRE_THAT( 1, MatcherA() || MatcherB() );
+
+    STATIC_REQUIRE(
+        std::is_same<decltype( MatcherA() && MatcherB() ),
+                     Catch::Matchers::Detail::
+                         MatchAllOfGeneric<MatcherA, MatcherB>>::value );
+
+    REQUIRE_THAT( 1, MatcherA() && MatcherB() );
+
+    STATIC_REQUIRE(
+        std::is_same<
+            decltype( MatcherA() || !MatcherB() ),
+            Catch::Matchers::Detail::MatchAnyOfGeneric<
+                MatcherA,
+                Catch::Matchers::Detail::MatchNotOfGeneric<MatcherB>>>::value );
+
+    REQUIRE_THAT( 1, MatcherA() || !MatcherB() );
+}
+
+TEST_CASE( "Combining MatchAnyOfGeneric does not nest",
+           "[matchers][templated]" ) {
+    // MatchAnyOfGeneric LHS + some matcher RHS
+    STATIC_REQUIRE(
+        std::is_same<
+            decltype( ( MatcherA() || MatcherB() ) || MatcherC() ),
+            Catch::Matchers::Detail::
+                MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>>::value );
+
+    REQUIRE_THAT( 1, ( MatcherA() || MatcherB() ) || MatcherC() );
+
+    // some matcher LHS + MatchAnyOfGeneric RHS
+    STATIC_REQUIRE(
+        std::is_same<
+            decltype( MatcherA() || ( MatcherB() || MatcherC() ) ),
+            Catch::Matchers::Detail::
+                MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>>::value );
+
+    REQUIRE_THAT( 1, MatcherA() || ( MatcherB() || MatcherC() ) );
+
+    // MatchAnyOfGeneric LHS + MatchAnyOfGeneric RHS
+    STATIC_REQUIRE(
+        std::is_same<
+            decltype( ( MatcherA() || MatcherB() ) ||
+                      ( MatcherC() || MatcherD() ) ),
+            Catch::Matchers::Detail::
+                MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>>::
+            value );
+
+    REQUIRE_THAT(
+        1, ( MatcherA() || MatcherB() ) || ( MatcherC() || MatcherD() ) );
+}
+
+TEST_CASE( "Combining MatchAllOfGeneric does not nest",
+           "[matchers][templated]" ) {
+    // MatchAllOfGeneric lhs + some matcher RHS
+    STATIC_REQUIRE(
+        std::is_same<
+            decltype( ( MatcherA() && MatcherB() ) && MatcherC() ),
+            Catch::Matchers::Detail::
+                MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>>::value );
+
+    REQUIRE_THAT( 1, ( MatcherA() && MatcherB() ) && MatcherC() );
+
+    // some matcher LHS + MatchAllOfGeneric RSH
+    STATIC_REQUIRE(
+        std::is_same<
+            decltype( MatcherA() && ( MatcherB() && MatcherC() ) ),
+            Catch::Matchers::Detail::
+                MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>>::value );
+
+    REQUIRE_THAT( 1, MatcherA() && ( MatcherB() && MatcherC() ) );
+
+    // MatchAllOfGeneric LHS + MatchAllOfGeneric RHS
+    STATIC_REQUIRE(
+        std::is_same<
+            decltype( ( MatcherA() && MatcherB() ) &&
+                      ( MatcherC() && MatcherD() ) ),
+            Catch::Matchers::Detail::
+                MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>>::
+            value );
+
+    REQUIRE_THAT(
+        1, ( MatcherA() && MatcherB() ) && ( MatcherC() && MatcherD() ) );
+}
+
+TEST_CASE( "Combining MatchNotOfGeneric does not nest",
+           "[matchers][templated]" ) {
+    STATIC_REQUIRE(
+        std::is_same<
+            decltype( !MatcherA() ),
+            Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA>>::value );
+
+    REQUIRE_THAT( 0, !MatcherA() );
+
+    STATIC_REQUIRE(
+        std::is_same<decltype( !!MatcherA() ), MatcherA const&>::value );
+
+    REQUIRE_THAT( 1, !!MatcherA() );
+
+    STATIC_REQUIRE(
+        std::is_same<
+            decltype( !!!MatcherA() ),
+            Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA>>::value );
+
+    REQUIRE_THAT( 0, !!!MatcherA() );
+
+    STATIC_REQUIRE(
+        std::is_same<decltype( !!!!MatcherA() ), MatcherA const&>::value );
+
+    REQUIRE_THAT( 1, !!!!MatcherA() );
+}
+
+struct EvilAddressOfOperatorUsed : std::exception {
+    EvilAddressOfOperatorUsed() {}
+    const char* what() const noexcept override {
+        return "overloaded address-of operator of matcher was used instead of "
+               "std::addressof";
     }
+};
 
-    struct MatcherA : Catch::Matchers::MatcherGenericBase {
-        std::string describe() const override { return "equals: (int) 1 or (float) 1.0f"; }
-        bool match(int i) const { return i == 1; }
-        bool match(float f) const { return f == 1.0f; }
-    };
+struct EvilCommaOperatorUsed : std::exception {
+    EvilCommaOperatorUsed() {}
+    const char* what() const noexcept override {
+        return "overloaded comma operator of matcher was used";
+    }
+};
 
-    struct MatcherB : Catch::Matchers::MatcherGenericBase {
-        std::string describe() const override { return "equals: (long long) 1"; }
-        bool match(long long l) const { return l == 1ll; }
-    };
+struct EvilMatcher : Catch::Matchers::MatcherGenericBase {
+    std::string describe() const override { return "equals: 45"; }
 
-    struct MatcherC : Catch::Matchers::MatcherGenericBase {
-        std::string describe() const override { return "equals: (T) 1"; }
-        template<typename T>
-        bool match(T t) const { return t == T{1}; }
-    };
+    bool match( int i ) const { return i == 45; }
 
-    struct MatcherD : Catch::Matchers::MatcherGenericBase {
-        std::string describe() const override { return "equals: true"; }
-        bool match(bool b) const { return b == true; }
-    };
+    EvilMatcher const* operator&() const { throw EvilAddressOfOperatorUsed(); }
 
-    TEST_CASE("Combining only templated matchers", "[matchers][templated]") {
-        STATIC_REQUIRE(std::is_same<
-            decltype(MatcherA() || MatcherB()),
-            Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB>
-        >::value);
+    int operator,( EvilMatcher const& ) const { throw EvilCommaOperatorUsed(); }
+};
 
-        REQUIRE_THAT(1, MatcherA() || MatcherB());
+TEST_CASE( "Overloaded comma or address-of operators are not used",
+           "[matchers][templated]" ) {
+    REQUIRE_THROWS_AS( ( EvilMatcher(), EvilMatcher() ),
+                       EvilCommaOperatorUsed );
+    REQUIRE_THROWS_AS( &EvilMatcher(), EvilAddressOfOperatorUsed );
+    REQUIRE_NOTHROW( EvilMatcher() || ( EvilMatcher() && !EvilMatcher() ) );
+    REQUIRE_NOTHROW( ( EvilMatcher() && EvilMatcher() ) || !EvilMatcher() );
+}
 
-        STATIC_REQUIRE(std::is_same<
-            decltype(MatcherA() && MatcherB()),
-            Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB>
-        >::value);
+struct ImmovableMatcher : Catch::Matchers::MatcherGenericBase {
+    ImmovableMatcher() = default;
+    ImmovableMatcher( ImmovableMatcher const& ) = delete;
+    ImmovableMatcher( ImmovableMatcher&& ) = delete;
+    ImmovableMatcher& operator=( ImmovableMatcher const& ) = delete;
+    ImmovableMatcher& operator=( ImmovableMatcher&& ) = delete;
 
-        REQUIRE_THAT(1, MatcherA() && MatcherB());
+    std::string describe() const override { return "always false"; }
 
-        STATIC_REQUIRE(std::is_same<
-            decltype(MatcherA() || !MatcherB()),
-            Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, Catch::Matchers::Detail::MatchNotOfGeneric<MatcherB>>
-        >::value);
+    template <typename T> bool match( T&& ) const { return false; }
+};
 
-        REQUIRE_THAT(1, MatcherA() || !MatcherB());
+struct MatcherWasMovedOrCopied : std::exception {
+    MatcherWasMovedOrCopied() {}
+    const char* what() const noexcept override {
+        return "attempted to copy or move a matcher";
     }
+};
 
-    TEST_CASE("Combining MatchAnyOfGeneric does not nest", "[matchers][templated]") {
-        // MatchAnyOfGeneric LHS + some matcher RHS
-        STATIC_REQUIRE(std::is_same<
-            decltype((MatcherA() || MatcherB()) || MatcherC()),
-            Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>
-        >::value);
-
-        REQUIRE_THAT(1, (MatcherA() || MatcherB()) || MatcherC());
-
-        // some matcher LHS + MatchAnyOfGeneric RHS
-        STATIC_REQUIRE(std::is_same<
-                       decltype(MatcherA() || (MatcherB() || MatcherC())),
-                       Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC>
-        >::value);
-
-        REQUIRE_THAT(1, MatcherA() || (MatcherB() || MatcherC()));
-
-
-        // MatchAnyOfGeneric LHS + MatchAnyOfGeneric RHS
-        STATIC_REQUIRE(std::is_same<
-            decltype((MatcherA() || MatcherB()) || (MatcherC() || MatcherD())),
-            Catch::Matchers::Detail::MatchAnyOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>
-        >::value);
-
-        REQUIRE_THAT(1, (MatcherA() || MatcherB()) || (MatcherC() || MatcherD()));
+struct ThrowOnCopyOrMoveMatcher : Catch::Matchers::MatcherGenericBase {
+    ThrowOnCopyOrMoveMatcher() = default;
+    [[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher const& ):
+        Catch::Matchers::MatcherGenericBase() {
+        throw MatcherWasMovedOrCopied();
     }
-
-    TEST_CASE("Combining MatchAllOfGeneric does not nest", "[matchers][templated]") {
-        // MatchAllOfGeneric lhs + some matcher RHS
-        STATIC_REQUIRE(std::is_same<
-            decltype((MatcherA() && MatcherB()) && MatcherC()),
-            Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>
-        >::value);
-
-        REQUIRE_THAT(1, (MatcherA() && MatcherB()) && MatcherC());
-
-        // some matcher LHS + MatchAllOfGeneric RSH
-        STATIC_REQUIRE(std::is_same<
-                       decltype(MatcherA() && (MatcherB() && MatcherC())),
-                       Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC>
-        >::value);
-
-        REQUIRE_THAT(1, MatcherA() && (MatcherB() && MatcherC()));
-
-
-        // MatchAllOfGeneric LHS + MatchAllOfGeneric RHS
-        STATIC_REQUIRE(std::is_same<
-            decltype((MatcherA() && MatcherB()) && (MatcherC() && MatcherD())),
-            Catch::Matchers::Detail::MatchAllOfGeneric<MatcherA, MatcherB, MatcherC, MatcherD>
-        >::value);
-
-        REQUIRE_THAT(1, (MatcherA() && MatcherB()) && (MatcherC() && MatcherD()));
+    [[noreturn]] ThrowOnCopyOrMoveMatcher( ThrowOnCopyOrMoveMatcher&& ):
+        Catch::Matchers::MatcherGenericBase() {
+        throw MatcherWasMovedOrCopied();
     }
-
-    TEST_CASE("Combining MatchNotOfGeneric does not nest", "[matchers][templated]") {
-        STATIC_REQUIRE(std::is_same<
-            decltype(!MatcherA()),
-            Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA>
-        >::value);
-
-        REQUIRE_THAT(0, !MatcherA());
-
-        STATIC_REQUIRE(std::is_same<
-            decltype(!!MatcherA()),
-            MatcherA const&
-        >::value);
-
-        REQUIRE_THAT(1, !!MatcherA());
-
-        STATIC_REQUIRE(std::is_same<
-            decltype(!!!MatcherA()),
-            Catch::Matchers::Detail::MatchNotOfGeneric<MatcherA>
-        >::value);
-
-        REQUIRE_THAT(0, !!!MatcherA());
-
-        STATIC_REQUIRE(std::is_same<
-            decltype(!!!!MatcherA()),
-            MatcherA const &
-        >::value);
-
-        REQUIRE_THAT(1, !!!!MatcherA());
+    ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher const& ) {
+        throw MatcherWasMovedOrCopied();
     }
-
-    struct EvilAddressOfOperatorUsed : std::exception {
-        EvilAddressOfOperatorUsed() {}
-        const char* what() const noexcept override {
-            return "overloaded address-of operator of matcher was used instead of std::addressof";
-        }
-    };
-
-    struct EvilCommaOperatorUsed : std::exception {
-        EvilCommaOperatorUsed() {}
-        const char* what() const noexcept override {
-            return "overloaded comma operator of matcher was used";
-        }
-    };
-
-    struct EvilMatcher : Catch::Matchers::MatcherGenericBase {
-        std::string describe() const override {
-            return "equals: 45";
-        }
-
-        bool match(int i) const {
-            return i == 45;
-        }
-
-        EvilMatcher const* operator& () const {
-            throw EvilAddressOfOperatorUsed();
-        }
-
-        int operator,(EvilMatcher const&) const {
-            throw EvilCommaOperatorUsed();
-        }
-    };
-
-    TEST_CASE("Overloaded comma or address-of operators are not used", "[matchers][templated]") {
-        REQUIRE_THROWS_AS((EvilMatcher(), EvilMatcher()), EvilCommaOperatorUsed);
-        REQUIRE_THROWS_AS(&EvilMatcher(), EvilAddressOfOperatorUsed);
-        REQUIRE_NOTHROW(EvilMatcher() || (EvilMatcher() && !EvilMatcher()));
-        REQUIRE_NOTHROW((EvilMatcher() && EvilMatcher()) || !EvilMatcher());
+    ThrowOnCopyOrMoveMatcher& operator=( ThrowOnCopyOrMoveMatcher&& ) {
+        throw MatcherWasMovedOrCopied();
     }
 
-    struct ImmovableMatcher : Catch::Matchers::MatcherGenericBase {
-        ImmovableMatcher() = default;
-        ImmovableMatcher(ImmovableMatcher const&) = delete;
-        ImmovableMatcher(ImmovableMatcher &&) = delete;
-        ImmovableMatcher& operator=(ImmovableMatcher const&) = delete;
-        ImmovableMatcher& operator=(ImmovableMatcher &&) = delete;
+    std::string describe() const override { return "always false"; }
 
-        std::string describe() const override {
-            return "always false";
-        }
-
-        template<typename T>
-        bool match(T&&) const {
-            return false;
-        }
-    };
-
-    struct MatcherWasMovedOrCopied : std::exception {
-        MatcherWasMovedOrCopied() {}
-        const char* what() const noexcept override {
-            return "attempted to copy or move a matcher";
-        }
-    };
+    template <typename T> bool match( T&& ) const { return false; }
+};
 
-    struct ThrowOnCopyOrMoveMatcher : Catch::Matchers::MatcherGenericBase {
-        ThrowOnCopyOrMoveMatcher() = default;
-        [[noreturn]]
-        ThrowOnCopyOrMoveMatcher(ThrowOnCopyOrMoveMatcher const&): Catch::Matchers::MatcherGenericBase() {
-            throw MatcherWasMovedOrCopied();
-        }
-        [[noreturn]]
-        ThrowOnCopyOrMoveMatcher(ThrowOnCopyOrMoveMatcher &&): Catch::Matchers::MatcherGenericBase() {
-            throw MatcherWasMovedOrCopied();
-        }
-        ThrowOnCopyOrMoveMatcher& operator=(ThrowOnCopyOrMoveMatcher const&) {
-            throw MatcherWasMovedOrCopied();
-        }
-        ThrowOnCopyOrMoveMatcher& operator=(ThrowOnCopyOrMoveMatcher &&) {
-            throw MatcherWasMovedOrCopied();
-        }
-
-        std::string describe() const override {
-            return "always false";
-        }
+TEST_CASE( "Matchers are not moved or copied",
+           "[matchers][templated][approvals]" ) {
+    REQUIRE_NOTHROW(
+        ( ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher() ) ||
+        !ThrowOnCopyOrMoveMatcher() );
+}
 
-        template<typename T>
-        bool match(T&&) const {
-            return false;
-        }
-    };
+TEST_CASE( "Immovable matchers can be used",
+           "[matchers][templated][approvals]" ) {
+    REQUIRE_THAT( 123,
+                  ( ImmovableMatcher() && ImmovableMatcher() ) ||
+                      !ImmovableMatcher() );
+}
 
-    TEST_CASE("Matchers are not moved or copied", "[matchers][templated][approvals]") {
-        REQUIRE_NOTHROW((ThrowOnCopyOrMoveMatcher() && ThrowOnCopyOrMoveMatcher()) || !ThrowOnCopyOrMoveMatcher());
-    }
+struct ReferencingMatcher : Catch::Matchers::MatcherGenericBase {
+    std::string describe() const override { return "takes reference"; }
+    bool match( int& i ) const { return i == 22; }
+};
 
-    TEST_CASE("Immovable matchers can be used", "[matchers][templated][approvals]") {
-        REQUIRE_THAT(123, (ImmovableMatcher() && ImmovableMatcher()) || !ImmovableMatcher());
-    }
+TEST_CASE( "Matchers can take references",
+           "[matchers][templated][approvals]" ) {
+    REQUIRE_THAT( 22, ReferencingMatcher{} );
+}
 
-    struct ReferencingMatcher : Catch::Matchers::MatcherGenericBase {
-        std::string describe() const override {
-            return "takes reference";
-        }
-        bool match(int& i) const {
-            return i == 22;
-        }
-    };
+#ifdef __clang__
+#    pragma clang diagnostic pop
+#endif
 
-    TEST_CASE("Matchers can take references", "[matchers][templated][approvals]") {
-        REQUIRE_THAT(22, ReferencingMatcher{});
-    }
+TEMPLATE_TEST_CASE(
+    "#2152 - ULP checks between differently signed values were wrong",
+    "[matchers][floating-point][ulp]",
+    float,
+    double ) {
+    using Catch::Matchers::WithinULP;
 
-} } // namespace MatchersTests
+    static constexpr TestType smallest_non_zero =
+        std::numeric_limits<TestType>::denorm_min();
 
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
+    CHECK_THAT( smallest_non_zero, WithinULP( -smallest_non_zero, 2 ) );
+    CHECK_THAT( smallest_non_zero, !WithinULP( -smallest_non_zero, 1 ) );
+}
diff --git a/packages/Catch2/tests/SelfTest/UsageTests/Misc.tests.cpp b/packages/Catch2/tests/SelfTest/UsageTests/Misc.tests.cpp
index 277723a8b71f1621c852f862dd22672450b9836c..e3b9bd3a23881ca2fe953a8ae6cd1dbd65e84c0f 100644
--- a/packages/Catch2/tests/SelfTest/UsageTests/Misc.tests.cpp
+++ b/packages/Catch2/tests/SelfTest/UsageTests/Misc.tests.cpp
@@ -19,60 +19,58 @@
 #include <array>
 #include <tuple>
 
-namespace { namespace MiscTests {
+namespace {
 
-#ifndef MISC_TEST_HELPERS_INCLUDED // Don't compile this more than once per TU
-#define MISC_TEST_HELPERS_INCLUDED
+    static const char* makeString(bool makeNull) {
+        return makeNull ? nullptr : "valid string";
+    }
+    static bool testCheckedIf(bool flag) {
+        CHECKED_IF(flag)
+            return true;
+    else
+    return false;
+    }
+    static bool testCheckedElse(bool flag) {
+        CHECKED_ELSE(flag)
+            return false;
 
-inline const char* makeString( bool makeNull ) {
-    return makeNull ? nullptr : "valid string";
-}
-inline bool testCheckedIf( bool flag )  {
-    CHECKED_IF( flag )
         return true;
-    else
-        return false;
-}
-inline bool testCheckedElse( bool flag ) {
-    CHECKED_ELSE( flag )
-        return false;
+    }
 
-    return true;
-}
+    static unsigned int Factorial(unsigned int number) {
+        return number > 1 ? Factorial(number - 1) * number : 1;
+    }
 
-inline unsigned int Factorial( unsigned int number )  {
-    return number > 1 ? Factorial(number-1)*number : 1;
-}
+    static int f() {
+        return 1;
+    }
 
-static int f() {
-    return 1;
-}
+    static void manuallyRegisteredTestFunction() {
+        SUCCEED("was called");
+    }
 
-inline void manuallyRegisteredTestFunction() {
-    SUCCEED( "was called" );
-}
+    struct AutoTestReg {
+        AutoTestReg() {
+            REGISTER_TEST_CASE(manuallyRegisteredTestFunction, "ManuallyRegistered");
+        }
+    };
 
-struct AutoTestReg {
-    AutoTestReg() {
-        REGISTER_TEST_CASE( manuallyRegisteredTestFunction, "ManuallyRegistered" );
-    }
-};
+    CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
+    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
+    static AutoTestReg autoTestReg;
+    CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
 
-CATCH_INTERNAL_START_WARNINGS_SUPPRESSION
-CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
-static AutoTestReg autoTestReg;
-CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION
+        template<typename T>
+    struct Foo {
+        size_t size() { return 0; }
+    };
 
-template<typename T>
-struct Foo {
-    size_t size() { return 0; }
-};
+    template<typename T, size_t S>
+    struct Bar {
+        size_t size() { return S; }
+    };
 
-template<typename T, size_t S>
-struct Bar {
-    size_t size() { return S; }
-};
-#endif
+}
 
 TEST_CASE( "random SECTION tests", "[.][sections][failing]" ) {
     int a = 1;
@@ -182,6 +180,39 @@ TEST_CASE( "checkedElse, failing", "[failing][.]" ) {
     REQUIRE( testCheckedElse( false ) );
 }
 
+TEST_CASE("Testing checked-if", "[checked-if]") {
+    CHECKED_IF(true) {
+        SUCCEED();
+    }
+    CHECKED_IF(false) {
+        FAIL();
+    }
+    CHECKED_ELSE(true) {
+        FAIL();
+    }
+    CHECKED_ELSE(false) {
+        SUCCEED();
+    }
+}
+
+TEST_CASE("Testing checked-if 2", "[checked-if][!shouldfail]") {
+    CHECKED_IF(true) {
+        FAIL();
+    }
+    // If the checked if is not entered, this passes and the test
+    // fails, because of the [!shouldfail] tag.
+    SUCCEED();
+}
+
+TEST_CASE("Testing checked-if 3", "[checked-if][!shouldfail]") {
+    CHECKED_ELSE(false) {
+        FAIL();
+    }
+    // If the checked false is not entered, this passes and the test
+    // fails, because of the [!shouldfail] tag.
+    SUCCEED();
+}
+
 TEST_CASE( "xmlentitycheck" ) {
     SECTION( "embedded xml: <test>it should be possible to embed xml characters, such as <, \" or &, or even whole <xml>documents</xml> within an attribute</test>" ) {
         SUCCEED(); // We need this here to stop it failing due to no tests
@@ -489,4 +520,5 @@ TEMPLATE_TEST_CASE_SIG("#1954 - 7 arg template test case sig compiles", "[regres
     SUCCEED();
 }
 
-}} // namespace MiscTests
+TEST_CASE("Same test name but with different tags is fine", "[.approvals][some-tag]") {}
+TEST_CASE("Same test name but with different tags is fine", "[.approvals][other-tag]") {}
diff --git a/packages/Catch2/tests/TestScripts/testPartialTestCaseEvent.py b/packages/Catch2/tests/TestScripts/testPartialTestCaseEvent.py
new file mode 100755
index 0000000000000000000000000000000000000000..d119e6dab81361ca9fb7db882c82148b0ac0a742
--- /dev/null
+++ b/packages/Catch2/tests/TestScripts/testPartialTestCaseEvent.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+
+"""
+This test script verifies that the testCasePartial{Starting,Ended} reporter
+events fire properly. This is done by calling a test binary compiled with
+reporter that reports specifically testCase* events, and verifying the
+outputs match what we expect.
+"""
+
+import subprocess
+import sys
+
+expected_section_output = '''\
+TestCaseStarting: section
+TestCaseStartingPartial: section#0
+TestCasePartialEnded: section#0
+TestCaseStartingPartial: section#1
+TestCasePartialEnded: section#1
+TestCaseStartingPartial: section#2
+TestCasePartialEnded: section#2
+TestCaseStartingPartial: section#3
+TestCasePartialEnded: section#3
+TestCaseEnded: section
+'''
+
+expected_generator_output = '''\
+TestCaseStarting: generator
+TestCaseStartingPartial: generator#0
+TestCasePartialEnded: generator#0
+TestCaseStartingPartial: generator#1
+TestCasePartialEnded: generator#1
+TestCaseStartingPartial: generator#2
+TestCasePartialEnded: generator#2
+TestCaseStartingPartial: generator#3
+TestCasePartialEnded: generator#3
+TestCaseEnded: generator
+'''
+
+
+from typing import List
+
+def get_test_output(test_exe: str, sections: bool) -> List[str]:
+    cmd = [test_exe, '--reporter', 'partial']
+    if sections:
+        cmd.append('section')
+    else:
+        cmd.append('generator')
+
+    ret = subprocess.run(cmd,
+                         stdout = subprocess.PIPE,
+                         stderr = subprocess.PIPE,
+                         timeout = 10,
+                         check = True,
+                         universal_newlines = True)
+
+    return ret.stdout
+
+def main():
+    test_exe, = sys.argv[1:]
+    actual_section_output = get_test_output(test_exe, sections = True)
+
+    assert actual_section_output == expected_section_output, (
+    'Sections\nActual:\n{}\nExpected:\n{}\n'.format(actual_section_output, expected_section_output))
+
+    actual_generator_output = get_test_output(test_exe, sections = False)
+    assert actual_generator_output == expected_generator_output, (
+    'Generators\nActual:\n{}\nExpected:\n{}\n'.format(actual_generator_output, expected_generator_output))
+
+
+
+if __name__ == '__main__':
+    sys.exit(main())
diff --git a/packages/Catch2/tools/misc/appveyorBuildConfigurationScript.bat b/packages/Catch2/tools/misc/appveyorBuildConfigurationScript.bat
index 9ebd6ae0d81780592a5ab42a9833cc9308c8f0e4..d9be52eb76717ab83d6feac9ee1d28d6417de68e 100644
--- a/packages/Catch2/tools/misc/appveyorBuildConfigurationScript.bat
+++ b/packages/Catch2/tools/misc/appveyorBuildConfigurationScript.bat
@@ -13,7 +13,7 @@ if "%CONFIGURATION%"=="Debug" (
     cmake -H. -BBuild -A%PLATFORM% -DCATCH_TEST_USE_WMAIN=%wmain% -DMEMORYCHECK_COMMAND=build-misc\Debug\CoverageHelper.exe -DMEMORYCHECK_COMMAND_OPTIONS=--sep-- -DMEMORYCHECK_TYPE=Valgrind -DCATCH_BUILD_EXAMPLES=%examples% -DCATCH_BUILD_EXTRA_TESTS=%examples% -DCATCH_DEVELOPMENT_BUILD=ON || exit /b !ERRORLEVEL!
   ) else (
     @REM # We know that coverage is 0
-    cmake -H. -BBuild -A%PLATFORM% -DCATCH_TEST_USE_WMAIN=%wmain% -DCATCH_BUILD_EXAMPLES=%examples% -DCATCH_BUILD_EXTRA_TESTS=%examples% -DCATCH_DEVELOPMENT_BUILD=ON || exit /b !ERRORLEVEL!
+    cmake -H. -BBuild -A%PLATFORM% -DCATCH_TEST_USE_WMAIN=%wmain% -DCATCH_BUILD_EXAMPLES=%examples% -DCATCH_BUILD_EXTRA_TESTS=%examples% -DCATCH_BUILD_SURROGATES=%surrogates% -DCATCH_DEVELOPMENT_BUILD=ON || exit /b !ERRORLEVEL!
   )
 )
 if "%CONFIGURATION%"=="Release" (
diff --git a/packages/Catch2/tools/scripts/releaseCommon.py b/packages/Catch2/tools/scripts/releaseCommon.py
index 6226bdc067cdc773255ce52057f37e06a7787bd9..925e801486f812627cf84c3a3ec77380dfd37d14 100644
--- a/packages/Catch2/tools/scripts/releaseCommon.py
+++ b/packages/Catch2/tools/scripts/releaseCommon.py
@@ -127,8 +127,8 @@ def updateVersionDefine(version):
 def updateVersionPlaceholder(filename, version):
     with open(filename, 'rb') as file:
         lines = file.readlines()
-    placeholderRegex = re.compile(b' in Catch X.Y.Z')
-    replacement = ' in Catch {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii')
+    placeholderRegex = re.compile(b'in Catch[0-9]? X.Y.Z')
+    replacement = 'in Catch2 {}.{}.{}'.format(version.majorVersion, version.minorVersion, version.patchNumber).encode('ascii')
     with open(filename, 'wb') as file:
         for line in lines:
             file.write(placeholderRegex.sub(replacement, line))
diff --git a/packages/PEGTL/.clang-format b/packages/PEGTL/.clang-format
index c423ca2749e808e112ba69db6d6f33ee76ca955f..a3f254129f2326865c1ca172033c1b99f63f3a11 100644
--- a/packages/PEGTL/.clang-format
+++ b/packages/PEGTL/.clang-format
@@ -1,6 +1,9 @@
-# the official .clang-format style for https://github.com/taocpp
-#
-# clang-format -i -style=file $(find . -name '[^.]*.[hc]pp')
+# The Art of C++
+# https://github.com/PEGTL
+
+# Copyright (c) 2016-2021 Daniel Frey
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 Language: Cpp
 Standard: Latest
diff --git a/packages/PEGTL/.clang-tidy b/packages/PEGTL/.clang-tidy
index cad3a9f3fc344995216d51ad6ae3df9821f03a0f..b98e0d8b54747e3497f2e0376815d17fa55c98ac 100644
--- a/packages/PEGTL/.clang-tidy
+++ b/packages/PEGTL/.clang-tidy
@@ -1,3 +1,10 @@
+# The Art of C++
+# https://github.com/PEGTL
+
+# Copyright (c) 2016-2021 Daniel Frey
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
 Checks: >-
   bugprone-*,
   cppcoreguidelines-slicing,
diff --git a/packages/PEGTL/.github/workflows/android.yml b/packages/PEGTL/.github/workflows/android.yml
index 8cf72544ebb4990caf3a62379218b524d5450c1b..44515ed7c67d1f4cfda8c7a4f35e5c735bc3d243 100644
--- a/packages/PEGTL/.github/workflows/android.yml
+++ b/packages/PEGTL/.github/workflows/android.yml
@@ -1,6 +1,14 @@
 name: Android
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   android:
diff --git a/packages/PEGTL/.github/workflows/clang-analyze.yml b/packages/PEGTL/.github/workflows/clang-analyze.yml
index 523b105564db92155b5cac93281e9e5d36776868..3919398d2e2a4c3402d4d95d86bbddbae4f4a410 100644
--- a/packages/PEGTL/.github/workflows/clang-analyze.yml
+++ b/packages/PEGTL/.github/workflows/clang-analyze.yml
@@ -1,6 +1,14 @@
 name: clang-analyze
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   clang-analyze:
@@ -9,6 +17,8 @@ jobs:
     steps:
     - uses: actions/checkout@v2
 
+    - run: sudo apt-get update -yq
+
     - run: sudo apt-get install -yq clang-tools
 
     - run: scan-build cmake -E make_directory build
diff --git a/packages/PEGTL/.github/workflows/clang-format.yml b/packages/PEGTL/.github/workflows/clang-format.yml
index ac69e2ed6d003d15f1acf8a883c88e38b7c1d9bb..a6aae1ae70f2a07d4c113f98034ece5124e5ec52 100644
--- a/packages/PEGTL/.github/workflows/clang-format.yml
+++ b/packages/PEGTL/.github/workflows/clang-format.yml
@@ -1,6 +1,14 @@
 name: clang-format
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   clang-format:
@@ -8,7 +16,7 @@ jobs:
 
     steps:
     - uses: actions/checkout@v2
-    - uses: DoozyX/clang-format-lint-action@v0.11
+    - uses: DoozyX/clang-format-lint-action@v0.12
       with:
         extensions: 'hpp,cpp'
-        clangFormatVersion: 11
+        clangFormatVersion: 12
diff --git a/packages/PEGTL/.github/workflows/clang-tidy.yml b/packages/PEGTL/.github/workflows/clang-tidy.yml
index 4da8af6ff8b1695835e65404a825f51d673bc49a..6f640820ac1e9ea0652c3f483b463346e27801d6 100644
--- a/packages/PEGTL/.github/workflows/clang-tidy.yml
+++ b/packages/PEGTL/.github/workflows/clang-tidy.yml
@@ -1,6 +1,14 @@
 name: clang-tidy
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   clang-tidy:
@@ -9,6 +17,8 @@ jobs:
     steps:
     - uses: actions/checkout@v2
 
+    - run: sudo apt-get update -yq
+
     - run: sudo apt-get install -yq clang-tidy
 
     - run: find include/ -name '*.hpp' | grep -vF file_mapper_win32.hpp | grep -vF endian_win.hpp | xargs -I '{}' clang-tidy --quiet '{}' -- --std=c++17 -Iinclude
diff --git a/packages/PEGTL/.github/workflows/code-coverage.yml b/packages/PEGTL/.github/workflows/code-coverage.yml
index 320e77d8c8545ef8dfe2e7c1d5a153e2615ed76c..537752ad00839fed46cebed0281f35d6f09a6f5b 100644
--- a/packages/PEGTL/.github/workflows/code-coverage.yml
+++ b/packages/PEGTL/.github/workflows/code-coverage.yml
@@ -1,6 +1,14 @@
 name: Code Coverage
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   code-coverage:
diff --git a/packages/PEGTL/.github/workflows/codeql-analysis.yml b/packages/PEGTL/.github/workflows/codeql-analysis.yml
index a2d930ad89209daf36ed8b234c83a3bb3e6fb87e..d0f90dfac25b137fc821ff4e7b08d572c74c9741 100644
--- a/packages/PEGTL/.github/workflows/codeql-analysis.yml
+++ b/packages/PEGTL/.github/workflows/codeql-analysis.yml
@@ -13,10 +13,16 @@ name: "CodeQL"
 
 on:
   push:
-    branches: [ master ]
+    branches: [ main ]
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
   pull_request:
     # The branches below must be a subset of the branches above
-    branches: [ master ]
+    branches: [ main ]
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
   schedule:
     - cron: '31 6 * * 0'
 
diff --git a/packages/PEGTL/.github/workflows/linux.yml b/packages/PEGTL/.github/workflows/linux.yml
index 139982af19acf195bf688fc94db6ac22e85b841e..d6f6d6ef76184ee0141ea1e8a74e612c958c2add 100644
--- a/packages/PEGTL/.github/workflows/linux.yml
+++ b/packages/PEGTL/.github/workflows/linux.yml
@@ -1,6 +1,14 @@
 name: Linux
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   linux:
@@ -10,9 +18,10 @@ jobs:
         compiler:
           - g++-9
           - g++-10
-          - clang++-9
+          #- g++-11
           - clang++-10
           - clang++-11
+          - clang++-12
         build_type: [Debug, Release]
 
     runs-on: ubuntu-latest
@@ -44,6 +53,7 @@ jobs:
           - clang++-6.0
           - clang++-7
           - clang++-8
+          - clang++-9
         build_type: [Debug, Release]
 
     runs-on: ubuntu-latest
@@ -54,9 +64,9 @@ jobs:
     steps:
     - uses: actions/checkout@v2
 
-    - run: sudo apt-get update
+    - run: sudo apt-get update -yq
 
-    - run: sudo apt-get install -y ${{ matrix.compiler }}
+    - run: sudo apt-get install -yq ${{ matrix.compiler }}
 
     - run: cmake -E make_directory build
 
diff --git a/packages/PEGTL/.github/workflows/macos.yml b/packages/PEGTL/.github/workflows/macos.yml
index 78c479511c47895ea6eb44b452d31b343036eacc..a2dfade495df59b9173a2d79d7740606d127692e 100644
--- a/packages/PEGTL/.github/workflows/macos.yml
+++ b/packages/PEGTL/.github/workflows/macos.yml
@@ -1,6 +1,14 @@
 name: macOS
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   xcode:
diff --git a/packages/PEGTL/.github/workflows/no-exceptions.yml b/packages/PEGTL/.github/workflows/no-exceptions.yml
index eeb964f2540b1a6711afc9c689f155c7ee63b827..25c49e0c53fd8fc95742cffe4184c690f126323a 100644
--- a/packages/PEGTL/.github/workflows/no-exceptions.yml
+++ b/packages/PEGTL/.github/workflows/no-exceptions.yml
@@ -1,6 +1,14 @@
 name: No-Exceptions
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   no-exceptions:
diff --git a/packages/PEGTL/.github/workflows/sanitizer.yml b/packages/PEGTL/.github/workflows/sanitizer.yml
index 61d61aebdfa1b6640934ed95dfeeb4581a6e8b4a..5bcb8a9b045adb1a0620e298817a503abacd7fe5 100644
--- a/packages/PEGTL/.github/workflows/sanitizer.yml
+++ b/packages/PEGTL/.github/workflows/sanitizer.yml
@@ -1,6 +1,14 @@
 name: Sanitizer
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   sanitizer:
diff --git a/packages/PEGTL/.github/workflows/windows.yml b/packages/PEGTL/.github/workflows/windows.yml
index 760efb9c85eaa444ba67299f1936ec5422ccb6c2..e474984856325ae1cd2f8e064bf804161d8755a5 100644
--- a/packages/PEGTL/.github/workflows/windows.yml
+++ b/packages/PEGTL/.github/workflows/windows.yml
@@ -1,6 +1,14 @@
 name: Windows
 
-on: [push, pull_request]
+on:
+  push:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
+  pull_request:
+    paths-ignore:
+      - 'README.md'
+      - 'doc/**'
 
 jobs:
   vs2019:
diff --git a/packages/PEGTL/.gitrepo b/packages/PEGTL/.gitrepo
index d60e16e83cde84179e4ec27115e58d51dd0cd242..279480bff62486421386892258910b04da453b3a 100644
--- a/packages/PEGTL/.gitrepo
+++ b/packages/PEGTL/.gitrepo
@@ -5,8 +5,8 @@
 ;
 [subrepo]
 	remote = git@github.com:taocpp/PEGTL.git
-	branch = master
-	commit = c131c2e2aad67037285ef39d11ec4f1d28d4fc73
-	parent = 2f2fa0e22bd114f44f78c5bee89bc13bd0959d1d
+	branch = main
+	commit = bf4487c9793121e483291c4e516cec3e5c1c17b5
+	parent = e00b72ebcd9d2add12cfe0e6fe4d114a7858dfa5
 	method = merge
 	cmdver = 0.4.3
diff --git a/packages/PEGTL/CMakeLists.txt b/packages/PEGTL/CMakeLists.txt
index c2ee336f31b4c18f309adfa94c77db6f99978936..2233099fa6181105a7e6053740984913bc856534 100644
--- a/packages/PEGTL/CMakeLists.txt
+++ b/packages/PEGTL/CMakeLists.txt
@@ -147,7 +147,7 @@ install(EXPORT pegtl-targets
 
 install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pegtl-config-version.cmake DESTINATION ${PEGTL_INSTALL_CMAKE_DIR})
 install(DIRECTORY include/ DESTINATION ${PEGTL_INSTALL_INCLUDE_DIR})
-install(FILES LICENSE DESTINATION ${PEGTL_INSTALL_DOC_DIR})
+install(FILES LICENSE_1_0.txt DESTINATION ${PEGTL_INSTALL_DOC_DIR})
 
 export(EXPORT pegtl-targets
   FILE ${pegtl_BINARY_DIR}/pegtl-targets.cmake
diff --git a/packages/PEGTL/LICENSE b/packages/PEGTL/LICENSE
deleted file mode 100644
index c981590829a1ee30e859ac04a31844398be0ae8e..0000000000000000000000000000000000000000
--- a/packages/PEGTL/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2007-2021 Dr. Colin Hirsch and Daniel Frey
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/packages/PEGTL/LICENSE_1_0.txt b/packages/PEGTL/LICENSE_1_0.txt
new file mode 100644
index 0000000000000000000000000000000000000000..36b7cd93cdfbac762f5be4c6ce276df2ea6305c2
--- /dev/null
+++ b/packages/PEGTL/LICENSE_1_0.txt
@@ -0,0 +1,23 @@
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/packages/PEGTL/Makefile b/packages/PEGTL/Makefile
index 3ecca070e2264b4af4542b275f0872bf2c2a32a8..773c525bbc936e079e33e33084a45ed33d5e7469 100644
--- a/packages/PEGTL/Makefile
+++ b/packages/PEGTL/Makefile
@@ -1,6 +1,9 @@
 # The Art of C++
+# https://github.com/PEGTL
+
 # Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-# Please see LICENSE for license or visit https://github.com/taocpp/PEGTL
+# Distributed under the Boost Software License, Version 1.0.
+# (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 .SUFFIXES:
 .SECONDARY:
@@ -61,31 +64,6 @@ build/%.d: %.cpp Makefile
 build/%: %.cpp build/%.d
 	$(CXX) $(CXXSTD) -Iinclude $(CPPFLAGS) $(CXXFLAGS) $< $(LDFLAGS) -o $@
 
-.PHONY: amalgamate
-amalgamate: build/amalgamated/pegtl.hpp
-
-build/amalgamated/pegtl.hpp: $(HEADERS)
-	@mkdir -p $(@D)
-	@rm -rf build/include
-	@cp -a include build/
-	@rm -rf build/include/tao/pegtl/contrib/icu
-	@sed -i -e 's%^#%//#%g' $$(find build/include -name '*.hpp')
-	@sed -i -e 's%^//#include "%#include "%g' $$(find build/include -name '*.hpp')
-	@for i in $$(find build/include -name '*.hpp'); do echo "#pragma once" >tmp.out; echo "#line 1" >>tmp.out; cat $$i >>tmp.out; mv tmp.out $$i; done
-	@echo '#include "tao/pegtl.hpp"' >build/include/amalgamated.hpp
-	@( cd build/include ; for i in tao/pegtl/contrib/*.hpp; do echo "#include \"$$i\""; done ) >>build/include/amalgamated.hpp
-	@echo -e "/*\n\nWelcome to the Parsing Expression Grammar Template Library (PEGTL)." >$@
-	@echo -e "See https://github.com/taocpp/PEGTL/ for more information, documentation, etc.\n" >>$@
-	@echo -e "The library is licensed as follows:\n" >>$@
-	@cat LICENSE >>$@
-	@echo -e "\n*/\n" >>$@
-	@( cd build/include ; g++ -E -C -nostdinc amalgamated.hpp ) >>$@
-	@sed -i -e 's%^//#%#%g' $@
-	@sed -i -e 's%^# \([0-9]* "[^"]*"\).*%#line \1%g' $@
-	@sed -i -e 's%^// Copyright.*%%g' $@
-	@sed -i -e 's%^// Please.*%%g' $@
-	@echo "Generated/updated $@ successfully."
-
 ifeq ($(findstring $(MAKECMDGOALS),clean),)
 -include $(DEPENDS)
 endif
diff --git a/packages/PEGTL/README.md b/packages/PEGTL/README.md
index e613c97f9b5a542d542103b3251bf9067806bdc6..399b2bb3a7c3e74c628a5df6a11fb7731f30e250 100644
--- a/packages/PEGTL/README.md
+++ b/packages/PEGTL/README.md
@@ -9,7 +9,7 @@
 [![clang-tidy](https://github.com/taocpp/PEGTL/workflows/clang-tidy/badge.svg)](https://github.com/taocpp/PEGTL/actions?query=workflow%3Aclang-tidy)
 [![Sanitizer](https://github.com/taocpp/PEGTL/workflows/Sanitizer/badge.svg)](https://github.com/taocpp/PEGTL/actions?query=workflow%3ASanitizer)
 [![CodeQL](https://github.com/taocpp/PEGTL/workflows/CodeQL/badge.svg)](https://github.com/taocpp/PEGTL/actions?query=workflow%3ACodeQL)
-[![Code Coverage](https://codecov.io/gh/taocpp/PEGTL/branch/master/graph/badge.svg?token=ykWa8RRdyk)](https://codecov.io/gh/taocpp/PEGTL)
+[![Code Coverage](https://codecov.io/gh/taocpp/PEGTL/branch/main/graph/badge.svg?token=ykWa8RRdyk)](https://codecov.io/gh/taocpp/PEGTL)
 
 The Parsing Expression Grammar Template Library (PEGTL) is a zero-dependency C++ header-only parser combinator library for creating parsers according to a [Parsing Expression Grammar](http://en.wikipedia.org/wiki/Parsing_expression_grammar) (PEG).
 
@@ -136,16 +136,17 @@ The PEGTL is part of [The Art of C++](https://taocpp.github.io/).
 
 ## License
 
-<a href="https://opensource.org/licenses/MIT"><img align="right" src="https://opensource.org/files/OSIApproved.png" width="150" hspace="20" alt="Open Source Initiative"></a>
+<a href="https://opensource.org/licenses/BSL-1.0"><img align="right" src="https://opensource.org/files/OSIApproved.png" width="150" hspace="20" alt="Open Source Initiative"></a>
+
+Copyright (c) 2007-2021 Daniel Frey and Dr. Colin Hirsch
 
 The PEGTL is certified [Open Source](http://www.opensource.org/docs/definition.html) software.
-It may be used for any purpose, including commercial purposes, at absolutely no cost.
-It is distributed under the terms of the [MIT license](http://www.opensource.org/licenses/mit-license.html) reproduced here.
+It is [licensed](https://pdimov.github.io/blog/2020/09/06/why-use-the-boost-license/) under the terms of the [Boost Software License, Version 1.0](https://www.boost.org/LICENSE_1_0.txt) reproduced here.
 
-> Copyright (c) 2007-2021 Dr. Colin Hirsch and Daniel Frey
+> Boost Software License - Version 1.0 - August 17th, 2003
 >
-> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+> Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following:
 >
-> The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+> The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor.
 >
-> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/packages/PEGTL/doc/Actions-and-States.md b/packages/PEGTL/doc/Actions-and-States.md
index 4c8101682972bde687cad06b2a1d96024e7afd79..272b8ac460ccd0c15bd2225142a42542e90d0865 100644
--- a/packages/PEGTL/doc/Actions-and-States.md
+++ b/packages/PEGTL/doc/Actions-and-States.md
@@ -383,6 +383,7 @@ The [`state` rule](Rule-Reference.md#state-s-r-) behaves similarly to [`seq`](Ru
 This new object is used replaces the current state(s) for the remainder of the implicit [`seq`](Rule-Reference.md#seq-r-).
 
 The new object is constructed with a const-reference to the current input of the parsing run, and all previous states, if any, as arguments.
+If no such constructor exists, the new object is default constructed.
 If the implicit [`seq`](Rule-Reference.md#seq-r-) of the sub-rules succeeds, then, by default, a member function named `success()` is called on this "new" object, receiving the same arguments as the constructor.
 At this point the input will be advanced by whatever the sub-rules have consumed in the meantime.
 
@@ -399,7 +400,7 @@ The differences are summarised in this table; note that `change_state` is more s
 | Feature | `change_state` | `change_states` |
 | --- | --- | --- |
 | Number of new states | one | any |
-| Construction of new states | with input and old states | default |
+| Construction of new states | optionally with input and old states | default |
 | Success function on action | if not on new state | required |
 
 With `change_state` only a single new state type can be given as template parameter, and only a single new state will be created.
@@ -603,4 +604,10 @@ Note that deriving from `require_apply` or `require_apply0` is optional and usua
 
 See the [section on legacy-style action rules](Rule-Reference.md#action-rules).
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Changelog.md b/packages/PEGTL/doc/Changelog.md
index cc194318f8cd00dc5a894e713003369036b02506..aca8f740a1f549f4f21702bfdd76862fdff29dc8 100644
--- a/packages/PEGTL/doc/Changelog.md
+++ b/packages/PEGTL/doc/Changelog.md
@@ -1,10 +1,32 @@
 # Changelog
 
-## 3.2.1
+## 3.3.0
 
 **Not yet released**
 
+* Switched to Boost Software License, Version 1.0.
+* Removed support for building an amalgamated header.
+* Deprecated the `TAO_PEGTL_NAMESPACE` macro.
+  * Will be removed in version 4.0.0.
+
+## 3.2.2
+
+Released 2021-10-22
+
+* Added rule [`odigit`](Rule-Reference.md#odigit) for octal digits.
+* Enabled default-constructed state in `state<>`, `change_state<>`, and `change_action_and_state<>`.
+* Changed rules in [`tao/pegtl/contrib/integer.hpp`](Contrib-and-Examples.md#taopegtlcontribintegerhpp) to not throw by default.
+* Added [`tao/pegtl/contrib/separated_seq.hpp`](Contrib-and-Examples.md#taopegtlcontribseparated_seqhpp).
+* Added `tao/pegtl/contrib/iri.hpp` grammar for IRIs.
+* Added `tao/pegtl/contrib/proto3.hpp` grammar for protocol buffer v3.
+
+## 3.2.1
+
+Released 2021-07-31
+
 * Added an optional limiter to guard against infinite recursion.
+* Fixed CMake export error.
+* Improved compile time efficiency.
 
 ## 3.2.0
 
@@ -625,4 +647,10 @@ Released 2008
 Development of the PEGTL started in November 2007 as an experiment in C++0x.
 It is based on ideas from the YARD library by Christopher Diggins.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2007-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Contrib-and-Examples.md b/packages/PEGTL/doc/Contrib-and-Examples.md
index 92c4032a29465e010f310ef1b40112e6273c5617..31f9035c161de1d791d8dc2b42eaf5662b11fcc1 100644
--- a/packages/PEGTL/doc/Contrib-and-Examples.md
+++ b/packages/PEGTL/doc/Contrib-and-Examples.md
@@ -40,6 +40,12 @@ For all questions and remarks contact us at **taocpp(at)icemx.net**.
 
 * Grammars and actions for PEGTL-input-to-integer conversions.
 
+###### `<tao/pegtl/contrib/limit_depth.hpp>`
+
+* Limits the nesting level of rules when parsing a grammar, prevents stack overflows.
+* Can be applied selectively at specific rules to improve efficiency.
+* See `src/test/pegtl/limit_depth.cpp`.
+
 ###### `<tao/pegtl/contrib/json.hpp>`
 
 * JSON grammar according to [RFC 7159](https://tools.ietf.org/html/rfc7159) (for UTF-8 encoded JSON only).
@@ -64,6 +70,11 @@ For all questions and remarks contact us at **taocpp(at)icemx.net**.
 * Contains optimised version of `rep_min_max< Min, Max, ascii::one< C > >`:
 * Rule `ascii::rep_one_min_max< Min, Max, C >`.
 
+###### `<tao/pegtl/contrib/separated_seq.hpp>`
+
+* Allows to parse rules separated by a separator.
+* Rule `separated_seq< S, A, B, C, D >` is equivalent to `seq< A, S, B, S, C, S, D >`.
+
 ###### `<tao/pegtl/contrib/to_string.hpp>`
 
 Utility function `to_string<>()` that converts template classes with arbitrary sequences of characters as template arguments into a `std::string` that contains these characters.
@@ -201,4 +212,10 @@ Uses the building blocks from `<tao/pegtl/contrib/unescape.hpp>` to show how to
 Shows how to use the included [tracer control](#taopegtlcontribtracerhpp), here together with the URI grammar from `<tao/pegtl/contrib/uri.hpp>`.
 Invoked with one or more URIs as command line arguments will attempt to parse the URIs while printing trace information to `std::cerr`.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Control-and-Debug.md b/packages/PEGTL/doc/Control-and-Debug.md
index 3ff6a88787a36b34de2397690b6cfca147edd9f0..10fa8f80a931247c2a4be54e7bacbe954aaa1473 100644
--- a/packages/PEGTL/doc/Control-and-Debug.md
+++ b/packages/PEGTL/doc/Control-and-Debug.md
@@ -144,4 +144,10 @@ Just like the action class template, a custom control class template can be used
 The latter requires the use of a [custom action](Actions-and-States.md).
 Deriving the specialisation of the custom action for `my_rule` from `tao::pegtl::change_control< my_control >` will switch the current control to `my_control` before attempting to match `my_rule`.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Errors-and-Exceptions.md b/packages/PEGTL/doc/Errors-and-Exceptions.md
index 7b8f018d84e757c919c2eae8f91864ccac87a060..a0b16d24c7bfc74228c504c38624de3cd5df7468 100644
--- a/packages/PEGTL/doc/Errors-and-Exceptions.md
+++ b/packages/PEGTL/doc/Errors-and-Exceptions.md
@@ -214,4 +214,10 @@ struct error
 It is advisable to choose the error points in the grammar with prudence.
 This choice becoming particularly cumbersome and/or resulting in a large number of error points might be an indication of the grammar needing some kind of simplification or restructuring.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Getting-Started.md b/packages/PEGTL/doc/Getting-Started.md
index 1deceba9f37ab81a91448682e0f39dcdbe1d8943..fbf71cbd304777526e661e0f50e0c2fb1b3eaf38 100644
--- a/packages/PEGTL/doc/Getting-Started.md
+++ b/packages/PEGTL/doc/Getting-Started.md
@@ -283,4 +283,10 @@ Typically, the following pattern helps to print the exceptions in a human friend
 
 For more information see [Errors and Exceptions](Errors-and-Exceptions.md).
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Grammar-Analysis.md b/packages/PEGTL/doc/Grammar-Analysis.md
index 629982d8ec624f154c86fb95e16edacec6704a5a..94bb40aef184878e3a5d7f1c17f520bdefbc1f8c 100644
--- a/packages/PEGTL/doc/Grammar-Analysis.md
+++ b/packages/PEGTL/doc/Grammar-Analysis.md
@@ -124,4 +124,10 @@ In practice it appears to catch all cases of left-recursion that are typical for
 
 False positives are a theoretical problem in that, while relatively easy to trigger, they are not usually encountered when dealing with real world grammars.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Inputs-and-Parsing.md b/packages/PEGTL/doc/Inputs-and-Parsing.md
index b2bb15f44672010dd71792fbc9e4ee06e0c33d00..96948728c3e62278d94feedb4083a9f764db382c 100644
--- a/packages/PEGTL/doc/Inputs-and-Parsing.md
+++ b/packages/PEGTL/doc/Inputs-and-Parsing.md
@@ -554,4 +554,10 @@ Trying to call any of those functions on `buffer_input<>`-based instances will l
 
 All input classes support [deduction guides](https://en.cppreference.com/w/cpp/language/class_template_argument_deduction), e.g. instead of `file_input<> in( "filename.txt" )` one can use `file_input in( "filename.txt" )`.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Installing-and-Using.md b/packages/PEGTL/doc/Installing-and-Using.md
index e235edfbaea9ccfade76f0587cddeaa227358a73..007987116287d8674c22b571c27239ca6e76a0a1 100644
--- a/packages/PEGTL/doc/Installing-and-Using.md
+++ b/packages/PEGTL/doc/Installing-and-Using.md
@@ -19,7 +19,6 @@
   * [Embedding in Binaries](#embedding-in-binaries)
   * [Embedding in Libraries](#embedding-in-libraries)
   * [Embedding in Library Interfaces](#embedding-in-library-interfaces)
-* [Single Header Version](#single-header-version)
 
 ## Requirements
 
@@ -98,8 +97,6 @@ Note that some of the listed packages are not updated regularly.
 
 ## Using Vcpkg
 
-[![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/pegtl.svg)](https://repology.org/project/pegtl/versions)
-
 You can download and install the PEGTL using the [Vcpkg] package manager:
 
 ```bash
@@ -115,8 +112,6 @@ For more options and ways to use Vcpkg, please refer to the [Vcpkg documentation
 
 ## Using Conan
 
-[![Download](https://api.bintray.com/packages/conan/conan-center/taocpp-pegtl%3A_/images/download.svg)](https://bintray.com/conan/conan-center/taocpp-pegtl%3A_/_latestVersion)
-
 You can download and install the PEGTL using the [Conan] package manager:
 
 ```bash
@@ -303,20 +298,13 @@ above; alternatively `include/tao/pegtl/config.hpp` can be directly modified.
 A practical example of how the result looks like can be found in our own
 header-only JSON library [taoJSON](https://github.com/taocpp/json/).
 
-## Single Header Version
-
-You can generate a single-header-version of the PEGTL with the included `Makefile`.
-In a Unix-shell, the following command will achieve this:
-
-```sh
-$ make amalgamate
-```
+---
 
-The above will generate a `build/amalgamated/pegtl.hpp` which will consist of
-the headers `tao/pegtl.hpp`, their dependencies, and all headers in
-`tao/pegtl/contrib/` except for the headers in `tao/pegtl/contrib/icu/`.
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
 
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
 
 [Vcpkg]: https://github.com/Microsoft/vcpkg
 [Vcpkg documentation]: https://github.com/Microsoft/vcpkg/tree/master/docs/index.md
diff --git a/packages/PEGTL/doc/Meta-Data-and-Visit.md b/packages/PEGTL/doc/Meta-Data-and-Visit.md
index 74f8672ca0d25e8d571b9dab9ff746b0c6034371..bed412efa030a9d8638dbee233b651a75276f7b9 100644
--- a/packages/PEGTL/doc/Meta-Data-and-Visit.md
+++ b/packages/PEGTL/doc/Meta-Data-and-Visit.md
@@ -147,4 +147,10 @@ struct coverage_result
 
 As usual, unless otherwise indicated, all functions and data structure are in the namespace `tao::pegtl`.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Migration-Guide.md b/packages/PEGTL/doc/Migration-Guide.md
index 91171f3b46050727eb42df083621ef46dc891c4c..19f543d970ad7274f79811b58c93c613850fe1f2 100644
--- a/packages/PEGTL/doc/Migration-Guide.md
+++ b/packages/PEGTL/doc/Migration-Guide.md
@@ -56,4 +56,10 @@ Please contact the authors at `taocpp(at)icemx.net` for any further questions wh
 There were [many important changes](Changelog.md#100) leading up to version 1.0.0.
 Please contact the authors at `taocpp(at)icemx.net` for any further questions when updating the PEGTL.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Parse-Tree.md b/packages/PEGTL/doc/Parse-Tree.md
index baa3b24a7fc1e4e54daac1e1e56d0f461900f336..e82f6cf7d49d932986bad8744bcbc7b386afc84b 100644
--- a/packages/PEGTL/doc/Parse-Tree.md
+++ b/packages/PEGTL/doc/Parse-Tree.md
@@ -94,7 +94,7 @@ template<> struct my_selector< my_rule_2 > : std::true_type
 };
 ```
 
-`transform` can modify `n` in any way you like, the [`parse_tree.cpp`](https://github.com/taocpp/PEGTL/blob/master/src/example/pegtl/parse_tree.cpp)-example shows two techniques for marking nodes as "content-less", and for transforming the parse tree into an AST.
+`transform` can modify `n` in any way you like, the [`parse_tree.cpp`](https://github.com/taocpp/PEGTL/blob/main/src/example/pegtl/parse_tree.cpp)-example shows two techniques for marking nodes as "content-less", and for transforming the parse tree into an AST.
 
 It is also possible to call `n.reset()`, or otherwise set `n` to an empty pointer, which effectively removes `n` (and all of its child nodes) from the parse tree.
 
@@ -185,7 +185,7 @@ struct node : basic_node< node > {};
 
 The name is the demangled name of the rule. By default, all nodes (except the root node) can provide the content that matched, i.e. the part of the input that the rule the node was created for matched. It is only necessary to check `has_content()` when `remove_content()` was used by a transform function (either directly or indirectly via one of the convenience helpers), otherwise all nodes except for the root will always "have content".
 
-See [`parse_tree.cpp`](https://github.com/taocpp/PEGTL/blob/master/src/example/pegtl/parse_tree.cpp) for more information on how to output (or otherwise use) the nodes.
+See [`parse_tree.cpp`](https://github.com/taocpp/PEGTL/blob/main/src/example/pegtl/parse_tree.cpp) for more information on how to output (or otherwise use) the nodes.
 
 ## Custom Node Class
 
@@ -240,4 +240,10 @@ struct my_node
 
 The parse tree uses a rule's meta data supplied by [`subs_t`](Meta-Data-and-Visit.md#sub-rules) for internal optimizations.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Performance-Notes.md b/packages/PEGTL/doc/Performance-Notes.md
index 6acf14257286df470492ddb8f2ee20093c121217..bad3886260a3498bbb2663ab06f9195627195e7d 100644
--- a/packages/PEGTL/doc/Performance-Notes.md
+++ b/packages/PEGTL/doc/Performance-Notes.md
@@ -42,4 +42,10 @@ However with `-O0`, the optimised `at_one< '"' >` was faster by 5-10% in a [JSON
 
 We still need to test whether the compiler manages to perform the same optimisation in more complex cases.
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/README.md b/packages/PEGTL/doc/README.md
index 7c028ccae0f67e1a8581605c48f4fb5f44491f1a..88bedb59217372fbd3a69cfa2b022e2e52806f89 100644
--- a/packages/PEGTL/doc/README.md
+++ b/packages/PEGTL/doc/README.md
@@ -20,7 +20,6 @@
     * [Embedding in Binaries](Installing-and-Using.md#embedding-in-binaries)
     * [Embedding in Libraries](Installing-and-Using.md#embedding-in-libraries)
     * [Embedding in Library Interfaces](Installing-and-Using.md#embedding-in-library-interfaces)
-  * [Single Header Version](Installing-and-Using.md#single-header-version)
 * [Rules and Grammars](Rules-and-Grammars.md)
   * [Combining Existing Rules](Rules-and-Grammars.md#combining-existing-rules)
   * [Toy S-Expression Grammar](Rules-and-Grammars.md#toy-s-expression-grammar)
@@ -227,6 +226,7 @@
 * [`not_range< C, D >`](Rule-Reference.md#not_range-c-d--2) <sup>[(binary rules)](Rule-Reference.md#binary-rules)</sup>
 * [`nul`](Rule-Reference.md#nul) <sup>[(ascii rules)](Rule-Reference.md#ascii-rules)</sup>
 * [`numeric_type< V >`](Rule-Reference.md#numeric_type-v-) <sup>[(icu rules)](Rule-Reference.md#icu-rules-for-enumerated-properties)</sup>
+* [`odigit`](Rule-Reference.md#odigit) <sup>[(ascii rules)](Rule-Reference.md#ascii-rules)</sup>
 * [`one< C... >`](Rule-Reference.md#one-c-) <sup>[(ascii rules)](Rule-Reference.md#ascii-rules)</sup>
 * [`one< C... >`](Rule-Reference.md#one-c--1) <sup>[(unicode rules)](Rule-Reference.md#unicode-rules)</sup>
 * [`one< C... >`](Rule-Reference.md#one-c--2) <sup>[(binary rules)](Rule-Reference.md#binary-rules)</sup>
@@ -300,4 +300,10 @@
 * [`xid_continue`](Rule-Reference.md#xid_continue) <sup>[(icu rules)](Rule-Reference.md#icu-rules-for-binary-properties)</sup>
 * [`xid_start`](Rule-Reference.md#xid_start) <sup>[(icu rules)](Rule-Reference.md#icu-rules-for-binary-properties)</sup>
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/doc/Rule-Reference.md b/packages/PEGTL/doc/Rule-Reference.md
index 79dd46ccf969050b4f40fbafc9ebe56bee2c5699..b1a6757a68976bc2d9c091c150470707f64a54fa 100644
--- a/packages/PEGTL/doc/Rule-Reference.md
+++ b/packages/PEGTL/doc/Rule-Reference.md
@@ -723,6 +723,13 @@ ASCII rules do not usually rely on other rules.
 * [Equivalent] to `one< '\0' >`.
   - `ascii::nul::rule_t` is `internal::one< result_on_found::success, internal::peek_char, 0 >`
 
+###### `odigit`
+
+* Matches and consumes a single ASCII octal digit character.
+* [Equivalent] to `range< '0', '7' >`.
+* [Meta data] and [implementation] mapping:
+  - `ascii::digit::rule_t` is `internal::range< internal::result_on_found::success, internal::peek_char, '0', '7' >`
+
 ###### `one< C... >`
 
 * Succeeds when the input is not empty, and:
@@ -1542,7 +1549,13 @@ Binary rules do not rely on other rules.
 * [`xid_continue`](#xid_continue) <sup>[(icu rules)](#icu-rules-for-binary-properties)</sup>
 * [`xid_start`](#xid_start) <sup>[(icu rules)](#icu-rules-for-binary-properties)</sup>
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
 
 [Equivalent]: #equivalence
 [implementation]: #implementation
diff --git a/packages/PEGTL/doc/Rules-and-Grammars.md b/packages/PEGTL/doc/Rules-and-Grammars.md
index 675ebfc3473a6e8f4407ec4e2ac7bebd8f1e8e88..f2242c0f4ab61786dc27b0300f4842adba1dccd9 100644
--- a/packages/PEGTL/doc/Rules-and-Grammars.md
+++ b/packages/PEGTL/doc/Rules-and-Grammars.md
@@ -398,4 +398,10 @@ long literal id was: "fraggle"
 long literal body was: "[foo["
 ```
 
+---
+
+This document is part of the [PEGTL](https://github.com/taocpp/PEGTL).
+
 Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
+Distributed under the Boost Software License, Version 1.0<br>
+See accompanying file [LICENSE_1_0.txt](../LICENSE_1_0.txt) or copy at https://www.boost.org/LICENSE_1_0.txt
diff --git a/packages/PEGTL/include/tao/pegtl.hpp b/packages/PEGTL/include/tao/pegtl.hpp
index 35cc4c6dcb31d6fea988f3df95b4208e6adef58d..982d0759af5339d756f1af83776ca9e67118d353 100644
--- a/packages/PEGTL/include/tao/pegtl.hpp
+++ b/packages/PEGTL/include/tao/pegtl.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_HPP
 #define TAO_PEGTL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/apply_mode.hpp b/packages/PEGTL/include/tao/pegtl/apply_mode.hpp
index 10b98a6668276d8c7aa39e1f5f6d0d6235b11c90..e4517c6e8734be54d0c3939c590fe42aefd16ffe 100644
--- a/packages/PEGTL/include/tao/pegtl/apply_mode.hpp
+++ b/packages/PEGTL/include/tao/pegtl/apply_mode.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_APPLY_MODE_HPP
 #define TAO_PEGTL_APPLY_MODE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/argv_input.hpp b/packages/PEGTL/include/tao/pegtl/argv_input.hpp
index 06cbc74cdf2d15a0d563c930fd3b1db0b6cec957..0518082ad067186846073f65845dfab233df883e 100644
--- a/packages/PEGTL/include/tao/pegtl/argv_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/argv_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_ARGV_INPUT_HPP
 #define TAO_PEGTL_ARGV_INPUT_HPP
@@ -20,9 +21,9 @@ namespace TAO_PEGTL_NAMESPACE
    {
       [[nodiscard]] inline std::string make_argv_source( const std::size_t argn )
       {
-         std::ostringstream os;
-         os << "argv[" << argn << ']';
-         return os.str();
+         std::ostringstream oss;
+         oss << "argv[" << argn << ']';
+         return std::move( oss ).str();
       }
 
    }  // namespace internal
diff --git a/packages/PEGTL/include/tao/pegtl/ascii.hpp b/packages/PEGTL/include/tao/pegtl/ascii.hpp
index 8e7b19e29789e2ea0df4e87627e69e810696ce70..73bb2cf5001697be0cfc2c4536a88cd0f2e1f6f5 100644
--- a/packages/PEGTL/include/tao/pegtl/ascii.hpp
+++ b/packages/PEGTL/include/tao/pegtl/ascii.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_ASCII_HPP
 #define TAO_PEGTL_ASCII_HPP
@@ -30,6 +31,7 @@ namespace TAO_PEGTL_NAMESPACE
       template< char... Cs > struct not_one : internal::one< internal::result_on_found::failure, internal::peek_char, Cs... > {};
       template< char Lo, char Hi > struct not_range : internal::range< internal::result_on_found::failure, internal::peek_char, Lo, Hi > {};
       struct nul : internal::one< internal::result_on_found::success, internal::peek_char, char( 0 ) > {};
+      struct odigit : internal::range< internal::result_on_found::success, internal::peek_char, '0', '7' > {};
       template< char... Cs > struct one : internal::one< internal::result_on_found::success, internal::peek_char, Cs... > {};
       struct print : internal::range< internal::result_on_found::success, internal::peek_char, char( 32 ), char( 126 ) > {};
       template< char Lo, char Hi > struct range : internal::range< internal::result_on_found::success, internal::peek_char, Lo, Hi > {};
diff --git a/packages/PEGTL/include/tao/pegtl/buffer_input.hpp b/packages/PEGTL/include/tao/pegtl/buffer_input.hpp
index 60d39d34935d21fe33be102ac7146ff4aa5e3a84..4ea21e7c6c4f91fd258ad2865dd1f75515e7e1a5 100644
--- a/packages/PEGTL/include/tao/pegtl/buffer_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/buffer_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_BUFFER_INPUT_HPP
 #define TAO_PEGTL_BUFFER_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/change_action.hpp b/packages/PEGTL/include/tao/pegtl/change_action.hpp
index 6e049ea958fd593a21c145bc7d1abebd0968a0d0..75496d45fe92dfa811cd6950cde436b2929450fd 100644
--- a/packages/PEGTL/include/tao/pegtl/change_action.hpp
+++ b/packages/PEGTL/include/tao/pegtl/change_action.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CHANGE_ACTION_HPP
 #define TAO_PEGTL_CHANGE_ACTION_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/change_action_and_state.hpp b/packages/PEGTL/include/tao/pegtl/change_action_and_state.hpp
index f226c71ee916f830849a1c1beabc82f3e1bb1035..248c6fedbeb9599dafd54b6a2d19b07d2bd7477a 100644
--- a/packages/PEGTL/include/tao/pegtl/change_action_and_state.hpp
+++ b/packages/PEGTL/include/tao/pegtl/change_action_and_state.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CHANGE_ACTION_AND_STATE_HPP
 #define TAO_PEGTL_CHANGE_ACTION_AND_STATE_HPP
@@ -12,6 +13,8 @@
 #include "nothing.hpp"
 #include "rewind_mode.hpp"
 
+#include "internal/dependent_false.hpp"
+
 namespace TAO_PEGTL_NAMESPACE
 {
    template< template< typename... > class NewAction, typename NewState >
@@ -30,14 +33,30 @@ namespace TAO_PEGTL_NAMESPACE
       [[nodiscard]] static bool match( ParseInput& in, States&&... st )
       {
          static_assert( !std::is_same_v< Action< void >, NewAction< void > >, "old and new action class templates are identical" );
-         NewState s( static_cast< const ParseInput& >( in ), st... );
-         if( Control< Rule >::template match< A, M, NewAction, Control >( in, s ) ) {
-            if constexpr( A == apply_mode::action ) {
-               Action< Rule >::success( static_cast< const ParseInput& >( in ), s, st... );
+
+         if constexpr( std::is_constructible_v< NewState, const ParseInput&, States... > ) {
+            NewState s( static_cast< const ParseInput& >( in ), st... );
+            if( Control< Rule >::template match< A, M, NewAction, Control >( in, s ) ) {
+               if constexpr( A == apply_mode::action ) {
+                  Action< Rule >::success( static_cast< const ParseInput& >( in ), s, st... );
+               }
+               return true;
             }
-            return true;
+            return false;
+         }
+         else if constexpr( std::is_default_constructible_v< NewState > ) {
+            NewState s;
+            if( Control< Rule >::template match< A, M, NewAction, Control >( in, s ) ) {
+               if constexpr( A == apply_mode::action ) {
+                  Action< Rule >::success( static_cast< const ParseInput& >( in ), s, st... );
+               }
+               return true;
+            }
+            return false;
+         }
+         else {
+            static_assert( internal::dependent_false< NewState >, "unable to instantiate new state" );
          }
-         return false;
       }
 
       template< typename ParseInput,
diff --git a/packages/PEGTL/include/tao/pegtl/change_action_and_states.hpp b/packages/PEGTL/include/tao/pegtl/change_action_and_states.hpp
index 6c452e04be4adcc346d2ff88ba180d7b114ccc19..9d0614f2f0620c10aeaf3ce67c049cb58ed7667f 100644
--- a/packages/PEGTL/include/tao/pegtl/change_action_and_states.hpp
+++ b/packages/PEGTL/include/tao/pegtl/change_action_and_states.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CHANGE_ACTION_AND_STATES_HPP
 #define TAO_PEGTL_CHANGE_ACTION_AND_STATES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/change_control.hpp b/packages/PEGTL/include/tao/pegtl/change_control.hpp
index b77a238041ef2fb6586e213c25745ae95113d9fb..e825e235903cff1d30a2d7bd44d6018f96aa02d5 100644
--- a/packages/PEGTL/include/tao/pegtl/change_control.hpp
+++ b/packages/PEGTL/include/tao/pegtl/change_control.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CHANGE_CONTROL_HPP
 #define TAO_PEGTL_CHANGE_CONTROL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/change_state.hpp b/packages/PEGTL/include/tao/pegtl/change_state.hpp
index bb1007979355ba37cff3c2f22c39bea5d01ec116..549ff4d7bc3c8f8cb3469f28af39103d4cca6e90 100644
--- a/packages/PEGTL/include/tao/pegtl/change_state.hpp
+++ b/packages/PEGTL/include/tao/pegtl/change_state.hpp
@@ -1,15 +1,20 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CHANGE_STATE_HPP
 #define TAO_PEGTL_CHANGE_STATE_HPP
 
+#include <type_traits>
+
 #include "apply_mode.hpp"
 #include "config.hpp"
 #include "match.hpp"
 #include "nothing.hpp"
 #include "rewind_mode.hpp"
 
+#include "internal/dependent_false.hpp"
+
 namespace TAO_PEGTL_NAMESPACE
 {
    template< typename NewState >
@@ -27,14 +32,29 @@ namespace TAO_PEGTL_NAMESPACE
                 typename... States >
       [[nodiscard]] static bool match( ParseInput& in, States&&... st )
       {
-         NewState s( static_cast< const ParseInput& >( in ), st... );
-         if( TAO_PEGTL_NAMESPACE::match< Rule, A, M, Action, Control >( in, s ) ) {
-            if constexpr( A == apply_mode::action ) {
-               Action< Rule >::success( static_cast< const ParseInput& >( in ), s, st... );
+         if constexpr( std::is_constructible_v< NewState, const ParseInput&, States... > ) {
+            NewState s( static_cast< const ParseInput& >( in ), st... );
+            if( TAO_PEGTL_NAMESPACE::match< Rule, A, M, Action, Control >( in, s ) ) {
+               if constexpr( A == apply_mode::action ) {
+                  Action< Rule >::success( static_cast< const ParseInput& >( in ), s, st... );
+               }
+               return true;
             }
-            return true;
+            return false;
+         }
+         else if constexpr( std::is_default_constructible_v< NewState > ) {
+            NewState s;
+            if( TAO_PEGTL_NAMESPACE::match< Rule, A, M, Action, Control >( in, s ) ) {
+               if constexpr( A == apply_mode::action ) {
+                  Action< Rule >::success( static_cast< const ParseInput& >( in ), s, st... );
+               }
+               return true;
+            }
+            return false;
+         }
+         else {
+            static_assert( internal::dependent_false< NewState >, "unable to instantiate new state" );
          }
-         return false;
       }
 
       template< typename ParseInput,
diff --git a/packages/PEGTL/include/tao/pegtl/change_states.hpp b/packages/PEGTL/include/tao/pegtl/change_states.hpp
index 4ec26dd34bd10ca907cfe802980b2cc6d58798a6..59835c266d7911820e13d08e72ba11ba8cb484ed 100644
--- a/packages/PEGTL/include/tao/pegtl/change_states.hpp
+++ b/packages/PEGTL/include/tao/pegtl/change_states.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CHANGE_STATES_HPP
 #define TAO_PEGTL_CHANGE_STATES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/config.hpp b/packages/PEGTL/include/tao/pegtl/config.hpp
index f1a3b8874f5ae5804a0ff926ce6d2d7ddf879787..a58f22eb00cf1947d12ad17c9a9644e2aa9c6ff9 100644
--- a/packages/PEGTL/include/tao/pegtl/config.hpp
+++ b/packages/PEGTL/include/tao/pegtl/config.hpp
@@ -1,10 +1,13 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONFIG_HPP
 #define TAO_PEGTL_CONFIG_HPP
 
-#if !defined( TAO_PEGTL_NAMESPACE )
+#if defined( TAO_PEGTL_NAMESPACE )
+#pragma message( "TAO_PEGTL_NAMESPACE is deprecated" )
+#else
 #define TAO_PEGTL_NAMESPACE tao::pegtl
 #endif
 
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/abnf.hpp b/packages/PEGTL/include/tao/pegtl/contrib/abnf.hpp
index df5f39daf36cee116e5b1057616b26b87f60e079..6778f7a10f9b481ab7403aa2e0974e6e781815e4 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/abnf.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/abnf.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_ABNF_HPP
 #define TAO_PEGTL_CONTRIB_ABNF_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/add_state.hpp b/packages/PEGTL/include/tao/pegtl/contrib/add_state.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..be91cd89cb1bf9e96112a8e4b1754410415e3686
--- /dev/null
+++ b/packages/PEGTL/include/tao/pegtl/contrib/add_state.hpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef TAO_PEGTL_CONTRIB_ADD_STATE_HPP
+#define TAO_PEGTL_CONTRIB_ADD_STATE_HPP
+
+#include <type_traits>
+
+#include "../apply_mode.hpp"
+#include "../config.hpp"
+#include "../match.hpp"
+#include "../nothing.hpp"
+#include "../rewind_mode.hpp"
+
+#include "../internal/dependent_false.hpp"
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   template< typename AddState >
+   struct add_state
+      : maybe_nothing
+   {
+      template< typename Rule,
+                apply_mode A,
+                rewind_mode M,
+                template< typename... >
+                class Action,
+                template< typename... >
+                class Control,
+                typename ParseInput,
+                typename... States >
+      [[nodiscard]] static bool match( ParseInput& in, States&&... st )
+      {
+         if constexpr( std::is_constructible_v< AddState, const ParseInput&, States... > ) {
+            AddState s( static_cast< const ParseInput& >( in ), st... );
+            if( TAO_PEGTL_NAMESPACE::match< Rule, A, M, Action, Control >( in, s, st... ) ) {
+               if constexpr( A == apply_mode::action ) {
+                  Action< Rule >::success( static_cast< const ParseInput& >( in ), s, st... );
+               }
+               return true;
+            }
+            return false;
+         }
+         else if constexpr( std::is_default_constructible_v< AddState > ) {
+            AddState s;
+            if( TAO_PEGTL_NAMESPACE::match< Rule, A, M, Action, Control >( in, s, st... ) ) {
+               if constexpr( A == apply_mode::action ) {
+                  Action< Rule >::success( static_cast< const ParseInput& >( in ), s, st... );
+               }
+               return true;
+            }
+            return false;
+         }
+         else {
+            static_assert( internal::dependent_false< AddState >, "unable to instantiate new state" );
+         }
+      }
+
+      template< typename ParseInput,
+                typename... States >
+      static void success( const ParseInput& in, AddState& s, States&&... st ) noexcept( noexcept( s.success( in, st... ) ) )
+      {
+         s.success( in, st... );
+      }
+   };
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#endif
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/alphabet.hpp b/packages/PEGTL/include/tao/pegtl/contrib/alphabet.hpp
index 1a9c24dbf276f8d8144a83f18db38a7fbd99f15f..798530e05af4d18eb04407412051dd6d6376bf35 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/alphabet.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/alphabet.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_ALPHABET_HPP
 #define TAO_PEGTL_CONTRIB_ALPHABET_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/analyze.hpp b/packages/PEGTL/include/tao/pegtl/contrib/analyze.hpp
index 0bfbd18c2fd77615ab5ff3eb7d1aec8914221807..6b4b093b720cb5b2be239b7d1ddef9ee253506bb 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/analyze.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/analyze.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_ANALYZE_HPP
 #define TAO_PEGTL_CONTRIB_ANALYZE_HPP
@@ -132,7 +133,7 @@ namespace TAO_PEGTL_NAMESPACE
                      }
                   }
                }
-               // LCOV_EXCL_END
+               // LCOV_EXCL_STOP
             }
             return accum;
          }
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/analyze_traits.hpp b/packages/PEGTL/include/tao/pegtl/contrib/analyze_traits.hpp
index 1d82cf643997e834424405aab3994c5be3990143..b835098fbc5076cb2801aa8c2b825c6db0263a5f 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/analyze_traits.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/analyze_traits.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_ANALYZE_TRAITS_HPP
 #define TAO_PEGTL_CONTRIB_ANALYZE_TRAITS_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/check_bytes.hpp b/packages/PEGTL/include/tao/pegtl/contrib/check_bytes.hpp
index 84333c7d7ab6425ab0b923b88eb68d9046cc5668..cfd313db7d7c148e4a7f366a23a895a220bcdadc 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/check_bytes.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/check_bytes.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_CHECK_BYTES_HPP
 #define TAO_PEGTL_CONTRIB_CHECK_BYTES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/control_action.hpp b/packages/PEGTL/include/tao/pegtl/contrib/control_action.hpp
index 06d61e29591f1a2ae6d969355a92ee64548d4154..239e6213bdabbb0d11bcc468dbc8cb27da574586 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/control_action.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/control_action.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_CONTROL_ACTION_HPP
 #define TAO_PEGTL_CONTRIB_CONTROL_ACTION_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/coverage.hpp b/packages/PEGTL/include/tao/pegtl/contrib/coverage.hpp
index 14d32ffcc133bfb3197c94571087b1ee083fd1c3..bfa75424fbe26d170b58c69c5f84ddc25b73795c 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/coverage.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/coverage.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_COVERAGE_HPP
 #define TAO_PEGTL_CONTRIB_COVERAGE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/forward.hpp b/packages/PEGTL/include/tao/pegtl/contrib/forward.hpp
index d60f9b54b8ee1ae3e32ddbe1d7354f6f0c43a415..aa9f351e4e2956fc7f11eb719d695a117439c0ec 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/forward.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/forward.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_FORWARD_HPP
 #define TAO_PEGTL_CONTRIB_FORWARD_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/function.hpp b/packages/PEGTL/include/tao/pegtl/contrib/function.hpp
index bed10604790529ef356ee5ce3da03966d62ad6fc..86d535896d4cef96d48e1b7b071060078032786d 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/function.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/function.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_FUNCTION_HPP
 #define TAO_PEGTL_CONTRIB_FUNCTION_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/http.hpp b/packages/PEGTL/include/tao/pegtl/contrib/http.hpp
index a1375aa9ec6219058063de1e643b1ba2f063469e..e794a6dd739a803d4a323728d87daa77fbfe3426 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/http.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/http.hpp
@@ -1,11 +1,12 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_HTTP_HPP
 #define TAO_PEGTL_CONTRIB_HTTP_HPP
 
 #if !defined( __cpp_exceptions )
-#error "Exception support required tao/pegtl/contrib/http.hpp"
+#error "Exception support required for tao/pegtl/contrib/http.hpp"
 #else
 
 #include "../ascii.hpp"
@@ -126,8 +127,8 @@ namespace TAO_PEGTL_NAMESPACE::http
    struct https_URI : if_must< istring< 'h', 't', 't', 'p', 's', ':', '/', '/' >, uri::authority, uri::path_abempty, uri::opt_query, uri::opt_fragment > {};
 
    struct partial_URI : seq< uri::relative_part, uri::opt_query > {};
-
    // clang-format on
+
    struct chunk_size
    {
       using rule_t = plus< abnf::HEXDIG >::rule_t;
@@ -170,13 +171,13 @@ namespace TAO_PEGTL_NAMESPACE::http
          return i > 0;
       }
    };
-   // clang-format off
 
+   // clang-format off
    struct chunk_ext_name : token {};
    struct chunk_ext_val : sor< quoted_string, token > {};
    struct chunk_ext : star_must< one< ';' >, chunk_ext_name, if_must< one< '=' >, chunk_ext_val > > {};
-
    // clang-format on
+
    struct chunk_data
    {
       using rule_t = star< abnf::OCTET >::rule_t;
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/icu/internal.hpp b/packages/PEGTL/include/tao/pegtl/contrib/icu/internal.hpp
index 596a7eced7aaa70885965a69577ce41990cbfa33..02976fd6d210a221d6f799aa72e8ef651bda4dd7 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/icu/internal.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/icu/internal.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_ICU_INTERNAL_HPP
 #define TAO_PEGTL_CONTRIB_ICU_INTERNAL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/icu/utf16.hpp b/packages/PEGTL/include/tao/pegtl/contrib/icu/utf16.hpp
index b73db99271631eb58c5220e02e70607733bad7d0..1b04ad8b2ec60a361ce8d32395c85c4e1332dc21 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/icu/utf16.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/icu/utf16.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_ICU_UTF16_HPP
 #define TAO_PEGTL_CONTRIB_ICU_UTF16_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/icu/utf32.hpp b/packages/PEGTL/include/tao/pegtl/contrib/icu/utf32.hpp
index 9781f567b35e6a58add6a634120db7850f1a3559..738b0e61f1dfe1f9c9d09c17e1e5e80911670908 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/icu/utf32.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/icu/utf32.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_ICU_UTF32_HPP
 #define TAO_PEGTL_CONTRIB_ICU_UTF32_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/icu/utf8.hpp b/packages/PEGTL/include/tao/pegtl/contrib/icu/utf8.hpp
index cf72da60ac3724a97dca3092db793db72255f0f8..7a7ced60128523593bc951ec037f3dfdddf603d7 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/icu/utf8.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/icu/utf8.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_ICU_UTF8_HPP
 #define TAO_PEGTL_CONTRIB_ICU_UTF8_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/if_then.hpp b/packages/PEGTL/include/tao/pegtl/contrib/if_then.hpp
index a17801355f5c07a87fcac9a0fb4836af2be2a42e..897f55c026d46394c3a63e0bbcb65083fd01bb0d 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/if_then.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/if_then.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_IF_THEN_HPP
 #define TAO_PEGTL_CONTRIB_IF_THEN_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/instantiate.hpp b/packages/PEGTL/include/tao/pegtl/contrib/instantiate.hpp
index ebf6e7f1ca1730a47617ccaea29d565270b5a888..ce8bb4c293fe9ef45f85d1919d49fe4cdfd4b4d3 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/instantiate.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/instantiate.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INSTANTIATE_HPP
 #define TAO_PEGTL_CONTRIB_INSTANTIATE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/integer.hpp b/packages/PEGTL/include/tao/pegtl/contrib/integer.hpp
index f859662bf07e1f619feec3c0c42937a28fa67e1b..2fbc7285dcde90629456db3a5c334ffb5e93e94f 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/integer.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/integer.hpp
@@ -1,11 +1,12 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTEGER_HPP
 #define TAO_PEGTL_CONTRIB_INTEGER_HPP
 
 #if !defined( __cpp_exceptions )
-#error "Exception support required tao/pegtl/contrib/integer.hpp"
+#error "Exception support required for tao/pegtl/contrib/integer.hpp"
 #else
 
 #include <cstdint>
@@ -168,7 +169,7 @@ namespace TAO_PEGTL_NAMESPACE
       template< typename ParseInput,
                 typename Unsigned,
                 Unsigned Maximum = ( std::numeric_limits< Unsigned >::max )() >
-      [[nodiscard]] bool match_and_convert_unsigned_with_maximum( ParseInput& in, Unsigned& st )
+      [[nodiscard]] bool match_and_convert_unsigned_with_maximum_throws( ParseInput& in, Unsigned& st )
       {
          // Assumes st == 0.
 
@@ -181,7 +182,7 @@ namespace TAO_PEGTL_NAMESPACE
                }
                do {
                   if( !accumulate_digit< Unsigned, Maximum >( st, c ) ) {
-                     throw TAO_PEGTL_NAMESPACE::parse_error( "integer overflow", in );  // Consistent with "as if" an action was doing the conversion.
+                     throw TAO_PEGTL_NAMESPACE::parse_error( "integer overflow", in );
                   }
                   in.bump_in_this_line();
                } while( ( !in.empty() ) && is_digit( c = in.peek_char() ) );
@@ -191,6 +192,38 @@ namespace TAO_PEGTL_NAMESPACE
          return false;
       }
 
+      template< typename ParseInput,
+                typename Unsigned,
+                Unsigned Maximum = ( std::numeric_limits< Unsigned >::max )() >
+      [[nodiscard]] bool match_and_convert_unsigned_with_maximum_nothrow( ParseInput& in, Unsigned& st )
+      {
+         // Assumes st == 0.
+
+         if( !in.empty() ) {
+            char c = in.peek_char();
+            if( c == '0' ) {
+               if( ( in.size( 2 ) < 2 ) || ( !is_digit( in.peek_char( 1 ) ) ) ) {
+                  in.bump_in_this_line();
+                  return true;
+               }
+               return false;
+            }
+            if( is_digit( c ) ) {
+               unsigned b = 0;
+
+               do {
+                  if( !accumulate_digit< Unsigned, Maximum >( st, c ) ) {
+                     return false;
+                  }
+                  ++b;
+               } while( ( !in.empty() ) && is_digit( c = in.peek_char( b ) ) );
+               in.bump_in_this_line( b );
+               return true;
+            }
+         }
+         return false;
+      }
+
    }  // namespace internal
 
    struct unsigned_action
@@ -250,7 +283,7 @@ namespace TAO_PEGTL_NAMESPACE
       {
          // This function "only" offers basic exception safety.
          st = 0;
-         return internal::match_and_convert_unsigned_with_maximum( in, st );  // Throws on overflow.
+         return internal::match_and_convert_unsigned_with_maximum_throws( in, st );  // Throws on overflow.
       }
    };
 
@@ -284,7 +317,7 @@ namespace TAO_PEGTL_NAMESPACE
       [[nodiscard]] static bool match( ParseInput& in )
       {
          Unsigned st = 0;
-         return internal::match_and_convert_unsigned_with_maximum< ParseInput, Unsigned, Maximum >( in, st );  // Throws on overflow.
+         return internal::match_and_convert_unsigned_with_maximum_nothrow< ParseInput, Unsigned, Maximum >( in, st );
       }
    };
 
@@ -307,7 +340,7 @@ namespace TAO_PEGTL_NAMESPACE
       [[nodiscard]] static auto match( ParseInput& in, States&&... /*unused*/ ) -> std::enable_if_t< A == apply_mode::nothing, bool >
       {
          Unsigned st = 0;
-         return internal::match_and_convert_unsigned_with_maximum< ParseInput, Unsigned, Maximum >( in, st );  // Throws on overflow.
+         return internal::match_and_convert_unsigned_with_maximum_throws< ParseInput, Unsigned, Maximum >( in, st );
       }
 
       template< apply_mode A,
@@ -322,7 +355,7 @@ namespace TAO_PEGTL_NAMESPACE
       {
          // This function "only" offers basic exception safety.
          st = 0;
-         return internal::match_and_convert_unsigned_with_maximum< ParseInput, Unsigned, Maximum >( in, st );  // Throws on overflow.
+         return internal::match_and_convert_unsigned_with_maximum_throws< ParseInput, Unsigned, Maximum >( in, st );
       }
    };
 
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/endian.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/endian.hpp
index ac40ead5894cab5d0004b773fcf88ebeb86e80d0..fc997a311651a584583f72198ab325d158406cec 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/endian.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/endian.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_ENDIAN_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_ENDIAN_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/endian_gcc.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/endian_gcc.hpp
index 9eeb9ad71bffc2ad171ed00757dd793bad9014b1..d6a82315520517ef535f0a947284169376529dd3 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/endian_gcc.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/endian_gcc.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_ENDIAN_GCC_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_ENDIAN_GCC_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/endian_win.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/endian_win.hpp
index 308dd3f018361baeebde0cf080ae49406ebfbace..ac21c8bcc900a3a47b48ef5e9c6a029f61b45839 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/endian_win.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/endian_win.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_ENDIAN_WIN_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_ENDIAN_WIN_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_mask_uint.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_mask_uint.hpp
index 2a6c8b3079d8661e76fe2bf385ea8ed908649fb9..f34baa4f708fd0e06424b8100b6d98aea5a6117c 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_mask_uint.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_mask_uint.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_PEEK_MASK_UINT_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_PEEK_MASK_UINT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_mask_uint8.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_mask_uint8.hpp
index 15bd6926f26467ff01c883e24b18d72d181c7bc6..b57da1809cb774ed26c2f5339a8e04293bf36ebf 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_mask_uint8.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_mask_uint8.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_PEEK_MASK_UINT8_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_PEEK_MASK_UINT8_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_uint.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_uint.hpp
index 04925f5ac3480a7176645cb412d4777c52b68e6f..62f7555b666c03a01384e9051cfcc1a99f1e8f12 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_uint.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_uint.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_PEEK_UINT_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_PEEK_UINT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_uint8.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_uint8.hpp
index 47b78efb2549146a425849a4537478d76a8de1af..9b437f4c650d827ca9b0e3c3681d1dd62db0093c 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_uint8.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_uint8.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_PEEK_UINT8_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_PEEK_UINT8_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_utf16.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_utf16.hpp
index c37b23918b7dad49dbd0d7bc7294a06b018ff25e..6a5006c216e847d993b5f56b06c9459cfc2dfb26 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_utf16.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_utf16.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_PEEK_UTF16_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_PEEK_UTF16_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_utf32.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_utf32.hpp
index b48dc4d2c2b0fc96152b8aa4f17fae28c0ad52d4..0d78db0dacf6cc8694a32e1d960362831f314ebe 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_utf32.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/peek_utf32.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_PEEK_UTF32_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_PEEK_UTF32_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/read_uint.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/read_uint.hpp
index 184c55ee095eebad005a1719b216ed1fdf9511ae..8f294726aa7b64d9b6f358ddd8148cc40d85c900 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/read_uint.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/read_uint.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_READ_UINT_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_READ_UINT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/set_stack_guard.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/set_stack_guard.hpp
index c0071e959d0c3bd52f405b5800e608107716a91c..a7c89b5ec3c86fc76a55f228c667fb3f35960fff 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/set_stack_guard.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/set_stack_guard.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_SET_STACK_GUARD_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_SET_STACK_GUARD_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/vector_stack_guard.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/vector_stack_guard.hpp
index 4b0cfbe8bf730bdac56b39c6e09cd627b5f928b5..b143457f4ff39776f9101b5284a16b5d53b22e29 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/internal/vector_stack_guard.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/vector_stack_guard.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_INTERNAL_VECTOR_STACK_GUARD_HPP
 #define TAO_PEGTL_CONTRIB_INTERNAL_VECTOR_STACK_GUARD_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/iri.hpp b/packages/PEGTL/include/tao/pegtl/contrib/iri.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..defdc42fa0d72b1725e97c1c4bf497a8af64e84d
--- /dev/null
+++ b/packages/PEGTL/include/tao/pegtl/contrib/iri.hpp
@@ -0,0 +1,107 @@
+// Copyright (c) 2021 Kelvin Hammond
+// Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef TAO_PEGTL_CONTRIB_IRI_HPP
+#define TAO_PEGTL_CONTRIB_IRI_HPP
+
+#if !defined( __cpp_exceptions )
+#error "Exception support required for tao/pegtl/contrib/iri.hpp"
+#else
+
+#include "../config.hpp"
+#include "../rules.hpp"
+#include "../utf8.hpp"
+
+#include "abnf.hpp"
+#include "uri.hpp"
+
+namespace TAO_PEGTL_NAMESPACE::iri
+{
+   // IRI grammar according to RFC 3987.
+
+   // This grammar is a direct PEG translation of the original URI grammar.
+   // It should be considered experimental -- in case of any issues, in particular
+   // missing rules for attached actions, please contact the developers.
+
+   // Note that this grammar has multiple top-level rules.
+
+   using uri::scheme;
+   using uri::port;
+   using uri::dslash;
+   using uri::IP_literal;
+   using uri::IPv4address;
+   using uri::pct_encoded;
+   using uri::sub_delims;
+   using uri::colon;
+
+   // clang-format off
+   struct ucschar : utf8::ranges<
+      0xA0, 0xD7FF,
+      0xF900, 0xFDCF,
+      0xFDF0, 0xFFEF,
+      0x10000, 0x1FFFD,
+      0x20000, 0x2FFFD,
+      0x30000, 0x3FFFD,
+      0x40000, 0x4FFFD,
+      0x50000, 0x5FFFD,
+      0x60000, 0x6FFFD,
+      0x70000, 0x7FFFD,
+      0x80000, 0x8FFFD,
+      0x90000, 0x9FFFD,
+      0xA0000, 0xAFFFD,
+      0xB0000, 0xBFFFD,
+      0xC0000, 0xCFFFD,
+      0xD0000, 0xDFFFD,
+      0xE1000, 0xEFFFD > {};
+
+   struct iprivate : utf8::ranges< 0xE000, 0xF8FF, 0xF0000, 0xFFFFD, 0x100000, 0x10FFFD > {};
+
+   struct iunreserved : sor< abnf::ALPHA, abnf::DIGIT, one< '-', '.', '_', '~' >, ucschar > {};
+
+   struct ipchar : sor< iunreserved, pct_encoded, sub_delims, one< ':', '@' > > {};
+
+   struct isegment : star< ipchar > {};
+   struct isegment_nz : plus< ipchar > {};
+   // non-zero-length segment without any colon ":"
+   struct isegment_nz_nc : plus< sor< iunreserved, pct_encoded, sub_delims, one< '@' > > > {};
+
+   struct ipath_abempty : star< one< '/' >, isegment > {};
+   struct ipath_absolute : seq< one< '/' >, opt< isegment_nz, star< one< '/' >, isegment > > > {};
+   struct ipath_noscheme : seq< isegment_nz_nc, star< one< '/' >, isegment > > {};
+   struct ipath_rootless : seq< isegment_nz, star< one< '/' >, isegment > > {};
+   struct ipath_empty : success {};
+
+   struct ipath : sor< ipath_noscheme,  // begins with a non-colon segment
+                       ipath_rootless,  // begins with a segment
+                       ipath_absolute,  // begins with "/" but not "//"
+                       ipath_abempty >  // begins with "/" or is empty
+   {};
+
+   struct ireg_name : star< sor< iunreserved, pct_encoded, sub_delims > > {};
+
+   struct ihost : sor< IP_literal, IPv4address, ireg_name > {};
+   struct iuserinfo : star< sor< iunreserved, pct_encoded, sub_delims, colon > > {};
+   struct opt_iuserinfo : opt< iuserinfo, one< '@' > > {};
+   struct iauthority : seq< opt_iuserinfo, ihost, opt< colon, port > > {};
+
+   struct iquery : star< sor< ipchar, iprivate, one< '/', '?' > > > {};
+   struct ifragment : star< sor< ipchar, one< '/', '?' > > > {};
+
+   struct opt_iquery : opt_must< one< '?' >, iquery > {};
+   struct opt_ifragment : opt_must< one< '#' >, ifragment > {};
+
+   struct ihier_part : sor< if_must< dslash, iauthority, ipath_abempty >, ipath_rootless, ipath_absolute, ipath_empty > {};
+   struct irelative_part : sor< if_must< dslash, iauthority, ipath_abempty >, ipath_noscheme, ipath_absolute, ipath_empty > {};
+   struct irelative_ref : seq< irelative_part, opt_iquery, opt_ifragment > {};
+
+   struct IRI : seq< scheme, one< ':' >, ihier_part, opt_iquery, opt_ifragment > {};
+   struct IRI_reference : sor< IRI, irelative_ref > {};
+   struct absolute_IRI : seq< scheme, one< ':' >, ihier_part, opt_iquery > {};
+   // clang-format off
+
+}  // namespace TAO_PEGTL_NAMESPACE::iri
+
+#endif
+#endif
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/json.hpp b/packages/PEGTL/include/tao/pegtl/contrib/json.hpp
index 027201ac88a1691e6fb5195389dd2afc907b0d40..e47aaae4b5b01bb22bd1212781aa6fdd8c347559 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/json.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/json.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_JSON_HPP
 #define TAO_PEGTL_CONTRIB_JSON_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/json_pointer.hpp b/packages/PEGTL/include/tao/pegtl/contrib/json_pointer.hpp
index 6230cfcea1263d460c76ee7d6ef4651012bf4243..5e920d720b1e0d2b82dc77f4252f965eb67e4db2 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/json_pointer.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/json_pointer.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_JSON_POINTER_HPP
 #define TAO_PEGTL_CONTRIB_JSON_POINTER_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/limit_bytes.hpp b/packages/PEGTL/include/tao/pegtl/contrib/limit_bytes.hpp
index cea99fc6fef85375352e6a377c9db42d19201ff5..1447e1fd58fdd2a964bcdcc1263ed90696a27005 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/limit_bytes.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/limit_bytes.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_LIMIT_BYTES_HPP
 #define TAO_PEGTL_CONTRIB_LIMIT_BYTES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/limit_depth.hpp b/packages/PEGTL/include/tao/pegtl/contrib/limit_depth.hpp
index 447bebff7cc21a78f631e37ac23800a2741fd671..a84e002eaed39f027d4c7f2b60a9c8ad27689cd3 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/limit_depth.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/limit_depth.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_LIMIT_DEPTH_HPP
 #define TAO_PEGTL_CONTRIB_LIMIT_DEPTH_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/parse_tree.hpp b/packages/PEGTL/include/tao/pegtl/contrib/parse_tree.hpp
index 2d83d557e6874f07f114075c4101647b96f6893a..8de35b8cd13051ba046959e108933a6c160d484e 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/parse_tree.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/parse_tree.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_PARSE_TREE_HPP
 #define TAO_PEGTL_CONTRIB_PARSE_TREE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/parse_tree_to_dot.hpp b/packages/PEGTL/include/tao/pegtl/contrib/parse_tree_to_dot.hpp
index 79dbb0104e0b51e8d2970e7c2f879f2e33199836..f186bf6ea0c559fb6d8e38c2fe1afb2d1c085b89 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/parse_tree_to_dot.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/parse_tree_to_dot.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_PARSE_TREE_TO_DOT_HPP
 #define TAO_PEGTL_CONTRIB_PARSE_TREE_TO_DOT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/peg.hpp b/packages/PEGTL/include/tao/pegtl/contrib/peg.hpp
deleted file mode 100644
index a921e372c158cfa789f448cff3b67abaf859aec9..0000000000000000000000000000000000000000
--- a/packages/PEGTL/include/tao/pegtl/contrib/peg.hpp
+++ /dev/null
@@ -1,125 +0,0 @@
-// Copyright (c) 2021 Daniel Deptford
-// Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#ifndef TAO_PEGTL_CONTRIB_PEG_HPP
-#define TAO_PEGTL_CONTRIB_PEG_HPP
-
-#include <tao/pegtl.hpp>
-
-namespace TAO_PEGTL_NAMESPACE
-{
-   namespace peg
-   {
-      // PEG grammar from https://pdos.csail.mit.edu/~baford/packrat/popl04/peg-popl04.pdf
-      namespace grammar
-      {
-         // clang-format off
-         struct AND;
-         struct Char;
-         struct Class;
-         struct CLOSE;
-         struct Comment;
-         struct Definition;
-         struct DOT;
-         struct EndOfFile;
-         struct EndOfLine;
-         struct Expression;
-         struct QUESTION;
-         struct IdentCont;
-         struct Identifier;
-         struct IdentStart;
-         struct LEFTARROW;
-         struct Literal;
-         struct NOT;
-         struct OPEN;
-         struct PLUS;
-         struct Prefix;
-         struct Primary;
-         struct Range;
-         struct Sequence;
-         struct SLASH;
-         struct Space;
-         struct Spacing;
-         struct STAR;
-         struct Suffix;
-
-         struct Grammar : seq< Spacing, plus< Definition >, EndOfFile > {};
-
-         struct Definition : seq< Identifier, LEFTARROW, Expression > {};
-         struct Expression : list< Sequence, SLASH > {};
-         struct Sequence : star< Prefix > {};
-
-         struct Prefix : seq< opt< sor< AND, NOT > >, Suffix > {};
-         struct Suffix : seq< Primary, opt< sor< QUESTION, STAR, PLUS > > > {};
-
-         struct Primary : sor<
-            seq< Identifier, not_at< LEFTARROW > >,
-            seq< OPEN, Expression, CLOSE >,
-            Literal,
-            Class,
-            DOT
-            > {};
-
-         struct Identifier : seq< IdentStart, star< IdentCont >, Spacing > {};
-
-         struct IdentStart : identifier_first {};
-
-         struct IdentCont : identifier_other {};
-
-         struct Literal : sor<
-            seq< one< '\'' >, until< one< '\'' >, Char >, Spacing >,
-            seq< one< '"' >, until< one< '"' >, Char >, Spacing >
-            > {};
-
-         struct Class : seq< one< '[' >, until< one< ']' >, Range >, Spacing > {};
-
-         struct Range : sor<
-            seq< Char, one< '-' >, Char >,
-            Char
-            > {};
-
-         struct Char : sor<
-            seq<
-               one< '\\' >,
-               one< 'n', 'r', 't', '\'', '"', '[', ']', '\\' > >,
-            seq<
-               one< '\\' >,
-               range< '0', '2' >,
-               range< '0', '7' >,
-               range< '0', '7' > >,
-            seq<
-               one< '\\' >,
-               range< '0','7' >,
-               opt< range< '0','7' > > >,
-            seq<
-               not_at< one< '\\' > >,
-               any >
-            > {};
-
-         struct LEFTARROW : seq< string< '<','-' >, Spacing > {};
-         struct SLASH : seq< one< '/' >, Spacing > {};
-         struct AND : seq< one< '&' >, Spacing > {};
-         struct NOT : seq< one< '!' >, Spacing > {};
-         struct QUESTION : seq< one< '?' >, Spacing > {};
-         struct STAR : seq< one< '*' >, Spacing > {};
-         struct PLUS : seq< one< '+' >, Spacing > {};
-         struct OPEN : seq< one< '(' >, Spacing > {};
-         struct CLOSE : seq< one< ')' >, Spacing > {};
-         struct DOT : seq< one< '.' >, Spacing > {};
-
-         struct Spacing : star< sor< Space, Comment > > {};
-         struct Comment : seq< one< '#' >, until< EndOfLine > > {};
-
-         struct Space : sor< one< ' ', '\t' >, EndOfLine > {};
-         struct EndOfLine : sor< string< '\r', '\n' >, one< '\n' >, one< '\r' > > {};
-         struct EndOfFile : eof {};
-         // clang-format on
-
-      }  // namespace grammar
-
-   }  // namespace peg
-
-}  // namespace TAO_PEGTL_NAMESPACE
-
-#endif  // TAO_PEGTL_CONTRIB_PEG_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/predicates.hpp b/packages/PEGTL/include/tao/pegtl/contrib/predicates.hpp
index 35a1f9b2f71da1a558ca4118e0f427503a3d5ca7..4e37e29072324e5545ea1cf0dc67c70acdeba3dd 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/predicates.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/predicates.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_PREDICATES_HPP
 #define TAO_PEGTL_CONTRIB_PREDICATES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/print.hpp b/packages/PEGTL/include/tao/pegtl/contrib/print.hpp
index 1583630f9ec62a1bcdb2a87b71f80c92ddfb8ae3..18d803021412de70c06aaac6bb698d959add68b1 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/print.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/print.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_PRINT_HPP
 #define TAO_PEGTL_CONTRIB_PRINT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/print_coverage.hpp b/packages/PEGTL/include/tao/pegtl/contrib/print_coverage.hpp
index 98be601d0d47a190df4383fb74ff0f27372c5a9c..9b0774f0467249aa09eb42716112845845aea6ae 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/print_coverage.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/print_coverage.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_PRINT_COVERAGE_HPP
 #define TAO_PEGTL_CONTRIB_PRINT_COVERAGE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/proto3.hpp b/packages/PEGTL/include/tao/pegtl/contrib/proto3.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..535cfa1c327da830d203f2e526d5d10aea3e0dcb
--- /dev/null
+++ b/packages/PEGTL/include/tao/pegtl/contrib/proto3.hpp
@@ -0,0 +1,143 @@
+// Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef TAO_PEGTL_CONTRIB_PROTO3_HPP
+#define TAO_PEGTL_CONTRIB_PROTO3_HPP
+
+#include "../ascii.hpp"
+#include "../config.hpp"
+#include "../rules.hpp"
+
+namespace TAO_PEGTL_NAMESPACE::proto3
+{
+   // protocol buffer v3
+   // https://developers.google.com/protocol-buffers/docs/reference/proto3-spec
+
+   // clang-format off
+   struct comment_sl : seq< two< '/' >, until< eolf > > {};
+   struct comment_ml : if_must< string< '/', '*' >, until< string< '*', '/' > > > {};
+   struct sp : sor< space, comment_sl, comment_ml > {};
+   struct sps : star< sp > {};
+
+   struct comma : one< ',' > {};
+   struct dot : one< '.' > {};
+   struct equ : one< '=' > {};
+   struct semi : one< ';' > {};
+
+   struct option;
+   struct message;
+   struct extend;
+
+   struct ident_first : ranges< 'a', 'z', 'A', 'Z' > {};  // NOTE: Yes, no '_'.
+   struct ident_other : ranges< 'a', 'z', 'A', 'Z', '0', '9', '_' > {};
+   struct ident : seq< ident_first, star< ident_other > > {};
+   struct full_ident : list_must< ident, dot > {};
+
+   struct hex_lit : seq< one< '0' >, one< 'x', 'X' >, plus< xdigit > > {};
+   struct oct_lit : seq< one< '0' >, plus< odigit > > {};
+   struct dec_lit : seq< range< '1', '9' >, star< digit > >  {};
+   struct int_lit : sor< hex_lit, oct_lit, dec_lit > {};
+
+   struct sign : one< '+', '-' > {};
+   struct exp : seq< one< 'E', 'e' >, opt< sign >, plus< digit > > {};
+   struct float_lit : sor<
+      seq< plus< digit >, dot, exp >,
+      seq< plus< digit >, dot, star< digit >, opt< exp > >,
+      seq< dot, plus< digit >, opt< exp > >,
+      keyword< 'i', 'n', 'f' >,
+      keyword< 'n', 'a', 'n' > > {};
+
+   struct bool_lit : sor< keyword< 't', 'r', 'u', 'e' >,
+                          keyword< 'f', 'a', 'l', 's', 'e' > > {};
+
+   struct hex_escape : if_must< one< 'x', 'X' >, xdigit, xdigit > {};
+   struct oct_escape : if_must< odigit, odigit, odigit > {};
+   struct char_escape : one< 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"' > {};
+   struct escape : if_must< one< '\\' >, hex_escape, oct_escape, char_escape > {};
+   struct char_value : sor< escape, not_one< '\n', '\0' > > {};  // NOTE: No need to exclude '\' from not_one<>, see escape rule.
+   template< char Q >
+   struct str_impl : if_must< one< Q >, until< one< Q >, char_value > > {};
+   struct str_lit : sor< str_impl< '\'' >, str_impl< '"' > > {};
+
+   struct constant : sor< bool_lit, seq< opt< sign >, float_lit >, seq< opt< sign >, int_lit >, str_lit, full_ident > {};
+
+   struct option_name : seq< sor< ident, if_must< one< '(' >, full_ident, one< ')' > > >, star_must< dot, ident > > {};
+   struct option : if_must< keyword< 'o', 'p', 't', 'i', 'o', 'n' >, sps, option_name, sps, equ, sps, constant, sps, semi > {};
+
+   struct bool_type : keyword< 'b', 'o', 'o', 'l' > {};
+   struct bytes_type : keyword< 'b', 'y', 't', 'e', 's' > {};
+   struct double_type : keyword< 'd', 'o', 'u', 'b', 'l', 'e' > {};
+   struct float_type : keyword< 'f', 'l', 'o', 'a', 't' > {};
+   struct string_type : keyword< 's', 't', 'r', 'i', 'n', 'g' > {};
+
+   struct int32_type : keyword< 'i', 'n', 't', '3', '2' > {};
+   struct int64_type : keyword< 'i', 'n', 't', '6', '4' > {};
+   struct sint32_type : keyword< 's', 'i', 'n', 't', '3', '2' > {};
+   struct sint64_type : keyword< 's', 'i', 'n', 't', '6', '4' > {};
+   struct uint32_type : keyword< 'u', 'i', 'n', 't', '3', '2' > {};
+   struct uint64_type : keyword< 'u', 'i', 'n', 't', '6', '4' > {};
+   struct fixed32_type : keyword< 'f', 'i', 'x', 'e', 'd', '3', '2' > {};
+   struct fixed64_type : keyword< 'f', 'i', 'x', 'e', 'd', '6', '4' > {};
+   struct sfixed32_type : keyword< 's', 'f', 'i', 'x', 'e', 'd', '3', '2' > {};
+   struct sfixed64_type : keyword< 's', 'f', 'i', 'x', 'e', 'd', '6', '4' > {};
+
+   struct builtin_type : sor< bool_type, bytes_type, double_type, float_type, string_type, int32_type, int64_type, sint32_type, sint64_type, uint32_type, uint64_type, fixed32_type, fixed64_type, sfixed32_type, sfixed64_type > {};
+
+   struct defined_type : seq< opt< dot >, full_ident > {};  // NOTE: This replaces both message_type and enum_type -- they have the same syntax.
+
+   struct type : sor< builtin_type, defined_type > {};
+
+   struct field_option : if_must< option_name, sps, equ, sps, constant > {};
+   struct field_options : if_must< one< '[' >, sps, list< field_option, comma, sp >, sps, one< ']' > > {};
+   struct field_name : ident {};
+   struct field_number : int_lit {};
+   struct field : seq< opt< sor< keyword< 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l' >, keyword< 'r', 'e', 'p', 'e', 'a', 't', 'e', 'd' > >, sps >, type, sps, field_name, sps, equ, sps, field_number, sps, opt< field_options, sps >, semi > {};
+
+   struct oneof_name : ident {};
+   struct oneof_field : if_must< type, sps, field_name, sps, equ, sps, field_number, sps, opt< field_options, sps >, semi > {};
+   struct oneof_body : sor< oneof_field, semi > {};
+   struct oneof : if_must< keyword< 'o', 'n', 'e', 'o', 'f' >, sps, oneof_name, sps, one< '{' >, sps, until< one< '}' >, oneof_body, sps >, sps > {};
+
+   struct key_type : seq< sor< bool_type, string_type, int32_type, int64_type, sint32_type, sint64_type, uint32_type, uint64_type, fixed32_type, fixed64_type, sfixed32_type, sfixed64_type >, not_at< ident_other > > {};
+   struct map_name : ident {};
+   struct map_field : if_must< keyword< 'm', 'a', 'p' >, sps, one< '<' >, sps, key_type, sps, comma, sps, type, sps, one< '>' >, sps, map_name, sps, equ, sps, field_number, sps, opt< field_options, sps >, semi > {};
+
+   struct range : if_must< int_lit, sps, keyword< 't', 'o' >, sps, sor< int_lit, keyword< 'm', 'a', 'x' > > > {};
+   struct ranges : list_must< range, comma, sp > {};
+   struct field_names : list_must< field_name, comma, sp > {};
+   struct reserved : if_must< keyword< 'r', 'e', 's', 'e', 'r', 'v', 'e', 'd' >, sps, sor< ranges, field_names >, sps, semi > {};
+
+   struct enum_name : ident {};
+   struct enum_value_option : seq< option_name, sps, equ, sps, constant > {};
+   struct enum_field : seq< ident, sps, equ, sps, int_lit, sps, opt_must< one< '[' >, sps, list_must< enum_value_option, comma, sp >, sps, one< ']' >, sps >, semi > {};
+   struct enum_body : if_must< one< '{' >, sps, star< sor< option, enum_field, semi >, sps >, one< '}' > > {};
+   struct enum_def : if_must< keyword< 'e', 'n', 'u', 'm' >, sps, enum_name, sps, enum_body > {};
+
+   struct message_thing : sor< field, enum_def, message, option, oneof, map_field, reserved, extend, semi > {};
+   struct message_body : seq< one<'{'>, sps, star< message_thing, sps >, one<'}'> > {};
+   struct message : if_must< keyword< 'm', 'e', 's', 's', 'a', 'g', 'e' >, sps, defined_type, sps, message_body > {};
+   struct extend : if_must< keyword< 'e', 'x', 't', 'e', 'n', 'd' >, sps, defined_type, sps, message_body > {};
+
+   struct package : if_must< keyword< 'p', 'a', 'c', 'k', 'a', 'g', 'e' >, sps, full_ident, sps, semi > {};
+
+   struct import_option : opt< sor< keyword< 'w', 'e', 'a', 'k' >, keyword< 'p', 'u', 'b', 'l', 'i', 'c' > > > {};
+   struct import : if_must< keyword< 'i', 'm', 'p', 'o', 'r', 't' >, sps, import_option, sps, str_lit, sps, semi > {};
+
+   struct rpc_name : ident {};
+   struct rpc_type : if_must< one< '(' >, sps, opt< keyword< 's', 't', 'r', 'e', 'a', 'm' >, sps >, defined_type, sps, one< ')' > > {};
+   struct rpc_options : if_must< one< '{' >, sps, star< sor< option, semi >, sps >, one< '}' > > {};
+   struct rpc : if_must< keyword< 'r', 'p', 'c' >, sps, rpc_name, sps, rpc_type, sps, keyword< 'r', 'e', 't', 'u', 'r', 'n', 's' >, sps, rpc_type, sps, sor< semi, rpc_options > > {};
+   struct service_name : ident {};
+   struct service : if_must< keyword< 's', 'e', 'r', 'v', 'i', 'c', 'e' >, sps, service_name, sps, one< '{' >, sps, star< sor< option, rpc, semi >, sps >, one< '}' > > {};
+
+   struct body : sor< import, package, option, message, enum_def, service, extend, semi > {};
+
+   struct quote : one< '\'', '"' > {};
+   struct head : if_must< keyword< 's', 'y', 'n', 't', 'a', 'x' >, sps, equ, sps, quote, string< 'p', 'r', 'o', 't', 'o', '3' >, quote, sps, semi > {};
+   struct proto : must< sps, head, sps, star< body, sps >, eof > {};
+   // clang-format on
+
+}  // namespace TAO_PEGTL_NAMESPACE::proto3
+
+#endif
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/raw_string.hpp b/packages/PEGTL/include/tao/pegtl/contrib/raw_string.hpp
index 819fb6248681a9b4e3dc95f213a914f00492676b..e24c578bcbbe4e7cc49181af95a9b465986bd79a 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/raw_string.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/raw_string.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_RAW_STRING_HPP
 #define TAO_PEGTL_CONTRIB_RAW_STRING_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/remove_first_state.hpp b/packages/PEGTL/include/tao/pegtl/contrib/remove_first_state.hpp
index 418347b757493e4acf8ea95e91209f3c5fa702d9..74384c63973b752330946ad98703b79aeea821a0 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/remove_first_state.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/remove_first_state.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_REMOVE_FIRST_STATE_HPP
 #define TAO_PEGTL_CONTRIB_REMOVE_FIRST_STATE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/remove_last_states.hpp b/packages/PEGTL/include/tao/pegtl/contrib/remove_last_states.hpp
index 8c6246407ff22fc68cf27f0150e48a9943272c71..5a4d67ecc2bf9481cc3a81e27ce7fe8328826662 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/remove_last_states.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/remove_last_states.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_REMOVE_LAST_STATES_HPP
 #define TAO_PEGTL_CONTRIB_REMOVE_LAST_STATES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/rep_one_min_max.hpp b/packages/PEGTL/include/tao/pegtl/contrib/rep_one_min_max.hpp
index 13df3b3faa63d74d1f50e2de9adaa4fb21a2a0a5..785e46f98004242ba1202424ce5d733b931e9055 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/rep_one_min_max.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/rep_one_min_max.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_REP_ONE_MIN_MAX_HPP
 #define TAO_PEGTL_CONTRIB_REP_ONE_MIN_MAX_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/rep_string.hpp b/packages/PEGTL/include/tao/pegtl/contrib/rep_string.hpp
index d29040d4a20fdee9016f8a75f536cac3922c1c41..b42061667f759180af7dba159877423a3f226421 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/rep_string.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/rep_string.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_REP_STRING_HPP
 #define TAO_PEGTL_CONTRIB_REP_STRING_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/separated_seq.hpp b/packages/PEGTL/include/tao/pegtl/contrib/separated_seq.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..26aac6ab4e5ca78977ce19ba8de23a5224a11451
--- /dev/null
+++ b/packages/PEGTL/include/tao/pegtl/contrib/separated_seq.hpp
@@ -0,0 +1,46 @@
+// Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
+#ifndef TAO_PEGTL_CONTRIB_SEPARATED_SEQ_HPP
+#define TAO_PEGTL_CONTRIB_SEPARATED_SEQ_HPP
+
+#include "../config.hpp"
+
+#include "../internal/seq.hpp"
+#include "../type_list.hpp"
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   namespace internal
+   {
+      template< typename... >
+      struct sep;
+
+      template< typename... Ts, typename S, typename Rule, typename... Rules >
+      struct sep< type_list< Ts... >, S, Rule, Rules... >
+         : sep< type_list< Ts..., Rule, S >, S, Rules... >
+      {};
+
+      template< typename... Ts, typename S, typename Rule >
+      struct sep< type_list< Ts... >, S, Rule >
+      {
+         using type = seq< Ts..., Rule >;
+      };
+
+      template< typename S >
+      struct sep< type_list<>, S >
+      {
+         using type = seq<>;
+      };
+
+   }  // namespace internal
+
+   template< typename S, typename... Rules >
+   struct separated_seq
+      : internal::sep< type_list<>, S, Rules... >::type
+   {};
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#endif
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/shuffle_states.hpp b/packages/PEGTL/include/tao/pegtl/contrib/shuffle_states.hpp
index 31faaea9d4b1cab5fd2632df28a91def2f30620a..8cc969048b756db43f1d7059ade41a8dd1303167 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/shuffle_states.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/shuffle_states.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_SHUFFLE_STATES_HPP
 #define TAO_PEGTL_CONTRIB_SHUFFLE_STATES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/state_control.hpp b/packages/PEGTL/include/tao/pegtl/contrib/state_control.hpp
index cb41b058c6bceb2c9022dc4dc5126b828d085316..a65a61b057b96a001777f60cd2c1227e922d7435 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/state_control.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/state_control.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_STATE_CONTROL_HPP
 #define TAO_PEGTL_CONTRIB_STATE_CONTROL_HPP
@@ -32,7 +33,8 @@ namespace TAO_PEGTL_NAMESPACE
                state.template start< Rule >( in, st... );
             }
 #if defined( _MSC_VER )
-            ( (void)st, ... );
+            ( (void)st,
+              ... );
 #endif
          }
 
@@ -46,7 +48,8 @@ namespace TAO_PEGTL_NAMESPACE
                Control< Rule >::success( in, st... );
             }
 #if defined( _MSC_VER )
-            ( (void)st, ... );
+            ( (void)st,
+              ... );
 #endif
          }
 
@@ -60,7 +63,8 @@ namespace TAO_PEGTL_NAMESPACE
                Control< Rule >::failure( in, st... );
             }
 #if defined( _MSC_VER )
-            ( (void)st, ... );
+            ( (void)st,
+              ... );
 #endif
          }
 
@@ -84,7 +88,8 @@ namespace TAO_PEGTL_NAMESPACE
                Control< Rule >::unwind( in, st... );
             }
 #if defined( _MSC_VER )
-            ( (void)st, ... );
+            ( (void)st,
+              ... );
 #endif
          }
 
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/to_string.hpp b/packages/PEGTL/include/tao/pegtl/contrib/to_string.hpp
index 987f02875f6d45f1ccb784fbd75a631896d85e81..1075e897efe9512e53245ea9ace873e5fad48c31 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/to_string.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/to_string.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_TO_STRING_HPP
 #define TAO_PEGTL_CONTRIB_TO_STRING_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/trace.hpp b/packages/PEGTL/include/tao/pegtl/contrib/trace.hpp
index d1ac4380b25d2494cb824ec06b18c853ea468ae6..751ceb50a341b597bc8d2b266c014a505db82b19 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/trace.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/trace.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_TRACE_HPP
 #define TAO_PEGTL_CONTRIB_TRACE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/uint16.hpp b/packages/PEGTL/include/tao/pegtl/contrib/uint16.hpp
index c3c1bf1e35925a292841578cb299cb119a7dd6af..1f7532861aa9c65b5a5e4570db86c0cc8f85f4a7 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/uint16.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/uint16.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_UINT16_HPP
 #define TAO_PEGTL_CONTRIB_UINT16_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/uint32.hpp b/packages/PEGTL/include/tao/pegtl/contrib/uint32.hpp
index 39a46c32230c8d000a8bbeaf1f3ca0b878cece2c..8bd88045bfa85c69a3dab544ecfc562f5201819d 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/uint32.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/uint32.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_UINT32_HPP
 #define TAO_PEGTL_CONTRIB_UINT32_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/uint64.hpp b/packages/PEGTL/include/tao/pegtl/contrib/uint64.hpp
index 3084a59d034f6264c668ebf0806bf883ccf7846b..366c9f4a6e95b7547f21d207495bdd19cb5c7a07 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/uint64.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/uint64.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_UINT64_HPP
 #define TAO_PEGTL_CONTRIB_UINT64_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/uint8.hpp b/packages/PEGTL/include/tao/pegtl/contrib/uint8.hpp
index f9e7dc7cd40fcc6e28a8d1eee284b9252f324cba..a5b325a298cea15ddac33de276364a14ae48c10b 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/uint8.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/uint8.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_UINT8_HPP
 #define TAO_PEGTL_CONTRIB_UINT8_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/unescape.hpp b/packages/PEGTL/include/tao/pegtl/contrib/unescape.hpp
index 5079f8275d72a178a5bd5527c3f85e12e8f9764e..7fb0c081ba054bce64d0d3fcd3ba3a7019f94b87 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/unescape.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/unescape.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_UNESCAPE_HPP
 #define TAO_PEGTL_CONTRIB_UNESCAPE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/uri.hpp b/packages/PEGTL/include/tao/pegtl/contrib/uri.hpp
index 701b2bb4102a6bdaa3d9b194cbd15ee5a74d4ed9..b019a7b36bb06e259cacf2016f2cddc8138a55e3 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/uri.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/uri.hpp
@@ -1,11 +1,12 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_URI_HPP
 #define TAO_PEGTL_CONTRIB_URI_HPP
 
 #if !defined( __cpp_exceptions )
-#error "Exception support required tao/pegtl/contrib/uri.hpp"
+#error "Exception support required for tao/pegtl/contrib/uri.hpp"
 #else
 
 #include <cstdint>
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/utf16.hpp b/packages/PEGTL/include/tao/pegtl/contrib/utf16.hpp
index 090b6b0615069e13990cb21a5fbcaf81aca14f8a..74915e16d44c3ca310e3717a894762dbe54d659b 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/utf16.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/utf16.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_UTF16_HPP
 #define TAO_PEGTL_CONTRIB_UTF16_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/utf32.hpp b/packages/PEGTL/include/tao/pegtl/contrib/utf32.hpp
index d8b80517a6b5bec4f56e9c085f40f165e45aa9bb..1201fca885e907e8fd2011ef6d48e20248ad5662 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/utf32.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/utf32.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CONTRIB_UTF32_HPP
 #define TAO_PEGTL_CONTRIB_UTF32_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/cstream_input.hpp b/packages/PEGTL/include/tao/pegtl/cstream_input.hpp
index 1496927b46cd78c85afeeee647a24798ec53a141..c589912261a99aeee6d9983c20be89f6a034b5d9 100644
--- a/packages/PEGTL/include/tao/pegtl/cstream_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/cstream_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_CSTREAM_INPUT_HPP
 #define TAO_PEGTL_CSTREAM_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/demangle.hpp b/packages/PEGTL/include/tao/pegtl/demangle.hpp
index 56cc170c6bd038b18cb95aa323ba033ccaeec45a..97b2606528390d15f53152cb235056ce2aa02c76 100644
--- a/packages/PEGTL/include/tao/pegtl/demangle.hpp
+++ b/packages/PEGTL/include/tao/pegtl/demangle.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_DEMANGLE_HPP
 #define TAO_PEGTL_DEMANGLE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/disable_action.hpp b/packages/PEGTL/include/tao/pegtl/disable_action.hpp
index 0781516f4ecd0cf39e938360d92aa76b96a958a3..92bc2f4d3c0f5bc1c182a14b085729af580bab5f 100644
--- a/packages/PEGTL/include/tao/pegtl/disable_action.hpp
+++ b/packages/PEGTL/include/tao/pegtl/disable_action.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_DISABLE_ACTION_HPP
 #define TAO_PEGTL_DISABLE_ACTION_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/discard_input.hpp b/packages/PEGTL/include/tao/pegtl/discard_input.hpp
index 61ddf0dd224e2d129b0b22ffb09f7a6ae3cb7301..dec0a9af57f812850f981ad141487e01d2baf869 100644
--- a/packages/PEGTL/include/tao/pegtl/discard_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/discard_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_DISCARD_INPUT_HPP
 #define TAO_PEGTL_DISCARD_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/discard_input_on_failure.hpp b/packages/PEGTL/include/tao/pegtl/discard_input_on_failure.hpp
index 6e4a9cd3014504519b7edd5d50a0ac954dcdda8c..305038e96b4d44663d71508d932e848e35ed92aa 100644
--- a/packages/PEGTL/include/tao/pegtl/discard_input_on_failure.hpp
+++ b/packages/PEGTL/include/tao/pegtl/discard_input_on_failure.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_DISCARD_INPUT_ON_FAILURE_HPP
 #define TAO_PEGTL_DISCARD_INPUT_ON_FAILURE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/discard_input_on_success.hpp b/packages/PEGTL/include/tao/pegtl/discard_input_on_success.hpp
index 3edc271bdc0136d796ea2fbe0953f1efefd1edad..c5beb7d91e56bb68ea4a533e913ea36899ccd309 100644
--- a/packages/PEGTL/include/tao/pegtl/discard_input_on_success.hpp
+++ b/packages/PEGTL/include/tao/pegtl/discard_input_on_success.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_DISCARD_INPUT_ON_SUCCESS_HPP
 #define TAO_PEGTL_DISCARD_INPUT_ON_SUCCESS_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/enable_action.hpp b/packages/PEGTL/include/tao/pegtl/enable_action.hpp
index a87726f8852029e49fa73861d80a6e40c4075c88..ae0c09983afdb813ce806793beeb24cec0989140 100644
--- a/packages/PEGTL/include/tao/pegtl/enable_action.hpp
+++ b/packages/PEGTL/include/tao/pegtl/enable_action.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_ENABLE_ACTION_HPP
 #define TAO_PEGTL_ENABLE_ACTION_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/eol.hpp b/packages/PEGTL/include/tao/pegtl/eol.hpp
index 8c496da7ebcc4ec937c45e2057870178715493ae..c2df140e3edf028695f3be563f4942e075d09e4b 100644
--- a/packages/PEGTL/include/tao/pegtl/eol.hpp
+++ b/packages/PEGTL/include/tao/pegtl/eol.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_EOL_HPP
 #define TAO_PEGTL_EOL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/eol_pair.hpp b/packages/PEGTL/include/tao/pegtl/eol_pair.hpp
index 239af370ead7b6d26e6c7d8aef12c6dd1dfa3bcf..b139d3bdccffb9726233a52cf65108a96a628525 100644
--- a/packages/PEGTL/include/tao/pegtl/eol_pair.hpp
+++ b/packages/PEGTL/include/tao/pegtl/eol_pair.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_EOL_PAIR_HPP
 #define TAO_PEGTL_EOL_PAIR_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/file_input.hpp b/packages/PEGTL/include/tao/pegtl/file_input.hpp
index 91895b31e4a9aad63b6968376eb9a2710e15b825..408b89fc0e2cbfdb62342c78d111e7ca5a475017 100644
--- a/packages/PEGTL/include/tao/pegtl/file_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/file_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_FILE_INPUT_HPP
 #define TAO_PEGTL_FILE_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/action.hpp b/packages/PEGTL/include/tao/pegtl/internal/action.hpp
index 3443c299775424dc08b547662b9565826bd46149..8068241e87b14c3a72419038c30b29fd7da7f820 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/action.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/action.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_ACTION_HPP
 #define TAO_PEGTL_INTERNAL_ACTION_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/action_input.hpp b/packages/PEGTL/include/tao/pegtl/internal/action_input.hpp
index ccc2d16501d84ac9926fe900bad3af0765a5ed50..fa622fa326cf0e87d9b9dc3ce91dda531b753070 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/action_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/action_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_ACTION_INPUT_HPP
 #define TAO_PEGTL_INTERNAL_ACTION_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/any.hpp b/packages/PEGTL/include/tao/pegtl/internal/any.hpp
index 59ba05f5af3d052e99355a759b977435853462e5..9e710c5f876d154e3f78f4f631b6da419f9ed9b2 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/any.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/any.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_ANY_HPP
 #define TAO_PEGTL_INTERNAL_ANY_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/apply.hpp b/packages/PEGTL/include/tao/pegtl/internal/apply.hpp
index cc5e9fd9e7f39de0d1da9413cb6cecad55d371e8..97a342b86a45a096fa90a50af91113c698a9a39a 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/apply.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/apply.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_APPLY_HPP
 #define TAO_PEGTL_INTERNAL_APPLY_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/apply0.hpp b/packages/PEGTL/include/tao/pegtl/internal/apply0.hpp
index a6e37f4e4d710f96a6deff53493d4ca920eb4750..2710de8e8ab89eea8b357b7de28f4e0dcd8b20d4 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/apply0.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/apply0.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_APPLY0_HPP
 #define TAO_PEGTL_INTERNAL_APPLY0_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/apply0_single.hpp b/packages/PEGTL/include/tao/pegtl/internal/apply0_single.hpp
index 11939064d828dbf41e00dce2cca02e5765bd2a04..167031924635e65c26d8a67093556fc4e33f67b5 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/apply0_single.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/apply0_single.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_APPLY0_SINGLE_HPP
 #define TAO_PEGTL_INTERNAL_APPLY0_SINGLE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/apply_single.hpp b/packages/PEGTL/include/tao/pegtl/internal/apply_single.hpp
index 57b0ca8fc0e66c0093ec087953a86076c765b1db..728ad4772e6d85a23f9f3d47bdde272543472b45 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/apply_single.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/apply_single.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_APPLY_SINGLE_HPP
 #define TAO_PEGTL_INTERNAL_APPLY_SINGLE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/at.hpp b/packages/PEGTL/include/tao/pegtl/internal/at.hpp
index 7f7d2894c52fe42c45d3736af80d103b2357035a..117b5a429d943d5c224e4afcb3254c266bb600a5 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/at.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/at.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_AT_HPP
 #define TAO_PEGTL_INTERNAL_AT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/bof.hpp b/packages/PEGTL/include/tao/pegtl/internal/bof.hpp
index da5c920bcc8e30dd16389efaae616beeb1558053..7038d953f77dd727003bcd7d3e526f9d74b0e9e8 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/bof.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/bof.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_BOF_HPP
 #define TAO_PEGTL_INTERNAL_BOF_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/bol.hpp b/packages/PEGTL/include/tao/pegtl/internal/bol.hpp
index a71109d77c3b3bb8a65d4e116f766c0dc8931d0a..759a8bd01cf91665908301f5a25ad4365ce1c094 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/bol.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/bol.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_BOL_HPP
 #define TAO_PEGTL_INTERNAL_BOL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/bump.hpp b/packages/PEGTL/include/tao/pegtl/internal/bump.hpp
index 02e2278a264596c4cfed965bfa249c91fc41b6d9..1b305f3a2f65187c05c38f16c662e81cec364d8d 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/bump.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/bump.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_BUMP_HPP
 #define TAO_PEGTL_INTERNAL_BUMP_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/bump_help.hpp b/packages/PEGTL/include/tao/pegtl/internal/bump_help.hpp
index 68c82b99758b59f8e960874e56e2534eefc69ca2..f40c6171cb97390956532ea7ae67b92decb3241b 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/bump_help.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/bump_help.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_BUMP_HELP_HPP
 #define TAO_PEGTL_INTERNAL_BUMP_HELP_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/bytes.hpp b/packages/PEGTL/include/tao/pegtl/internal/bytes.hpp
index 476ce965df766c1869edc11657b8bdc6b1084873..2012f05c74c340ec6084d0855aae830d53b49a9f 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/bytes.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/bytes.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_BYTES_HPP
 #define TAO_PEGTL_INTERNAL_BYTES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/control.hpp b/packages/PEGTL/include/tao/pegtl/internal/control.hpp
index e434ae06769efe1884ab87b71d898cba24fc764f..a202f5d43922327f9ac482f42a443ccb3fe776aa 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/control.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/control.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_CONTROL_HPP
 #define TAO_PEGTL_INTERNAL_CONTROL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/cr_crlf_eol.hpp b/packages/PEGTL/include/tao/pegtl/internal/cr_crlf_eol.hpp
index 22504513922d6ace165d1cef4cf51bb27910e6df..cf2e72ec8a74b11f8b89526b1d861233b4b6b137 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/cr_crlf_eol.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/cr_crlf_eol.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_CR_CRLF_EOL_HPP
 #define TAO_PEGTL_INTERNAL_CR_CRLF_EOL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/cr_eol.hpp b/packages/PEGTL/include/tao/pegtl/internal/cr_eol.hpp
index 833a7c0c31b193f5b0240991e32018fc46d385f6..5d1a61951134c91904c87bb09d3cfbd9925872f5 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/cr_eol.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/cr_eol.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_CR_EOL_HPP
 #define TAO_PEGTL_INTERNAL_CR_EOL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/crlf_eol.hpp b/packages/PEGTL/include/tao/pegtl/internal/crlf_eol.hpp
index 5b71de8306191a885e6352303f68ddbadb51dd8b..f419a6f483ec64097884988a33ebd04240d14c0c 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/crlf_eol.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/crlf_eol.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_CRLF_EOL_HPP
 #define TAO_PEGTL_INTERNAL_CRLF_EOL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/cstream_reader.hpp b/packages/PEGTL/include/tao/pegtl/internal/cstream_reader.hpp
index e8ca048c3e202fe2df558b8cb33b13ecf6ef7898..0dc630539a2d9a0b066986635627a29197b827d1 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/cstream_reader.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/cstream_reader.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_CSTREAM_READER_HPP
 #define TAO_PEGTL_INTERNAL_CSTREAM_READER_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/cstring_reader.hpp b/packages/PEGTL/include/tao/pegtl/internal/cstring_reader.hpp
index c98044c812d3f3a4f48bc41bc279c4243a2c5993..4c3b10e7496daccf5c75781f3b76f1c3f52b1905 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/cstring_reader.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/cstring_reader.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_CSTRING_READER_HPP
 #define TAO_PEGTL_INTERNAL_CSTRING_READER_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/dependent_false.hpp b/packages/PEGTL/include/tao/pegtl/internal/dependent_false.hpp
index 57661a415d295fbdb559518964e91b326f41e896..a5cc470a3a6cdb78b7f8f08d15ff39de33fbfa68 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/dependent_false.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/dependent_false.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_DEPENDENT_FALSE_HPP
 #define TAO_PEGTL_INTERNAL_DEPENDENT_FALSE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/disable.hpp b/packages/PEGTL/include/tao/pegtl/internal/disable.hpp
index 9a294d3b8415bcf2733902dac9e61f9af3ec3ca2..76bdc5a8e1c0cbb0c6903c8bf25dc830856bd24c 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/disable.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/disable.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_DISABLE_HPP
 #define TAO_PEGTL_INTERNAL_DISABLE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/discard.hpp b/packages/PEGTL/include/tao/pegtl/internal/discard.hpp
index b92760d04479a95d0d21a9f2a5bb2807004c75a3..bea7aa3f2dace46471fcc4be7e683f7c22db1c36 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/discard.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/discard.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_DISCARD_HPP
 #define TAO_PEGTL_INTERNAL_DISCARD_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/enable.hpp b/packages/PEGTL/include/tao/pegtl/internal/enable.hpp
index 9c453435c97d5efe2be4d7e46da8ba2f8dc612e2..cb4fbe9dbc7a4db4bf9729b4a7bc3d93c8054379 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/enable.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/enable.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_ENABLE_HPP
 #define TAO_PEGTL_INTERNAL_ENABLE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/enable_control.hpp b/packages/PEGTL/include/tao/pegtl/internal/enable_control.hpp
index 16ca4236900044b7f29930d739a569d614d14205..4fe5ae199cbbc1fe5d7950d4250cf7066763b555 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/enable_control.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/enable_control.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_ENABLE_CONTROL_HPP
 #define TAO_PEGTL_INTERNAL_ENABLE_CONTROL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/eof.hpp b/packages/PEGTL/include/tao/pegtl/internal/eof.hpp
index c4f0f81321811fa876c7e79894e7f8f0fcee4540..e227e0474fd0f19b40a0b070d599c77422972ee4 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/eof.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/eof.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_EOF_HPP
 #define TAO_PEGTL_INTERNAL_EOF_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/eol.hpp b/packages/PEGTL/include/tao/pegtl/internal/eol.hpp
index ecec04f32defd31c8f65e27abc0a0070e12219ba..6f8633afc96504de82d797af0aaf5563f02698c5 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/eol.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/eol.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_EOL_HPP
 #define TAO_PEGTL_INTERNAL_EOL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/eolf.hpp b/packages/PEGTL/include/tao/pegtl/internal/eolf.hpp
index 71fa8d891398473c985b8fe7e055b7f7bed462f5..101605b9e50d30e875722749e8c76ec1fee9203b 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/eolf.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/eolf.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_EOLF_HPP
 #define TAO_PEGTL_INTERNAL_EOLF_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/failure.hpp b/packages/PEGTL/include/tao/pegtl/internal/failure.hpp
index 75084997700d74d50672e0a6974de0ff4f08fafd..8f14322b8167f6466dd51a456f289a0c352b8a1a 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/failure.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/failure.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_FAILURE_HPP
 #define TAO_PEGTL_INTERNAL_FAILURE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/file_mapper_posix.hpp b/packages/PEGTL/include/tao/pegtl/internal/file_mapper_posix.hpp
index 0c60e3a4a6a85eb28e419826072e36e6ffb62cc5..def276bce3bab1a100934f66f45946dde0baf31c 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/file_mapper_posix.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/file_mapper_posix.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_FILE_MAPPER_POSIX_HPP
 #define TAO_PEGTL_INTERNAL_FILE_MAPPER_POSIX_HPP
@@ -54,7 +55,7 @@ namespace TAO_PEGTL_NAMESPACE::internal
             std::perror( "fstat() failed" );
             std::terminate();
 #endif
-            // LCOV_EXCL_END
+            // LCOV_EXCL_STOP
          }
          return std::size_t( st.st_size );
       }
@@ -105,7 +106,7 @@ namespace TAO_PEGTL_NAMESPACE::internal
             std::perror( "mmap() failed" );
             std::terminate();
 #endif
-            // LCOV_EXCL_END
+            // LCOV_EXCL_STOP
          }
       }
 
diff --git a/packages/PEGTL/include/tao/pegtl/internal/file_mapper_win32.hpp b/packages/PEGTL/include/tao/pegtl/internal/file_mapper_win32.hpp
index 73c27dec90556f0b091ed191a28ca6c4557e3d95..4a353ccf4fd0feda569010d281352ed39ba42b66 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/file_mapper_win32.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/file_mapper_win32.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_FILE_MAPPER_WIN32_HPP
 #define TAO_PEGTL_INTERNAL_FILE_MAPPER_WIN32_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/file_reader.hpp b/packages/PEGTL/include/tao/pegtl/internal/file_reader.hpp
index 7c2b054942877d96d308ea4be0a27e49e1917f00..a842f4bd005724dea283075b7fbcca07c66b9b0d 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/file_reader.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/file_reader.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_FILE_READER_HPP
 #define TAO_PEGTL_INTERNAL_FILE_READER_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/filesystem.hpp b/packages/PEGTL/include/tao/pegtl/internal/filesystem.hpp
index ad241abd222ac917a8032b91bdb2be3e8c62cc00..fdb5e2f1013cf8af8dd337724a6a33690d512ed5 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/filesystem.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/filesystem.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_FILESYSTEM_HPP
 #define TAO_PEGTL_INTERNAL_FILESYSTEM_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/has_apply.hpp b/packages/PEGTL/include/tao/pegtl/internal/has_apply.hpp
index 697f6ff3c62528307a65a733497a305149fdd3d1..5b746ebccb65a25f284c471d9c20445a3da7ff91 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/has_apply.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/has_apply.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_HAS_APPLY_HPP
 #define TAO_PEGTL_INTERNAL_HAS_APPLY_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/has_apply0.hpp b/packages/PEGTL/include/tao/pegtl/internal/has_apply0.hpp
index 3f5fc599d3703110a6d568d236bd74ecdafeb06b..cb14882ac98895a1ebf86f7743b1b1624f4b42ce 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/has_apply0.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/has_apply0.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_HAS_APPLY0_HPP
 #define TAO_PEGTL_INTERNAL_HAS_APPLY0_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/has_match.hpp b/packages/PEGTL/include/tao/pegtl/internal/has_match.hpp
index 0606d7a5a3d42c22fef50e80dc75908b63bccbb2..ab842587437d7e32e36ab6dfabb64fad61e2dbae 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/has_match.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/has_match.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_HAS_MATCH_HPP
 #define TAO_PEGTL_INTERNAL_HAS_MATCH_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/has_unwind.hpp b/packages/PEGTL/include/tao/pegtl/internal/has_unwind.hpp
index 566c24e4e5f2cc14b8f54cadc4a5c68eb2480653..8714d93725868dfef1a148c6c6449a8841b26397 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/has_unwind.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/has_unwind.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_HAS_UNWIND_HPP
 #define TAO_PEGTL_INTERNAL_HAS_UNWIND_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/identifier.hpp b/packages/PEGTL/include/tao/pegtl/internal/identifier.hpp
index 4ecbbb3244959798596a667842bc6524a7e4d8da..fc78c220b993192dc47bc9375e933fd3d5ef374c 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/identifier.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/identifier.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_IDENTIFIER_HPP
 #define TAO_PEGTL_INTERNAL_IDENTIFIER_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/if_apply.hpp b/packages/PEGTL/include/tao/pegtl/internal/if_apply.hpp
index 27dd002abd37be48b5d75d45397adec761745cd8..797be335928f20dc3bb16772ca59c45f9e1af215 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/if_apply.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/if_apply.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_IF_APPLY_HPP
 #define TAO_PEGTL_INTERNAL_IF_APPLY_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/if_must.hpp b/packages/PEGTL/include/tao/pegtl/internal/if_must.hpp
index 4cf2260e10f32406c4a3d04c589531304261ed46..9f5e0bacc452056815610a0cce362aaf22fb2aa4 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/if_must.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/if_must.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_IF_MUST_HPP
 #define TAO_PEGTL_INTERNAL_IF_MUST_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/if_must_else.hpp b/packages/PEGTL/include/tao/pegtl/internal/if_must_else.hpp
index 685c7ca211bc0615a88369dc6803e46c1fc77b44..51ad934824b5a48cb010fc909903c3691e3f05ea 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/if_must_else.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/if_must_else.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_IF_MUST_ELSE_HPP
 #define TAO_PEGTL_INTERNAL_IF_MUST_ELSE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/if_then_else.hpp b/packages/PEGTL/include/tao/pegtl/internal/if_then_else.hpp
index bc6fa3cea2484526cfe0589763b8a1b1404b6b7c..dfe62ceb957f1943102f0837f4742fb64a3afa0a 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/if_then_else.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/if_then_else.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_IF_THEN_ELSE_HPP
 #define TAO_PEGTL_INTERNAL_IF_THEN_ELSE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/input_pair.hpp b/packages/PEGTL/include/tao/pegtl/internal/input_pair.hpp
index a3bf92210652df71f37f0af4271e8667aad442bf..423375c23f311ca3269a725280b8204b0a2484ac 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/input_pair.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/input_pair.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_INPUT_PAIR_HPP
 #define TAO_PEGTL_INTERNAL_INPUT_PAIR_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/istream_reader.hpp b/packages/PEGTL/include/tao/pegtl/internal/istream_reader.hpp
index 371ffbab5c27bbdf97432f2b3adbc39451cff64d..4b67437194f2e69f73ab6762e58a5ffb941c9c26 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/istream_reader.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/istream_reader.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_ISTREAM_READER_HPP
 #define TAO_PEGTL_INTERNAL_ISTREAM_READER_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/istring.hpp b/packages/PEGTL/include/tao/pegtl/internal/istring.hpp
index f7abe365feef29043788340c99b4d2b9bcddc4b4..5969c0672f8e2075f691daa0328fcd359cccbf04 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/istring.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/istring.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_ISTRING_HPP
 #define TAO_PEGTL_INTERNAL_ISTRING_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/iterator.hpp b/packages/PEGTL/include/tao/pegtl/internal/iterator.hpp
index 758016772646b38eb436b712fe1ccc6870100a36..96099cab899619a0cab5815caa8a79aff3e4128d 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/iterator.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/iterator.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_ITERATOR_HPP
 #define TAO_PEGTL_INTERNAL_ITERATOR_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/lf_crlf_eol.hpp b/packages/PEGTL/include/tao/pegtl/internal/lf_crlf_eol.hpp
index c3eae42c1b1a2e32ba85071926bab84d119c3cfc..ac266014a7f07f1990149e47241cc3e38d1c2f09 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/lf_crlf_eol.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/lf_crlf_eol.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_LF_CRLF_EOL_HPP
 #define TAO_PEGTL_INTERNAL_LF_CRLF_EOL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/lf_eol.hpp b/packages/PEGTL/include/tao/pegtl/internal/lf_eol.hpp
index 0f4e33d64925aa4fd75888e75407d7803e85da28..d0f3adceb71904a82e4848f253b4ea6a02d4b31b 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/lf_eol.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/lf_eol.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_LF_EOL_HPP
 #define TAO_PEGTL_INTERNAL_LF_EOL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/list.hpp b/packages/PEGTL/include/tao/pegtl/internal/list.hpp
index 07318e6572ac825d67e8eb562a85d81b6e62e889..24a3935163bfa627e80cc21df792f9cca5d3f41a 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/list.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/list.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_LIST_HPP
 #define TAO_PEGTL_INTERNAL_LIST_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/list_must.hpp b/packages/PEGTL/include/tao/pegtl/internal/list_must.hpp
index aff1786960e1e4e053f95232af296d665d5faf76..1d9cef89ef7b31f22512bc1f426981a43409d081 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/list_must.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/list_must.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_LIST_MUST_HPP
 #define TAO_PEGTL_INTERNAL_LIST_MUST_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/list_tail.hpp b/packages/PEGTL/include/tao/pegtl/internal/list_tail.hpp
index 5fe3ff413ed1be126fccc90c94f2d362fa5cfbbf..59fae07ff18b969770d379084ac84752a57721ac 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/list_tail.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/list_tail.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_LIST_TAIL_HPP
 #define TAO_PEGTL_INTERNAL_LIST_TAIL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/list_tail_pad.hpp b/packages/PEGTL/include/tao/pegtl/internal/list_tail_pad.hpp
index b96c626b8e8fe4b233a4cbcd4a2e28f4ba99f92c..f204b23e83f09302731442a12a71f8aae79fe27e 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/list_tail_pad.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/list_tail_pad.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_LIST_TAIL_PAD_HPP
 #define TAO_PEGTL_INTERNAL_LIST_TAIL_PAD_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/marker.hpp b/packages/PEGTL/include/tao/pegtl/internal/marker.hpp
index a9ffa824f356e85addebb0c99ddab3373c62b3df..a061aea738eef1866887be743e4f6125605526a3 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/marker.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/marker.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_MARKER_HPP
 #define TAO_PEGTL_INTERNAL_MARKER_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/minus.hpp b/packages/PEGTL/include/tao/pegtl/internal/minus.hpp
index 4062f420e6bb7a301bb71b964b02a49249ba5562..f89ec02c8bf04a4937a5f8a0036657899d63aa5d 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/minus.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/minus.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_MINUS_HPP
 #define TAO_PEGTL_INTERNAL_MINUS_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/missing_apply.hpp b/packages/PEGTL/include/tao/pegtl/internal/missing_apply.hpp
index e69cbf3a9b30187091b0dbe3699f434aa9dd54fb..b979c1420357a2d24dd6762a8a384d899c3700c9 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/missing_apply.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/missing_apply.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_MISSING_APPLY_HPP
 #define TAO_PEGTL_INTERNAL_MISSING_APPLY_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/missing_apply0.hpp b/packages/PEGTL/include/tao/pegtl/internal/missing_apply0.hpp
index 2b63a28e5a50f30363b8761c2a7f4eb3bc7e9213..fedd156c0cb6b622488f261545effc523db15742 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/missing_apply0.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/missing_apply0.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_MISSING_APPLY0_HPP
 #define TAO_PEGTL_INTERNAL_MISSING_APPLY0_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/must.hpp b/packages/PEGTL/include/tao/pegtl/internal/must.hpp
index 213fc56eb2f0c336ea63d0c78c801c2cceeeb6e0..cc77f705814392f5f43af452367d84c600172819 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/must.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/must.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_MUST_HPP
 #define TAO_PEGTL_INTERNAL_MUST_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/not_at.hpp b/packages/PEGTL/include/tao/pegtl/internal/not_at.hpp
index 00758969724a0838d1a3f0d93c20997afffe9d1c..2e27e2d629795cfd5b90ec53829bc5c974459c83 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/not_at.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/not_at.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_NOT_AT_HPP
 #define TAO_PEGTL_INTERNAL_NOT_AT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/one.hpp b/packages/PEGTL/include/tao/pegtl/internal/one.hpp
index e2bc05ef41cc1bc599ab0be5ce0fe73f3577e8ad..2eac40f659d9d982b7b60b2200b10e6495551ac1 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/one.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/one.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_ONE_HPP
 #define TAO_PEGTL_INTERNAL_ONE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/opt.hpp b/packages/PEGTL/include/tao/pegtl/internal/opt.hpp
index 9dd1a0cf419401bc657ec1d6f68b959db58529fe..4a10afe77090dea98fe3bcab1d4e570514f1318c 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/opt.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/opt.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_OPT_HPP
 #define TAO_PEGTL_INTERNAL_OPT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/pad.hpp b/packages/PEGTL/include/tao/pegtl/internal/pad.hpp
index ff49ce22c78dc9626fb62913e4973805745b073d..13b473e2479b27f6fb738f1b7d9bf4a18b774599 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/pad.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/pad.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_PAD_HPP
 #define TAO_PEGTL_INTERNAL_PAD_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/pad_opt.hpp b/packages/PEGTL/include/tao/pegtl/internal/pad_opt.hpp
index 02af2363a9a8fffb48f777e66058c7aa78ef29da..80b5064f796b8eb8818b8946742afc80871e69ec 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/pad_opt.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/pad_opt.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_PAD_OPT_HPP
 #define TAO_PEGTL_INTERNAL_PAD_OPT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/path_to_string.hpp b/packages/PEGTL/include/tao/pegtl/internal/path_to_string.hpp
index d28dbac739888cda5138ec6ea399ad2ae01036a6..7d7757a0bdf50f785f95bf7c6980dde0731c5b5d 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/path_to_string.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/path_to_string.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_PATH_TO_STRING_HPP
 #define TAO_PEGTL_INTERNAL_PATH_TO_STRING_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/peek_char.hpp b/packages/PEGTL/include/tao/pegtl/internal/peek_char.hpp
index 7dbb06f699faaeca7e4d8f9808e06a6505a5b93f..e85e5607d94e95ce669cc2c9995d159928adb25c 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/peek_char.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/peek_char.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_PEEK_CHAR_HPP
 #define TAO_PEGTL_INTERNAL_PEEK_CHAR_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/peek_utf8.hpp b/packages/PEGTL/include/tao/pegtl/internal/peek_utf8.hpp
index 77f623d1069075b687f96053a70cfc9172f0cc78..a47d813a361d1af48209703b1bd6e108fd926c70 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/peek_utf8.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/peek_utf8.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_PEEK_UTF8_HPP
 #define TAO_PEGTL_INTERNAL_PEEK_UTF8_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/pegtl_string.hpp b/packages/PEGTL/include/tao/pegtl/internal/pegtl_string.hpp
index aac858d018e4300e0bf817c819776fd640bb14c4..576c216d286a45c7c8df26b8a6351526b5143855 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/pegtl_string.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/pegtl_string.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_PEGTL_STRING_HPP
 #define TAO_PEGTL_INTERNAL_PEGTL_STRING_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/plus.hpp b/packages/PEGTL/include/tao/pegtl/internal/plus.hpp
index 4a0ee5a99d9a7d6bf9e1f294d3794d35cda62e70..2ebdd3e62e42437d299fd4b38f7ff3ad338b4aaf 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/plus.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/plus.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_PLUS_HPP
 #define TAO_PEGTL_INTERNAL_PLUS_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/raise.hpp b/packages/PEGTL/include/tao/pegtl/internal/raise.hpp
index 9d9c831889c5be7b2ee2654f11565bb19511bef0..11db9fce43efc3c33d438eddabbba4c6f02e148f 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/raise.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/raise.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_RAISE_HPP
 #define TAO_PEGTL_INTERNAL_RAISE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/range.hpp b/packages/PEGTL/include/tao/pegtl/internal/range.hpp
index d1ad9e98dde2d61f40e9689032299042706e1122..f504161d1c93485b4643c4ae74e055695b974ee7 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/range.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/range.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_RANGE_HPP
 #define TAO_PEGTL_INTERNAL_RANGE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/ranges.hpp b/packages/PEGTL/include/tao/pegtl/internal/ranges.hpp
index bf57db611e00058ae681096676cb5893efe7b355..9df06d7b3d756778b2fc64dd1938378f7e156468 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/ranges.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/ranges.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_RANGES_HPP
 #define TAO_PEGTL_INTERNAL_RANGES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/rematch.hpp b/packages/PEGTL/include/tao/pegtl/internal/rematch.hpp
index a1f7e3496c283944bbbc9baaa270162677a7936f..4877a3401e6d9f81d8b29159f1742f15dd6608dc 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/rematch.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/rematch.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_REMATCH_HPP
 #define TAO_PEGTL_INTERNAL_REMATCH_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/rep.hpp b/packages/PEGTL/include/tao/pegtl/internal/rep.hpp
index 3ef122d64452670b63c5eeed229f1cfbcaff1d49..79a2be78917f73779e906b0e9c36b1dc552ab8c4 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/rep.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/rep.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_REP_HPP
 #define TAO_PEGTL_INTERNAL_REP_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/rep_min.hpp b/packages/PEGTL/include/tao/pegtl/internal/rep_min.hpp
index 73f8531aa3e7d56bd2f62f13bb85e263ff9d1ada..a6efe9e6734f44152544c4fc5ad8111ec662c21e 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/rep_min.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/rep_min.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_REP_MIN_HPP
 #define TAO_PEGTL_INTERNAL_REP_MIN_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/rep_min_max.hpp b/packages/PEGTL/include/tao/pegtl/internal/rep_min_max.hpp
index b600152f019e1d2362573215879a3c80a93f2ead..b0026458efa40eb655aeac6f2dede2ab7111836c 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/rep_min_max.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/rep_min_max.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_REP_MIN_MAX_HPP
 #define TAO_PEGTL_INTERNAL_REP_MIN_MAX_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/rep_opt.hpp b/packages/PEGTL/include/tao/pegtl/internal/rep_opt.hpp
index 435332d45fb393d6521526f257327bbc95b9d926..bea26e5be97d4ff612e2c68b6e19c22d21094009 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/rep_opt.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/rep_opt.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_REP_OPT_HPP
 #define TAO_PEGTL_INTERNAL_REP_OPT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/require.hpp b/packages/PEGTL/include/tao/pegtl/internal/require.hpp
index f2ce5a02280a90f72a787594f047da2c9ed9e834..b4e24fb158c13e5bc4c3b697e8e58f2c5c20be3f 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/require.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/require.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_REQUIRE_HPP
 #define TAO_PEGTL_INTERNAL_REQUIRE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/result_on_found.hpp b/packages/PEGTL/include/tao/pegtl/internal/result_on_found.hpp
index 09fa6ed6ee070ad76d1ba2c67e82bc7825847ebf..df2bcd77b0e9a3d9c97783d456c0e1dd0aed0e6c 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/result_on_found.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/result_on_found.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_RESULT_ON_FOUND_HPP
 #define TAO_PEGTL_INTERNAL_RESULT_ON_FOUND_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/rules.hpp b/packages/PEGTL/include/tao/pegtl/internal/rules.hpp
index 5496a94b8fa261133e276e0e2fddbcffbb871a61..2f89826f167b17fdb77da2a21e20ea930c42925f 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/rules.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/rules.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_RULES_HPP
 #define TAO_PEGTL_INTERNAL_RULES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/seq.hpp b/packages/PEGTL/include/tao/pegtl/internal/seq.hpp
index 11af3dcca8f208cdecd6b1a4e85b9ed2b5f53701..eb0a8624d7eea11ef11996f3885dff7092d6197a 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/seq.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/seq.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_SEQ_HPP
 #define TAO_PEGTL_INTERNAL_SEQ_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/sor.hpp b/packages/PEGTL/include/tao/pegtl/internal/sor.hpp
index 0a8fcdf73a718634b8d208fc3f07e40bd2766e04..b09d6779c0cd781257028754012ab5f116afb35b 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/sor.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/sor.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_SOR_HPP
 #define TAO_PEGTL_INTERNAL_SOR_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/star.hpp b/packages/PEGTL/include/tao/pegtl/internal/star.hpp
index de779dffc5fed4c610e6767b99836efab2628fcb..bc2360bf759b911ef7c27b74f9423fdbbb142a45 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/star.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/star.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_STAR_HPP
 #define TAO_PEGTL_INTERNAL_STAR_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/star_must.hpp b/packages/PEGTL/include/tao/pegtl/internal/star_must.hpp
index 2ac45e42bc2cef61d459ec5d9e8750decb393174..8dff5771bef509bce1fb6634aea0f75006b89f7e 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/star_must.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/star_must.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_STAR_MUST_HPP
 #define TAO_PEGTL_INTERNAL_STAR_MUST_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/state.hpp b/packages/PEGTL/include/tao/pegtl/internal/state.hpp
index e86965bf3bf3db50a484044d67741c246d82beeb..4ed8d22c859b10782101168cb60320d8da5047d0 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/state.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/state.hpp
@@ -1,11 +1,15 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_STATE_HPP
 #define TAO_PEGTL_INTERNAL_STATE_HPP
 
 #include "../config.hpp"
 
+#include <type_traits>
+
+#include "dependent_false.hpp"
 #include "enable_control.hpp"
 #include "seq.hpp"
 #include "success.hpp"
@@ -16,18 +20,18 @@
 
 namespace TAO_PEGTL_NAMESPACE::internal
 {
-   template< typename State, typename... Rules >
+   template< typename NewState, typename... Rules >
    struct state
-      : state< State, seq< Rules... > >
+      : state< NewState, seq< Rules... > >
    {};
 
-   template< typename State >
-   struct state< State >
+   template< typename NewState >
+   struct state< NewState >
       : success
    {};
 
-   template< typename State, typename Rule >
-   struct state< State, Rule >
+   template< typename NewState, typename Rule >
+   struct state< NewState, Rule >
    {
       using rule_t = state;
       using subs_t = type_list< Rule >;
@@ -42,17 +46,30 @@ namespace TAO_PEGTL_NAMESPACE::internal
                 typename... States >
       [[nodiscard]] static bool match( ParseInput& in, States&&... st )
       {
-         State s( static_cast< const ParseInput& >( in ), st... );
-         if( Control< Rule >::template match< A, M, Action, Control >( in, s ) ) {
-            s.success( static_cast< const ParseInput& >( in ), st... );
-            return true;
+         if constexpr( std::is_constructible_v< NewState, const ParseInput&, States... > ) {
+            NewState s( static_cast< const ParseInput& >( in ), st... );
+            if( Control< Rule >::template match< A, M, Action, Control >( in, s ) ) {
+               s.success( static_cast< const ParseInput& >( in ), st... );
+               return true;
+            }
+            return false;
+         }
+         else if constexpr( std::is_default_constructible_v< NewState > ) {
+            NewState s;
+            if( Control< Rule >::template match< A, M, Action, Control >( in, s ) ) {
+               s.success( static_cast< const ParseInput& >( in ), st... );
+               return true;
+            }
+            return false;
+         }
+         else {
+            static_assert( internal::dependent_false< NewState >, "unable to instantiate new state" );
          }
-         return false;
       }
    };
 
-   template< typename State, typename... Rules >
-   inline constexpr bool enable_control< state< State, Rules... > > = false;
+   template< typename NewState, typename... Rules >
+   inline constexpr bool enable_control< state< NewState, Rules... > > = false;
 
 }  // namespace TAO_PEGTL_NAMESPACE::internal
 
diff --git a/packages/PEGTL/include/tao/pegtl/internal/string.hpp b/packages/PEGTL/include/tao/pegtl/internal/string.hpp
index d659ab8b328534096af1e256ab0208285c967083..4ad5fcb548ef922fa21b4e3353d9d7eda83d53d1 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/string.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/string.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_STRING_HPP
 #define TAO_PEGTL_INTERNAL_STRING_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/success.hpp b/packages/PEGTL/include/tao/pegtl/internal/success.hpp
index 03ea08d7d4c394b10e250f7ee3cdd074e9b3a026..706122bd578b2e98ae425d563556e5bbc91addd3 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/success.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/success.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_SUCCESS_HPP
 #define TAO_PEGTL_INTERNAL_SUCCESS_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/try_catch_type.hpp b/packages/PEGTL/include/tao/pegtl/internal/try_catch_type.hpp
index 4e33287e50ad485bc15f9d54406f29f113a9cbfe..ebeb3de01e3ede936f55682d9984f4616a26e930 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/try_catch_type.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/try_catch_type.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_TRY_CATCH_TYPE_HPP
 #define TAO_PEGTL_INTERNAL_TRY_CATCH_TYPE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/internal/until.hpp b/packages/PEGTL/include/tao/pegtl/internal/until.hpp
index b5c5e2152c0a85aa8a94d0cdb97bcf8efc2da0af..b6efd3a18a963feae054cae9f05e363bb96145ba 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/until.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/until.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_INTERNAL_UNTIL_HPP
 #define TAO_PEGTL_INTERNAL_UNTIL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/istream_input.hpp b/packages/PEGTL/include/tao/pegtl/istream_input.hpp
index 893316e5c8e06dd1f19f85c7ffa63c8f34b73f22..f37c1bc9653bddfaffaa5656d8e33914b1072278 100644
--- a/packages/PEGTL/include/tao/pegtl/istream_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/istream_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_ISTREAM_INPUT_HPP
 #define TAO_PEGTL_ISTREAM_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/match.hpp b/packages/PEGTL/include/tao/pegtl/match.hpp
index 6ac9f6178a1e17f2f103b687af614b4c027eb87c..ad42582286d1a1e2364d302a60dec8a2aba9aa9a 100644
--- a/packages/PEGTL/include/tao/pegtl/match.hpp
+++ b/packages/PEGTL/include/tao/pegtl/match.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_MATCH_HPP
 #define TAO_PEGTL_MATCH_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/memory_input.hpp b/packages/PEGTL/include/tao/pegtl/memory_input.hpp
index 7cdc9f9f88aa5a2adbaf3158730217d3a2176334..c20887131eb2beff684f74217df55ea5c90f31b0 100644
--- a/packages/PEGTL/include/tao/pegtl/memory_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/memory_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_MEMORY_INPUT_HPP
 #define TAO_PEGTL_MEMORY_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/mmap_input.hpp b/packages/PEGTL/include/tao/pegtl/mmap_input.hpp
index 2362b6c765747a6ccbe7964417434f32fe2bf04f..759ea7035521ff20aac72e5a8d0068f9c25c26fb 100644
--- a/packages/PEGTL/include/tao/pegtl/mmap_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/mmap_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_MMAP_INPUT_HPP
 #define TAO_PEGTL_MMAP_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/must_if.hpp b/packages/PEGTL/include/tao/pegtl/must_if.hpp
index 0140a57a6693d93f5837f2c54d1e8dd05cfb3920..875e7e9c2a20e0a6c948dfcbeeba0f42e7ff4a4a 100644
--- a/packages/PEGTL/include/tao/pegtl/must_if.hpp
+++ b/packages/PEGTL/include/tao/pegtl/must_if.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_MUST_IF_HPP
 #define TAO_PEGTL_MUST_IF_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/normal.hpp b/packages/PEGTL/include/tao/pegtl/normal.hpp
index ac9d77e26585a8307298e8da256b69b008ee2cf8..bdbdd85eedf79a606db8a376c473e7a6ac9f47c5 100644
--- a/packages/PEGTL/include/tao/pegtl/normal.hpp
+++ b/packages/PEGTL/include/tao/pegtl/normal.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_NORMAL_HPP
 #define TAO_PEGTL_NORMAL_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/nothing.hpp b/packages/PEGTL/include/tao/pegtl/nothing.hpp
index 302b43555cf55adbc70a9fbdafad9b1451968e18..28b68f8449c09c81303f53d9a965aadcac05bf74 100644
--- a/packages/PEGTL/include/tao/pegtl/nothing.hpp
+++ b/packages/PEGTL/include/tao/pegtl/nothing.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_NOTHING_HPP
 #define TAO_PEGTL_NOTHING_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/parse.hpp b/packages/PEGTL/include/tao/pegtl/parse.hpp
index 3888fb3fc3ed619423ad4a3af5abaa2510fa9e3c..962f3051a2bdc94af2efc7157ba5048e64714db0 100644
--- a/packages/PEGTL/include/tao/pegtl/parse.hpp
+++ b/packages/PEGTL/include/tao/pegtl/parse.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_PARSE_HPP
 #define TAO_PEGTL_PARSE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/parse_error.hpp b/packages/PEGTL/include/tao/pegtl/parse_error.hpp
index df62ce6811129c929d2bd2edec924c08fa24fc8a..6a0ad019510caf8aa95132f1766d3806b11f99c7 100644
--- a/packages/PEGTL/include/tao/pegtl/parse_error.hpp
+++ b/packages/PEGTL/include/tao/pegtl/parse_error.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_PARSE_ERROR_HPP
 #define TAO_PEGTL_PARSE_ERROR_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/position.hpp b/packages/PEGTL/include/tao/pegtl/position.hpp
index 36081668155eed64771b27c4ec3da58a40d48ec3..78ed32c9f35db5355f4df69ea6f43beed440d1e8 100644
--- a/packages/PEGTL/include/tao/pegtl/position.hpp
+++ b/packages/PEGTL/include/tao/pegtl/position.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_POSITION_HPP
 #define TAO_PEGTL_POSITION_HPP
@@ -88,9 +89,9 @@ namespace TAO_PEGTL_NAMESPACE
 
    [[nodiscard]] inline std::string to_string( const position& p )
    {
-      std::ostringstream o;
-      o << p;
-      return o.str();
+      std::ostringstream oss;
+      oss << p;
+      return std::move( oss ).str();
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/include/tao/pegtl/read_input.hpp b/packages/PEGTL/include/tao/pegtl/read_input.hpp
index 019c9cd20b806f28772825a1bd9fa319b4c818a8..adbad4ca0484f8aa3669e92948f4faa3bff023f5 100644
--- a/packages/PEGTL/include/tao/pegtl/read_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/read_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_READ_INPUT_HPP
 #define TAO_PEGTL_READ_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/require_apply.hpp b/packages/PEGTL/include/tao/pegtl/require_apply.hpp
index 1200909ae8bcd3628f92536e7c3b7fc592b39376..7eaa5cc07bc2d85f5ed8d3037b03e8200d8110d1 100644
--- a/packages/PEGTL/include/tao/pegtl/require_apply.hpp
+++ b/packages/PEGTL/include/tao/pegtl/require_apply.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_REQUIRE_APPLY_HPP
 #define TAO_PEGTL_REQUIRE_APPLY_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/require_apply0.hpp b/packages/PEGTL/include/tao/pegtl/require_apply0.hpp
index 368f9b37d26a213ef23962d283e5d0aa845ef940..a20f0a21085a4db72278511ecc1b2df93c3dc936 100644
--- a/packages/PEGTL/include/tao/pegtl/require_apply0.hpp
+++ b/packages/PEGTL/include/tao/pegtl/require_apply0.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_REQUIRE_APPLY0_HPP
 #define TAO_PEGTL_REQUIRE_APPLY0_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/rewind_mode.hpp b/packages/PEGTL/include/tao/pegtl/rewind_mode.hpp
index 48e9dc9528d6f2ce2b671b1a359b2208672a4d7c..b97cbcfaf4e53593c7390b2170c22eb11226371e 100644
--- a/packages/PEGTL/include/tao/pegtl/rewind_mode.hpp
+++ b/packages/PEGTL/include/tao/pegtl/rewind_mode.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_REWIND_MODE_HPP
 #define TAO_PEGTL_REWIND_MODE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/rules.hpp b/packages/PEGTL/include/tao/pegtl/rules.hpp
index 675098833215d9b2e075d370d7487d9b10f158a8..59d73c742fae9131514ecd01aa421a71a3191ba5 100644
--- a/packages/PEGTL/include/tao/pegtl/rules.hpp
+++ b/packages/PEGTL/include/tao/pegtl/rules.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_RULES_HPP
 #define TAO_PEGTL_RULES_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/string_input.hpp b/packages/PEGTL/include/tao/pegtl/string_input.hpp
index 43349eb6f0e76480ff879cba04eb79eecda54152..f66ee24c8fb8ddd348efeb5393187852e6783274 100644
--- a/packages/PEGTL/include/tao/pegtl/string_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/string_input.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_STRING_INPUT_HPP
 #define TAO_PEGTL_STRING_INPUT_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/tracking_mode.hpp b/packages/PEGTL/include/tao/pegtl/tracking_mode.hpp
index 18e11f4d11668e96cdd4ae6a0b7a1dcc3eeb94e6..6e0f06b8fd5b74934c4a68d84502366eeaf639ad 100644
--- a/packages/PEGTL/include/tao/pegtl/tracking_mode.hpp
+++ b/packages/PEGTL/include/tao/pegtl/tracking_mode.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_TRACKING_MODE_HPP
 #define TAO_PEGTL_TRACKING_MODE_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/type_list.hpp b/packages/PEGTL/include/tao/pegtl/type_list.hpp
index 243edeb8c15a4229c25729a55505f16d3f6d83cd..f0704aabea50744da880609ee97aeb47eb8b41e2 100644
--- a/packages/PEGTL/include/tao/pegtl/type_list.hpp
+++ b/packages/PEGTL/include/tao/pegtl/type_list.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_TYPE_LIST_HPP
 #define TAO_PEGTL_TYPE_LIST_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/utf8.hpp b/packages/PEGTL/include/tao/pegtl/utf8.hpp
index f0c0a83a5e3accbef3c61fe1d4b9fe300bd48136..95b45ef49ccab20bb6a8b8bab23ad708104f4410 100644
--- a/packages/PEGTL/include/tao/pegtl/utf8.hpp
+++ b/packages/PEGTL/include/tao/pegtl/utf8.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_UTF8_HPP
 #define TAO_PEGTL_UTF8_HPP
diff --git a/packages/PEGTL/include/tao/pegtl/version.hpp b/packages/PEGTL/include/tao/pegtl/version.hpp
index 2ffc6b953b1869b690588655bf26281f84fb68d5..034954416633cf614672005f8047bd3b275a35d4 100644
--- a/packages/PEGTL/include/tao/pegtl/version.hpp
+++ b/packages/PEGTL/include/tao/pegtl/version.hpp
@@ -1,13 +1,14 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_VERSION_HPP
 #define TAO_PEGTL_VERSION_HPP
 
-#define TAO_PEGTL_VERSION "3.2.1"
+#define TAO_PEGTL_VERSION "3.3.0"
 
 #define TAO_PEGTL_VERSION_MAJOR 3
-#define TAO_PEGTL_VERSION_MINOR 2
-#define TAO_PEGTL_VERSION_PATCH 1
+#define TAO_PEGTL_VERSION_MINOR 3
+#define TAO_PEGTL_VERSION_PATCH 0
 
 #endif
diff --git a/packages/PEGTL/include/tao/pegtl/visit.hpp b/packages/PEGTL/include/tao/pegtl/visit.hpp
index dfce2f756650804ad3ba2adddb7186416e894fe4..81ca6ac75cb151d9694b72f15b8f0196f66f1f52 100644
--- a/packages/PEGTL/include/tao/pegtl/visit.hpp
+++ b/packages/PEGTL/include/tao/pegtl/visit.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_VISIT_HPP
 #define TAO_PEGTL_VISIT_HPP
diff --git a/packages/PEGTL/src/example/pegtl/CMakeLists.txt b/packages/PEGTL/src/example/pegtl/CMakeLists.txt
index 64bb7380dfb645e72c027fb5c7cc511f62ee03a3..236d365ddc71c1ce53e984963df5abd4c026765e 100644
--- a/packages/PEGTL/src/example/pegtl/CMakeLists.txt
+++ b/packages/PEGTL/src/example/pegtl/CMakeLists.txt
@@ -11,6 +11,7 @@ set(example_sources
   expression.cpp
   hello_world.cpp
   indent_aware.cpp
+  iri.cpp
   json_analyze.cpp
   json_ast.cpp
   json_build.cpp
@@ -22,12 +23,9 @@ set(example_sources
   json_trace.cpp
   lua53_analyze.cpp
   lua53_parse.cpp
-  lua53_print_debug.cpp
-  lua53_print_names.cpp
   modulus_match.cpp
   parse_tree.cpp
   parse_tree_user_state.cpp
-  peg2pegtl.cpp
   proto3.cpp
   recover.cpp
   s_expression.cpp
diff --git a/packages/PEGTL/src/example/pegtl/abnf2pegtl.cpp b/packages/PEGTL/src/example/pegtl/abnf2pegtl.cpp
index 1a9b0d6b98d58e5cdff1abe7ccbfc4351654f6cb..3fd3b976941bb5c87770f31c33dbb46f14f886a1 100644
--- a/packages/PEGTL/src/example/pegtl/abnf2pegtl.cpp
+++ b/packages/PEGTL/src/example/pegtl/abnf2pegtl.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <algorithm>
 #include <exception>
@@ -313,10 +314,16 @@ namespace TAO_PEGTL_NAMESPACE
       template<> inline constexpr auto error_message< abnf::grammar::defined_as > = "expected '=' or '=/'";
       template<> inline constexpr auto error_message< abnf::grammar::req_c_nl > = "unterminated rule";
       template<> inline constexpr auto error_message< abnf::grammar::rule > = "expected rule";
-
-      struct error { template< typename Rule > static constexpr auto message = error_message< Rule >; };
-      template< typename Rule > using control = must_if< error >::control< Rule >;
       // clang-format on
+
+      struct error
+      {
+         template< typename Rule >
+         static constexpr auto message = error_message< Rule >;
+      };
+
+      template< typename Rule >
+      using control = must_if< error >::control< Rule >;
 #else
       template< typename Rule >
       using control = normal< Rule >;
@@ -629,9 +636,9 @@ namespace TAO_PEGTL_NAMESPACE
                v <<= 1;
                v |= ( *p++ & 1 );
             } while( p != n->m_end.data );
-            std::ostringstream o;
-            o << v;
-            return o.str();
+            std::ostringstream oss;
+            oss << v;
+            return std::move( oss ).str();
          } );
 
          nrv.add< grammar::hex_val::type >( []( const node_ptr& n ) { return gen_val< grammar::hex_val::range >( n ); } );
@@ -727,9 +734,9 @@ namespace TAO_PEGTL_NAMESPACE
             if( min_val == max_val ) {
                return min_element;
             }
-            std::ostringstream os;
-            os << ( max_val - min_val );
-            const auto max_element = prefix + ( ( max_val - min_val == 1 ) ? "opt< " : ( "rep_opt< " + os.str() + ", " ) ) + content + " >";
+            std::ostringstream oss;
+            oss << ( max_val - min_val );
+            const auto max_element = prefix + ( ( max_val - min_val == 1 ) ? "opt< " : ( "rep_opt< " + std::move( oss ).str() + ", " ) ) + content + " >";
             return prefix + "seq< " + min_element + ", " + max_element + " >";
          } );
 
diff --git a/packages/PEGTL/src/example/pegtl/analyze.cpp b/packages/PEGTL/src/example/pegtl/analyze.cpp
index 94b4c9aa5546f5c2b5e55771a6a8fdff91bbcc2c..8f7aa0057d4f51ff363936f6dfb70db07993dfa8 100644
--- a/packages/PEGTL/src/example/pegtl/analyze.cpp
+++ b/packages/PEGTL/src/example/pegtl/analyze.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <tao/pegtl.hpp>
 
diff --git a/packages/PEGTL/src/example/pegtl/calculator.cpp b/packages/PEGTL/src/example/pegtl/calculator.cpp
index f69110c06f49a2c56ac8be81a6b05ca41e02dbea..ed9fb2b05512e06196ac23e136457b869c9fbb8d 100644
--- a/packages/PEGTL/src/example/pegtl/calculator.cpp
+++ b/packages/PEGTL/src/example/pegtl/calculator.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <cassert>
 #include <functional>
diff --git a/packages/PEGTL/src/example/pegtl/chomsky_hierarchy.cpp b/packages/PEGTL/src/example/pegtl/chomsky_hierarchy.cpp
index a135d3e50da36aa72754944aff4a628cf822c43e..cb671c0e8894908e1dd3cf8f53bc852a617a2623 100644
--- a/packages/PEGTL/src/example/pegtl/chomsky_hierarchy.cpp
+++ b/packages/PEGTL/src/example/pegtl/chomsky_hierarchy.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <cassert>
 #include <cstring>
diff --git a/packages/PEGTL/src/example/pegtl/csv1.cpp b/packages/PEGTL/src/example/pegtl/csv1.cpp
index 5bcdb2b336d9952c896eb8a0946e6b37020b5ad3..826363fde8d3b6dc213a591e3db7372c8f341444 100644
--- a/packages/PEGTL/src/example/pegtl/csv1.cpp
+++ b/packages/PEGTL/src/example/pegtl/csv1.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <cassert>
 #include <cstdint>
diff --git a/packages/PEGTL/src/example/pegtl/csv2.cpp b/packages/PEGTL/src/example/pegtl/csv2.cpp
index fdc363634edb3c8d91663b2ab2df50f469fce561..c4aafd56ca8860ec60e9857011b7b71f5f0a8b96 100644
--- a/packages/PEGTL/src/example/pegtl/csv2.cpp
+++ b/packages/PEGTL/src/example/pegtl/csv2.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <exception>
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/double.hpp b/packages/PEGTL/src/example/pegtl/double.hpp
index 8ceec7cd1c61727496bd6ebf378084a52d084552..c79f5cc755659155e64db2edfef6be602b476f2a 100644
--- a/packages/PEGTL/src/example/pegtl/double.hpp
+++ b/packages/PEGTL/src/example/pegtl/double.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_EXAMPLES_PEGTL_DOUBLE_HPP
 #define TAO_PEGTL_SRC_EXAMPLES_PEGTL_DOUBLE_HPP
diff --git a/packages/PEGTL/src/example/pegtl/dynamic_match.cpp b/packages/PEGTL/src/example/pegtl/dynamic_match.cpp
index 73ed8258df709cf2c6f1f6bdc1932ddb5c932d5f..bf1465066e88c21dbcb5703f7a84ca7cb971299e 100644
--- a/packages/PEGTL/src/example/pegtl/dynamic_match.cpp
+++ b/packages/PEGTL/src/example/pegtl/dynamic_match.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <cassert>
 #include <cstring>
diff --git a/packages/PEGTL/src/example/pegtl/expression.cpp b/packages/PEGTL/src/example/pegtl/expression.cpp
index 5d490f93f70fa150298f700ad48a416d97842ab6..5602ad3f09fb464f4fff6d963c1239d8496506b6 100644
--- a/packages/PEGTL/src/example/pegtl/expression.cpp
+++ b/packages/PEGTL/src/example/pegtl/expression.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
@@ -17,8 +18,6 @@ int main()
 #include <iomanip>
 #include <iostream>
 #include <stdexcept>
-#include <tuple>
-#include <variant>
 #include <vector>
 
 #include <tao/pegtl.hpp>
@@ -46,7 +45,7 @@ namespace TAO_PEGTL_NAMESPACE::expression
    {
       struct prefix_info
       {
-         prefix_info( const std::string_view n, const std::uint8_t pbp ) noexcept
+         prefix_info( const std::string_view n, const unsigned pbp ) noexcept
             : name( n ),
               prefix_binding_power( pbp )
          {
@@ -55,16 +54,16 @@ namespace TAO_PEGTL_NAMESPACE::expression
 
          std::string name;
 
-         std::uint8_t prefix_binding_power;
+         unsigned prefix_binding_power;
       };
 
       struct infix_postfix_info
       {
-         infix_postfix_info( const std::string_view n, const std::uint8_t lbp, const std::uint8_t rbp = 0 ) noexcept
+         infix_postfix_info( const std::string_view n, const unsigned lbp, const unsigned rbp = 0 ) noexcept
             : infix_postfix_info( n, std::string_view(), lbp, rbp )
          {}
 
-         infix_postfix_info( const std::string_view n, const std::string_view o, const std::uint8_t lbp, const std::uint8_t rbp = 0 ) noexcept
+         infix_postfix_info( const std::string_view n, const std::string_view o, const unsigned lbp, const unsigned rbp = 0 ) noexcept
             : name( n ),
               other( o ),
               left_binding_power( lbp ),
@@ -90,8 +89,8 @@ namespace TAO_PEGTL_NAMESPACE::expression
          std::string name;
          std::string other;  // Used for the ':' of the ternary operator etc.
 
-         std::uint8_t left_binding_power;
-         std::uint8_t right_binding_power;
+         unsigned left_binding_power;
+         unsigned right_binding_power;
       };
 
       template< typename ParseInput >
@@ -120,7 +119,7 @@ namespace TAO_PEGTL_NAMESPACE::expression
       }
 
       template< typename ParseInput, typename OperatorInfo >
-      [[nodiscard]] const OperatorInfo* match_infix_postfix( ParseInput& in, const std::size_t max_length, const std::vector< OperatorInfo >& ops, const std::uint8_t min_precedence )
+      [[nodiscard]] const OperatorInfo* match_infix_postfix( ParseInput& in, const std::size_t max_length, const std::vector< OperatorInfo >& ops, const unsigned min_precedence )
       {
          const std::size_t max = std::min( max_length, in.size( max_length ) );
          for( std::string op( in.current(), max ); !op.empty(); op.pop_back() ) {
@@ -143,68 +142,64 @@ namespace TAO_PEGTL_NAMESPACE::expression
 
       struct operator_maps
       {
-         // clang-format off
          operator_maps()
-            : prefix( sorted_operator_vector( {
-                  prefix_info( "!", 80 ),
-                  prefix_info( "+", 80 ),
-                  prefix_info( "-", 80 ),
-                  prefix_info( "~", 80 ),
-                  prefix_info( "*", 80 ),
-                  prefix_info( "&", 80 ),
-                  prefix_info( "++", 80 ),
-                  prefix_info( "--", 80 )
-               } ) ),
-              infix_postfix( sorted_operator_vector( {
-                  infix_postfix_info( "::", 99, 100 ),  // Special: Followed by identifier (or template-space-identifer, which we don't support yet).
-                  infix_postfix_info( ".*", 37, 38 ),
-                  infix_postfix_info( "->*", 37, 38 ),
-                  infix_postfix_info( "*", 35, 36 ),
-                  infix_postfix_info( "/", 35, 36 ),
-                  infix_postfix_info( "%", 35, 36 ),
-                  infix_postfix_info( "+", 33, 34 ),
-                  infix_postfix_info( "-", 33, 34 ),
-                  infix_postfix_info( "<<", 31, 32 ),
-                  infix_postfix_info( ">>", 31, 32 ),
-                  infix_postfix_info( "<=>", 29, 30 ),
-                  infix_postfix_info( "<", 27, 28 ),
-                  infix_postfix_info( "<=", 27, 28 ),
-                  infix_postfix_info( ">", 27, 28 ),
-                  infix_postfix_info( ">=", 27, 28 ),
-                  infix_postfix_info( "==", 25, 26 ),
-                  infix_postfix_info( "!=", 25, 26 ),
-                  infix_postfix_info( "&", 23, 24 ),
-                  infix_postfix_info( "^", 21, 22 ),
-                  infix_postfix_info( "|", 19, 20 ),
-                  infix_postfix_info( "&&", 17, 18 ),
-                  infix_postfix_info( "||", 15, 16 ),
-                  infix_postfix_info( "?", ":", 14, 13 ),  // Special: Ternary operator.
-                  infix_postfix_info( "=", 12, 11 ),
-                  infix_postfix_info( "+=", 12, 11 ),
-                  infix_postfix_info( "-=", 12, 11 ),
-                  infix_postfix_info( "*=", 12, 11 ),
-                  infix_postfix_info( "/=", 12, 11 ),
-                  infix_postfix_info( "%=", 12, 11 ),
-                  infix_postfix_info( "<<=", 12, 11 ),
-                  infix_postfix_info( ">>=", 12, 11 ),
-                  infix_postfix_info( "&=", 12, 11 ),
-                  infix_postfix_info( "^=", 12, 11 ),
-                  infix_postfix_info( "|=", 12, 11 ),
-                  // infix_postfix_info( ",", 9, 10 ),  // TODO: Enable, but forbid in function argument list.
-                  infix_postfix_info( "[", "]", 90 ),  // Special: Argument list.
-                  infix_postfix_info( "(", ")", 90 ),  // Special: Argument list.
-                  infix_postfix_info( ".", 90 ),  // Special: Followed by identifier.
-                  infix_postfix_info( "->", 90 ),  // Special: Followed by identifier.
-                  infix_postfix_info( "++", 90 ),
-                  infix_postfix_info( "--", 90 )
-               } ) ),
+            : prefix( sorted_operator_vector(
+               { prefix_info( "!", 80 ),
+                 prefix_info( "+", 80 ),
+                 prefix_info( "-", 80 ),
+                 prefix_info( "~", 80 ),
+                 prefix_info( "*", 80 ),
+                 prefix_info( "&", 80 ),
+                 prefix_info( "++", 80 ),
+                 prefix_info( "--", 80 ) } ) ),
+              infix_postfix( sorted_operator_vector(
+                 { infix_postfix_info( "::", 99, 100 ),  // Special: Followed by identifier (or template-space-identifer, which we don't support yet).
+                   infix_postfix_info( ".*", 37, 38 ),
+                   infix_postfix_info( "->*", 37, 38 ),
+                   infix_postfix_info( "*", 35, 36 ),
+                   infix_postfix_info( "/", 35, 36 ),
+                   infix_postfix_info( "%", 35, 36 ),
+                   infix_postfix_info( "+", 33, 34 ),
+                   infix_postfix_info( "-", 33, 34 ),
+                   infix_postfix_info( "<<", 31, 32 ),
+                   infix_postfix_info( ">>", 31, 32 ),
+                   infix_postfix_info( "<=>", 29, 30 ),
+                   infix_postfix_info( "<", 27, 28 ),
+                   infix_postfix_info( "<=", 27, 28 ),
+                   infix_postfix_info( ">", 27, 28 ),
+                   infix_postfix_info( ">=", 27, 28 ),
+                   infix_postfix_info( "==", 25, 26 ),
+                   infix_postfix_info( "!=", 25, 26 ),
+                   infix_postfix_info( "&", 23, 24 ),
+                   infix_postfix_info( "^", 21, 22 ),
+                   infix_postfix_info( "|", 19, 20 ),
+                   infix_postfix_info( "&&", 17, 18 ),
+                   infix_postfix_info( "||", 15, 16 ),
+                   infix_postfix_info( "?", ":", 14, 13 ),  // Special: Ternary operator.
+                   infix_postfix_info( "=", 12, 11 ),
+                   infix_postfix_info( "+=", 12, 11 ),
+                   infix_postfix_info( "-=", 12, 11 ),
+                   infix_postfix_info( "*=", 12, 11 ),
+                   infix_postfix_info( "/=", 12, 11 ),
+                   infix_postfix_info( "%=", 12, 11 ),
+                   infix_postfix_info( "<<=", 12, 11 ),
+                   infix_postfix_info( ">>=", 12, 11 ),
+                   infix_postfix_info( "&=", 12, 11 ),
+                   infix_postfix_info( "^=", 12, 11 ),
+                   infix_postfix_info( "|=", 12, 11 ),
+                   // infix_postfix_info( ",", 9, 10 ),  // TODO: Enable, but forbid in function argument list.
+                   infix_postfix_info( "[", "]", 90 ),  // Special: Argument list.
+                   infix_postfix_info( "(", ")", 90 ),  // Special: Argument list.
+                   infix_postfix_info( ".", 90 ),       // Special: Followed by identifier.
+                   infix_postfix_info( "->", 90 ),      // Special: Followed by identifier.
+                   infix_postfix_info( "++", 90 ),
+                   infix_postfix_info( "--", 90 ) } ) ),
               max_prefix_length( std::max_element( prefix.begin(), prefix.end(), []( const auto& l, const auto& r ) { return l.name.size() < r.name.size(); } )->name.size() ),
               max_infix_postfix_length( std::max_element( infix_postfix.begin(), infix_postfix.end(), []( const auto& l, const auto& r ) { return l.name.size() < r.name.size(); } )->name.size() )
          {
             // These are C++20 operators with the correct associativity and relative precedence, however some are still missing:
             // TODO: Compound literal (C99), _Alignof (C11), Functional cast, sizeof, co_await, co_yield, throw, new, new[], delete, delete[], C-style casts.
          }
-         // clang-format on
 
          const std::vector< prefix_info > prefix;
          const std::vector< infix_postfix_info > infix_postfix;
@@ -251,7 +246,7 @@ namespace TAO_PEGTL_NAMESPACE::expression
                    typename ParseInput,
                    typename Result,
                    typename Config >
-         [[nodiscard]] static bool match( ParseInput& in, Result& res, const Config& cfg, const std::uint8_t /*unused*/ )
+         [[nodiscard]] static bool match( ParseInput& in, Result& res, const Config& cfg, const unsigned /*unused*/ )
          {
             return Control< if_must< one< '(' >, star< ignored >, expression< Literal, Identifier >, star< ignored >, one< ')' > > >::template match< A, M, Action, Control >( in, res, cfg, 0 );
          }
@@ -269,7 +264,7 @@ namespace TAO_PEGTL_NAMESPACE::expression
                    typename ParseInput,
                    typename Result,
                    typename Config >
-         [[nodiscard]] static bool match( ParseInput& in, Result& res, const Config& cfg, const std::uint8_t /*unused*/ )
+         [[nodiscard]] static bool match( ParseInput& in, Result& res, const Config& cfg, const unsigned /*unused*/ )
          {
             if( const auto* info = match_prefix( in, cfg.max_prefix_length, cfg.prefix ) ) {
                (void)Control< must< star< ignored >, expression< Literal, Identifier > > >::template match< A, M, Action, Control >( in, res, cfg, info->prefix_binding_power );
@@ -294,7 +289,7 @@ namespace TAO_PEGTL_NAMESPACE::expression
                    typename ParseInput,
                    typename Result,
                    typename Config >
-         [[nodiscard]] static bool match( ParseInput& in, Result& res, const Config& cfg, const std::uint8_t min )
+         [[nodiscard]] static bool match( ParseInput& in, Result& res, const Config& cfg, const unsigned min )
          {
             if( const auto* info = match_infix_postfix( in, cfg.max_infix_postfix_length, cfg.infix_postfix, min ) ) {
                if( info->name == "?" ) {
@@ -314,11 +309,11 @@ namespace TAO_PEGTL_NAMESPACE::expression
                   return true;
                }
                if( ( info->name == "(" ) || ( info->name == "[" ) ) {
-                  const std::size_t size = res.term_stack.size();  // TODO: Determine number of arguments without relying on res!?
+                  const std::size_t size = res.string_stack.size();  // TODO: Determine number of arguments without relying on res!!!
                   (void)Control< must< star< ignored >, opt< list_must< expression< Literal, Identifier >, one< ',' >, ignored > > > >::template match< A, M, Action, Control >( in, res, cfg, 0 );
                   (void)Control< must< star< ignored >, string_view_rule > >::template match< A, M, Action, Control >( in, info->other );
                   if constexpr( A == apply_mode::action ) {
-                     res.call( info->name, info->other, res.term_stack.size() - size );
+                     res.call( info->name, info->other, res.string_stack.size() - size );
                   }
                   return true;
                }
@@ -393,28 +388,6 @@ namespace application
 {
    namespace pegtl = TAO_PEGTL_NAMESPACE;
 
-   struct term_t;
-
-   using tuple_t = std::tuple< std::string, std::vector< term_t > >;
-   using variant_t = std::variant< std::int64_t, std::string, tuple_t >;
-
-   struct term_t
-   {
-      explicit term_t( const std::int64_t l ) noexcept
-         : variant( l )
-      {}
-
-      explicit term_t( std::string&& s ) noexcept
-         : variant( std::move( s ) )
-      {}
-
-      explicit term_t( variant_t&& v ) noexcept
-         : variant( std::move( v ) )
-      {}
-
-      variant_t variant;
-   };
-
    [[nodiscard]] inline std::string operator+( const char* l, const std::string_view r )
    {
       return std::string( l ) + " '" + std::string( r ) + "'";
@@ -424,131 +397,68 @@ namespace application
    {
       void infix( const std::string_view op )
       {
-         assert( term_stack.size() >= 2 );
-         {
-            variant_t tmp = tuple_t( "infix" + op, { std::move( term_stack.at( term_stack.size() - 2 ) ), std::move( term_stack.at( term_stack.size() - 1 ) ) } );
-            term_stack.pop_back();
-            term_stack.back().variant = std::move( tmp );
-         }
          assert( string_stack.size() >= 2 );
-         {
-            std::string tmp = "( " + string_stack.at( string_stack.size() - 2 ) + " " + std::string( op ) + " " + string_stack.at( string_stack.size() - 1 ) + " )";
-            string_stack.pop_back();
-            string_stack.back() = std::move( tmp );
-         }
+
+         std::string tmp = "( " + string_stack.at( string_stack.size() - 2 ) + " " + std::string( op ) + " " + string_stack.at( string_stack.size() - 1 ) + " )";
+         string_stack.pop_back();
+         string_stack.back() = std::move( tmp );
       }
 
       void prefix( const std::string_view op )
       {
-         assert( term_stack.size() >= 1 );  // NOLINT(readability-container-size-empty)
-         {
-            variant_t tmp = tuple_t( "prefix" + op, { std::move( term_stack.at( term_stack.size() - 1 ) ) } );
-            term_stack.back().variant = std::move( tmp );
-         }
          assert( string_stack.size() >= 1 );  // NOLINT(readability-container-size-empty)
-         {
-            std::string tmp = std::string( op ) + "( " + string_stack.at( string_stack.size() - 1 ) + " )";
-            string_stack.back() = std::move( tmp );
-         }
+
+         std::string tmp = std::string( op ) + "( " + string_stack.at( string_stack.size() - 1 ) + " )";
+         string_stack.back() = std::move( tmp );
       }
 
       void postfix( const std::string_view op )
       {
-         assert( term_stack.size() >= 1 );  // NOLINT(readability-container-size-empty)
-         {
-            variant_t tmp = tuple_t( "postfix" + op, { std::move( term_stack.at( term_stack.size() - 1 ) ) } );
-            term_stack.back().variant = std::move( tmp );
-         }
          assert( string_stack.size() >= 1 );  // NOLINT(readability-container-size-empty)
-         {
-            std::string tmp = "( " + string_stack.at( string_stack.size() - 1 ) + " )" + std::string( op );
-            string_stack.back() = std::move( tmp );
-         }
+
+         std::string tmp = "( " + string_stack.at( string_stack.size() - 1 ) + " )" + std::string( op );
+         string_stack.back() = std::move( tmp );
       }
 
       void ternary( const std::string_view op, const std::string_view o2 )
       {
-         assert( term_stack.size() >= 2 );
-         {
-            variant_t tmp = tuple_t( "ternary", { std::move( term_stack.at( term_stack.size() - 3 ) ), std::move( term_stack.at( term_stack.size() - 2 ) ), std::move( term_stack.at( term_stack.size() - 1 ) ) } );
-            term_stack.pop_back();
-            term_stack.pop_back();
-            term_stack.back().variant = std::move( tmp );
-         }
          assert( string_stack.size() >= 2 );
-         {
-            std::string tmp = "( " + string_stack.at( string_stack.size() - 3 ) + " " + std::string( op ) + " " + string_stack.at( string_stack.size() - 2 ) + " " + std::string( o2 ) + " " + string_stack.at( string_stack.size() - 1 ) + " )";
-            string_stack.pop_back();
-            string_stack.pop_back();
-            string_stack.back() = std::move( tmp );
-         }
+
+         std::string tmp = "( " + string_stack.at( string_stack.size() - 3 ) + " " + std::string( op ) + " " + string_stack.at( string_stack.size() - 2 ) + " " + std::string( o2 ) + " " + string_stack.at( string_stack.size() - 1 ) + " )";
+         string_stack.pop_back();
+         string_stack.pop_back();
+         string_stack.back() = std::move( tmp );
       }
 
       void call( const std::string_view op, const std::string_view o2, const std::size_t args )
       {
-         assert( term_stack.size() > args );
-         {
-            variant_t tmp = tuple_t( "call '" + std::string( op ) + std::string( o2 ) + "'", std::vector< term_t >( term_stack.end() - args - 1, term_stack.end() ) );
-            for( std::size_t i = 0; i < args; ++i ) {
-               term_stack.pop_back();
-            }
-            term_stack.back().variant = ( std::move( tmp ) );
-         }
          assert( string_stack.size() > args );
-         {
-            std::string tmp = *( string_stack.end() - args - 1 ) + std::string( op ) + " ";
-            for( std::size_t i = 0; i < args; ++i ) {
-               if( i > 0 ) {
-                  tmp += ", ";
-               }
-               tmp += *( string_stack.end() - args + i );
+
+         std::string tmp = *( string_stack.end() - args - 1 ) + std::string( op ) + " ";
+         for( std::size_t i = 0; i < args; ++i ) {
+            if( i > 0 ) {
+               tmp += ", ";
             }
-            tmp += " " + std::string( o2 );
-            string_stack.resize( string_stack.size() - args );
-            string_stack.back() = std::move( tmp );
+            tmp += *( string_stack.end() - args + i );
          }
+         tmp += " " + std::string( o2 );
+         string_stack.resize( string_stack.size() - args );
+         string_stack.back() = std::move( tmp );
       }
 
       void number( const std::int64_t l )
       {
-         term_stack.emplace_back( l );
          string_stack.emplace_back( std::to_string( l ) );
       }
 
       void identifier( const std::string& id )
       {
-         term_stack.emplace_back( id );
          string_stack.emplace_back( id );
       }
 
-      std::vector< term_t > term_stack;
       std::vector< std::string > string_stack;
    };
 
-   inline std::ostream& operator<<( std::ostream& o, const term_t& t );
-
-   inline std::ostream& operator<<( std::ostream& o, const tuple_t& t )
-   {
-      o << "{ " << std::get< 0 >( t );
-      for( const auto& res : std::get< 1 >( t ) ) {
-         o << " " << res;
-      }
-      o << " }";
-      return o;
-   }
-
-   inline std::ostream& operator<<( std::ostream& o, const variant_t& v )
-   {
-      std::visit( [ & ]( const auto& t ) { o << t; }, v );
-      return o;
-   }
-
-   inline std::ostream& operator<<( std::ostream& o, const term_t& t )
-   {
-      o << t.variant;
-      return o;
-   }
-
    struct literal
       : pegtl::plus< pegtl::digit >
    {};
@@ -595,10 +505,8 @@ int main( int argc, char** argv )
          application::result res;
          TAO_PEGTL_NAMESPACE::parse< application::grammar, application::action >( in, res );
          std::cout << "Input: " << argv[ i ] << std::endl;
-         assert( res.term_stack.size() == 1 );
          assert( res.string_stack.size() == 1 );
          std::cout << "Result: " << res.string_stack.at( 0 ) << std::endl;
-         std::cout << "Result: " << res.term_stack.at( 0 ) << std::endl;
       }
       catch( const TAO_PEGTL_NAMESPACE::parse_error& e ) {
          const auto p = e.positions().front();
diff --git a/packages/PEGTL/src/example/pegtl/hello_world.cpp b/packages/PEGTL/src/example/pegtl/hello_world.cpp
index a15273daf6c28f9f1231aa85a84c62569b2ae01b..8ecbdb813a87e41c1410da79e786acb9e507a9fb 100644
--- a/packages/PEGTL/src/example/pegtl/hello_world.cpp
+++ b/packages/PEGTL/src/example/pegtl/hello_world.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 #include <string>
diff --git a/packages/PEGTL/src/example/pegtl/indent_aware.cpp b/packages/PEGTL/src/example/pegtl/indent_aware.cpp
index 0d6c65aacf3af1dd4f35b1c0676011526788dee4..1462f89af04646a740182e9554e0b5c965f8585e 100644
--- a/packages/PEGTL/src/example/pegtl/indent_aware.cpp
+++ b/packages/PEGTL/src/example/pegtl/indent_aware.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
@@ -42,7 +43,6 @@ def b():
 namespace example
 {
    // clang-format off
-
    struct eq : pegtl::one< '=' > {};
    struct co : pegtl::one< ':' > {};
    struct hs : pegtl::one< '#' > {};
@@ -77,7 +77,6 @@ namespace example
    struct line : pegtl::sor< nothing, something > {};
 
    struct grammar : pegtl::until< pegtl::eof, pegtl::must< line > > {};
-
    // clang-format on
 
    enum class type
diff --git a/packages/PEGTL/src/example/pegtl/iri.cpp b/packages/PEGTL/src/example/pegtl/iri.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aed3e981bf7630fd85f8ea330b1e5b373b6e317b
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/iri.cpp
@@ -0,0 +1,102 @@
+// Copyright (c) 2021 Kelvin Hammond
+// Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
+#if !defined( __cpp_exceptions )
+#include <iostream>
+int main()
+{
+   std::cerr << "Exception support required, example unavailable." << std::endl;
+   return 1;
+}
+#else
+
+#include <tao/pegtl.hpp>
+#include <tao/pegtl/contrib/iri.hpp>
+
+#include <iostream>
+
+namespace pegtl = TAO_PEGTL_NAMESPACE;
+
+struct IRI
+{
+   std::string scheme;
+   std::string authority;
+   std::string userinfo;
+   std::string host;
+   std::string port;
+   std::string path;
+   std::string query;
+   std::string fragment;
+
+   explicit IRI( const std::string& iri );
+};
+
+namespace iri
+{
+   template< std::string IRI::*Field >
+   struct bind
+   {
+      template< typename ActionInput >
+      static void apply( const ActionInput& in, IRI& iri )
+      {
+         iri.*Field = in.string();
+      }
+   };
+
+   // clang-format off
+   template< typename Rule > struct action {};
+
+   template<> struct action< pegtl::iri::scheme > : bind< &IRI::scheme > {};
+   template<> struct action< pegtl::iri::iauthority > : bind< &IRI::authority > {};
+   // userinfo: see below
+   template<> struct action< pegtl::iri::ihost > : bind< &IRI::host > {};
+   template<> struct action< pegtl::iri::port > : bind< &IRI::port > {};
+   template<> struct action< pegtl::iri::ipath_noscheme > : bind< &IRI::path > {};
+   template<> struct action< pegtl::iri::ipath_rootless > : bind< &IRI::path > {};
+   template<> struct action< pegtl::iri::ipath_absolute > : bind< &IRI::path > {};
+   template<> struct action< pegtl::iri::ipath_abempty > : bind< &IRI::path > {};
+   template<> struct action< pegtl::iri::iquery > : bind< &IRI::query > {};
+   template<> struct action< pegtl::iri::ifragment > : bind< &IRI::fragment > {};
+   // clang-format on
+
+   template<>
+   struct action< pegtl::iri::opt_iuserinfo >
+   {
+      template< typename ActionInput >
+      static void apply( const ActionInput& in, IRI& iri )
+      {
+         if( !in.empty() ) {
+            iri.userinfo = std::string( in.begin(), in.size() - 1 );
+         }
+      }
+   };
+
+}  // namespace iri
+
+IRI::IRI( const std::string& iri )
+{
+   using grammar = pegtl::must< pegtl::iri::IRI >;
+   pegtl::memory_input input( iri, "iri" );
+   pegtl::parse< grammar, iri::action >( input, *this );
+}
+
+int main( int argc, char** argv )
+{
+   for( int i = 1; i < argc; ++i ) {
+      std::cout << "Parsing " << argv[ i ] << std::endl;
+      const IRI iri( argv[ i ] );
+      std::cout << "IRI.scheme: " << iri.scheme << std::endl;
+      std::cout << "IRI.authority: " << iri.authority << std::endl;
+      std::cout << "IRI.userinfo: " << iri.userinfo << std::endl;
+      std::cout << "IRI.host: " << iri.host << std::endl;
+      std::cout << "IRI.port: " << iri.port << std::endl;
+      std::cout << "IRI.path: " << iri.path << std::endl;
+      std::cout << "IRI.query: " << iri.query << std::endl;
+      std::cout << "IRI.fragment: " << iri.fragment << std::endl;
+   }
+   return 0;
+}
+
+#endif
diff --git a/packages/PEGTL/src/example/pegtl/json_analyze.cpp b/packages/PEGTL/src/example/pegtl/json_analyze.cpp
index ed5c83a2be8961fa3751ed9be20257f9bfb3271e..9a23359a53f694f672c821f78eb24738a729762a 100644
--- a/packages/PEGTL/src/example/pegtl/json_analyze.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_analyze.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 
diff --git a/packages/PEGTL/src/example/pegtl/json_ast.cpp b/packages/PEGTL/src/example/pegtl/json_ast.cpp
index fecaab39a17d51b4d09e989f1d77f148aaade17c..e227ea874f418f034b9de2411cd4b12157ad181c 100644
--- a/packages/PEGTL/src/example/pegtl/json_ast.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_ast.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iomanip>
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/json_build.cpp b/packages/PEGTL/src/example/pegtl/json_build.cpp
index c5e607b7c46047df3ddf93ba8122f6be5e7d0e01..00d90a587455f721b7e5541ad9bd454fc6762cc2 100644
--- a/packages/PEGTL/src/example/pegtl/json_build.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_build.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <cassert>
 #include <iomanip>
diff --git a/packages/PEGTL/src/example/pegtl/json_classes.hpp b/packages/PEGTL/src/example/pegtl/json_classes.hpp
index 3e69515b0e35066c3c6b6f141a1a0c91e85a7f11..840804328240a2f5534642682740576bd1e9b97d 100644
--- a/packages/PEGTL/src/example/pegtl/json_classes.hpp
+++ b/packages/PEGTL/src/example/pegtl/json_classes.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_EXAMPLES_PEGTL_JSON_CLASSES_HPP
 #define TAO_PEGTL_SRC_EXAMPLES_PEGTL_JSON_CLASSES_HPP
diff --git a/packages/PEGTL/src/example/pegtl/json_count.cpp b/packages/PEGTL/src/example/pegtl/json_count.cpp
index 09bf155ef930b3bdc5b28f00798d49f1a7ab0292..f5de1e2ed3b342b84c26faa152ea9760ba2227b0 100644
--- a/packages/PEGTL/src/example/pegtl/json_count.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_count.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <cstddef>
 #include <iomanip>
diff --git a/packages/PEGTL/src/example/pegtl/json_coverage.cpp b/packages/PEGTL/src/example/pegtl/json_coverage.cpp
index 3748eb16003079d866c8b3c952e090564854ece3..99bf55a14ef4d383ec3692fb95e891e038e00b98 100644
--- a/packages/PEGTL/src/example/pegtl/json_coverage.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_coverage.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iomanip>
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/json_errors.hpp b/packages/PEGTL/src/example/pegtl/json_errors.hpp
index c03d21f75d14b5b668d4e377e6b14330fcc0d5a2..5dd28f0ac628f9f6fd7b0faaf46560bcaaa03026 100644
--- a/packages/PEGTL/src/example/pegtl/json_errors.hpp
+++ b/packages/PEGTL/src/example/pegtl/json_errors.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_EXAMPLES_PEGTL_JSON_ERRORS_HPP
 #define TAO_PEGTL_SRC_EXAMPLES_PEGTL_JSON_ERRORS_HPP
@@ -37,15 +38,17 @@ namespace example
    template<> inline constexpr auto error_message< pegtl::json::key::content > = "unterminated key";
 
    template<> inline constexpr auto error_message< pegtl::eof > = "unexpected character after JSON value";
+   // clang-format on
 
    // As must_if<> can not take error_message as a template parameter directly, we need to wrap it.
    struct error
    {
-      template< typename Rule > static constexpr auto message = error_message< Rule >;
+      template< typename Rule >
+      static constexpr auto message = error_message< Rule >;
    };
 
-   template< typename Rule > using control = pegtl::must_if< error >::control< Rule >;
-   // clang-format on
+   template< typename Rule >
+   using control = pegtl::must_if< error >::control< Rule >;
 
 #else
 
diff --git a/packages/PEGTL/src/example/pegtl/json_parse.cpp b/packages/PEGTL/src/example/pegtl/json_parse.cpp
index 240a8898c38744eff056e5cd885a4464e93450de..cdff8772a535a1a275e5bac85504998063e0e506 100644
--- a/packages/PEGTL/src/example/pegtl/json_parse.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_parse.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iomanip>
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/json_print_debug.cpp b/packages/PEGTL/src/example/pegtl/json_print_debug.cpp
index ce7b08862b9474aeff3b70be9ee8a392c4d4b577..8adadfdb0aad76e2b702a1fe9d8ca300c6c13e58 100644
--- a/packages/PEGTL/src/example/pegtl/json_print_debug.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_print_debug.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 
diff --git a/packages/PEGTL/src/example/pegtl/json_print_names.cpp b/packages/PEGTL/src/example/pegtl/json_print_names.cpp
index ffb4a929a559691437acad0d70d37513aa6acc7d..2537b7c06b18fb21e50266acc6fc72c00e913819 100644
--- a/packages/PEGTL/src/example/pegtl/json_print_names.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_print_names.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 
diff --git a/packages/PEGTL/src/example/pegtl/json_trace.cpp b/packages/PEGTL/src/example/pegtl/json_trace.cpp
index 0084722c54fdbb7b8fd603de2f137afb62c2947b..345118a29e0681f845d545883c1f3274808687e0 100644
--- a/packages/PEGTL/src/example/pegtl/json_trace.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_trace.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iomanip>
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/json_unescape.hpp b/packages/PEGTL/src/example/pegtl/json_unescape.hpp
index 79c6074c5668a3d8120f4877bad9f59fa3bfc86b..210087e205d9bd27fa2ee19e9a3cadb7f47ad17d 100644
--- a/packages/PEGTL/src/example/pegtl/json_unescape.hpp
+++ b/packages/PEGTL/src/example/pegtl/json_unescape.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_EXAMPLES_PEGTL_JSON_UNESCAPE_HPP
 #define TAO_PEGTL_SRC_EXAMPLES_PEGTL_JSON_UNESCAPE_HPP
diff --git a/packages/PEGTL/src/example/pegtl/lua53.hpp b/packages/PEGTL/src/example/pegtl/lua53.hpp
index 3bfc953702c4192849e610eeb9978820331ebe52..da57ea822bb17947e8725e32ceabd64ab5a84fa0 100644
--- a/packages/PEGTL/src/example/pegtl/lua53.hpp
+++ b/packages/PEGTL/src/example/pegtl/lua53.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_EXAMPLES_PEGTL_LUA53_HPP
 #define TAO_PEGTL_SRC_EXAMPLES_PEGTL_LUA53_HPP
diff --git a/packages/PEGTL/src/example/pegtl/lua53_analyze.cpp b/packages/PEGTL/src/example/pegtl/lua53_analyze.cpp
index 788ee84bac12b2d034f2c3b203e6eed409a07f8e..d785e2a98c2139ef6fddab75cb5b95beceb6e658 100644
--- a/packages/PEGTL/src/example/pegtl/lua53_analyze.cpp
+++ b/packages/PEGTL/src/example/pegtl/lua53_analyze.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/lua53_parse.cpp b/packages/PEGTL/src/example/pegtl/lua53_parse.cpp
index 72c1fddabbb34f5cd8beb78d9d4f69cd4108dde9..317e23c47b2454d33fb9d1817e61d51bd35b63a2 100644
--- a/packages/PEGTL/src/example/pegtl/lua53_parse.cpp
+++ b/packages/PEGTL/src/example/pegtl/lua53_parse.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/lua53_print_debug.cpp b/packages/PEGTL/src/example/pegtl/lua53_print_debug.cpp
deleted file mode 100644
index 6703c5a1c14dd05e46069698d264ead0f813a2e7..0000000000000000000000000000000000000000
--- a/packages/PEGTL/src/example/pegtl/lua53_print_debug.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#if !defined( __cpp_exceptions )
-#include <iostream>
-int main()
-{
-   std::cerr << "Exception support required, example unavailable." << std::endl;
-   return 1;
-}
-#else
-
-#include <iostream>
-
-#include <tao/pegtl/contrib/print.hpp>
-
-#include "lua53.hpp"
-
-int main()  // NOLINT(bugprone-exception-escape)
-{
-   tao::pegtl::print_debug< lua53::grammar >( std::cout );
-   return 0;
-}
-
-#endif
diff --git a/packages/PEGTL/src/example/pegtl/lua53_print_names.cpp b/packages/PEGTL/src/example/pegtl/lua53_print_names.cpp
deleted file mode 100644
index 12260a20c889aea9b06a35ce09801a3f31880020..0000000000000000000000000000000000000000
--- a/packages/PEGTL/src/example/pegtl/lua53_print_names.cpp
+++ /dev/null
@@ -1,25 +0,0 @@
-// Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#if !defined( __cpp_exceptions )
-#include <iostream>
-int main()
-{
-   std::cerr << "Exception support required, example unavailable." << std::endl;
-   return 1;
-}
-#else
-
-#include <iostream>
-
-#include <tao/pegtl/contrib/print.hpp>
-
-#include "lua53.hpp"
-
-int main()  // NOLINT(bugprone-exception-escape)
-{
-   tao::pegtl::print_names< lua53::grammar >( std::cout );
-   return 0;
-}
-
-#endif
diff --git a/packages/PEGTL/src/example/pegtl/modulus_match.cpp b/packages/PEGTL/src/example/pegtl/modulus_match.cpp
index f3a7d348d50d26ed23b793412e5472e684842e4e..fd866c5a53d167f67f043e833c41d98c38a089f7 100644
--- a/packages/PEGTL/src/example/pegtl/modulus_match.cpp
+++ b/packages/PEGTL/src/example/pegtl/modulus_match.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <tao/pegtl.hpp>
 
diff --git a/packages/PEGTL/src/example/pegtl/parse_tree.cpp b/packages/PEGTL/src/example/pegtl/parse_tree.cpp
index 1a37e3d05bd4dd28b930231e3a27d6a88d063ebd..c105e1621fc06a747f7c07ad5aacb930a14fd647 100644
--- a/packages/PEGTL/src/example/pegtl/parse_tree.cpp
+++ b/packages/PEGTL/src/example/pegtl/parse_tree.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <array>
 #include <iomanip>
diff --git a/packages/PEGTL/src/example/pegtl/parse_tree_user_state.cpp b/packages/PEGTL/src/example/pegtl/parse_tree_user_state.cpp
index 7268bca8a3b5372ee197895e33fe155346bc9004..6bb7a382a26fec64a91d16e6ac8451b66db3a978 100644
--- a/packages/PEGTL/src/example/pegtl/parse_tree_user_state.cpp
+++ b/packages/PEGTL/src/example/pegtl/parse_tree_user_state.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <type_traits>
 
diff --git a/packages/PEGTL/src/example/pegtl/peg.peg b/packages/PEGTL/src/example/pegtl/peg.peg
deleted file mode 100644
index 99a7c12a89466a1bb6d89a1fae70ed8a06dd418c..0000000000000000000000000000000000000000
--- a/packages/PEGTL/src/example/pegtl/peg.peg
+++ /dev/null
@@ -1,37 +0,0 @@
-# Parsing Expression Grammar (PEG) taken from
-# https://pdos.csail.mit.edu/~baford/packrat/popl04/peg-popl04.pdf
-
-# Hierarchical syntax
-Grammar <- Spacing Definition+ EndOfFile
-Definition <- Identifier LEFTARROW Expression
-Expression <- Sequence (SLASH Sequence)*
-Sequence <- Prefix*
-Prefix <- (AND / NOT)? Suffix
-Suffix <- Primary (QUESTION / STAR / PLUS)?
-Primary <- Identifier !LEFTARROW / OPEN Expression CLOSE / Literal / Class / DOT
-
-# Lexical syntax
-Identifier <- IdentStart IdentCont* Spacing
-IdentStart <- [a-zA-Z_]
-IdentCont <- IdentStart / [0-9]
-Literal <- ['] (!['] Char)* ['] Spacing / ["] (!["] Char)* ["] Spacing
-Class <- '[' (!']' Range)* ']' Spacing
-Range <- Char '-' Char / Char
-Char <- '\\' [nrt'"\[\]\\] / '\\' [0-2][0-7][0-7] / '\\' [0-7][0-7]? / !'\\' .
-
-LEFTARROW <- '<-' Spacing
-SLASH <- '/' Spacing
-AND <- '&' Spacing
-NOT <- '!' Spacing
-QUESTION <- '?' Spacing
-STAR <- '*' Spacing
-PLUS <- '+' Spacing
-OPEN <- '(' Spacing
-CLOSE <- ')' Spacing
-DOT <- '.' Spacing
-
-Spacing <- (Space / Comment)*
-Comment <- '#' (!EndOfLine .)* EndOfLine
-Space <- ' ' / '\t' / EndOfLine
-EndOfLine <- '\r\n' / '\n' / '\r'
-EndOfFile <- !.
\ No newline at end of file
diff --git a/packages/PEGTL/src/example/pegtl/peg2pegtl.cpp b/packages/PEGTL/src/example/pegtl/peg2pegtl.cpp
deleted file mode 100644
index c1ce89438ffe0137163f5e1b2aff5199d1d76306..0000000000000000000000000000000000000000
--- a/packages/PEGTL/src/example/pegtl/peg2pegtl.cpp
+++ /dev/null
@@ -1,539 +0,0 @@
-// Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Copyright (c) 2021 Daniel Deptford
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#include <algorithm>
-#include <iomanip>
-#include <iostream>
-#include <iterator>
-#include <sstream>
-#include <string>
-#include <unordered_map>
-#include <unordered_set>
-#include <utility>
-#include <vector>
-
-#include <cassert>
-#include <cctype>
-#include <cstdlib>
-
-#if defined( _MSC_VER )
-#include <string.h>
-#define TAO_PEGTL_STRCASECMP _stricmp
-#else
-#include <strings.h>
-#define TAO_PEGTL_STRCASECMP strcasecmp
-#endif
-
-#include <tao/pegtl.hpp>
-#include <tao/pegtl/contrib/parse_tree.hpp>
-#include <tao/pegtl/contrib/peg.hpp>
-
-namespace TAO_PEGTL_NAMESPACE
-{
-   namespace peg
-   {
-      using node_ptr = std::unique_ptr< parse_tree::node >;
-
-      namespace
-      {
-         std::string prefix = "tao::pegtl::";
-
-         std::unordered_set< std::string > keywords = {
-            "alignas",
-            "alignof",
-            "and",
-            "and_eq",
-            "asm",
-            "auto",
-            "bitand",
-            "bitor",
-            "bool",
-            "break",
-            "case",
-            "catch",
-            "char",
-            "char8_t",
-            "char16_t",
-            "char32_t",
-            "class",
-            "compl",
-            "concept",
-            "const",
-            "consteval",
-            "constexpr",
-            "constinit",
-            "const_cast",
-            "continue",
-            "co_await",
-            "co_return",
-            "co_yield",
-            "decltype",
-            "default",
-            "delete",
-            "do",
-            "double",
-            "dynamic_cast",
-            "else",
-            "enum",
-            "explicit",
-            "export",
-            "extern",
-            "false",
-            "float",
-            "for",
-            "friend",
-            "goto",
-            "if",
-            "inline",
-            "int",
-            "long",
-            "mutable",
-            "namespace",
-            "new",
-            "noexcept",
-            "not",
-            "not_eq",
-            "nullptr",
-            "operator",
-            "or",
-            "or_eq",
-            "private",
-            "protected",
-            "public",
-            "register",
-            "reinterpret_cast",
-            "return",
-            "requires",
-            "short",
-            "signed",
-            "sizeof",
-            "static",
-            "static_assert",
-            "static_cast",
-            "struct",
-            "switch",
-            "template",
-            "this",
-            "thread_local",
-            "throw",
-            "true",
-            "try",
-            "typedef",
-            "typeid",
-            "typename",
-            "union",
-            "unsigned",
-            "using",
-            "virtual",
-            "void",
-            "volatile",
-            "wchar_t",
-            "while",
-            "xor",
-            "xor_eq"
-         };
-
-         using identifiers_t = std::vector< std::string >;
-         identifiers_t identifiers_defined;
-         identifiers_t identifiers;
-
-         identifiers_t::reverse_iterator find_identifier( identifiers_t& r, const std::string& v, const identifiers_t::reverse_iterator& rbegin )
-         {
-            return std::find_if( rbegin, r.rend(), [ & ]( const identifiers_t::value_type& p ) { return TAO_PEGTL_STRCASECMP( p.c_str(), v.c_str() ) == 0; } );
-         }
-
-         identifiers_t::reverse_iterator find_identifier( identifiers_t& r, const std::string& v )
-         {
-            return find_identifier( r, v, r.rbegin() );
-         }
-
-         char char_node_to_char( const node_ptr& n )
-         {
-            const char ch = n->string_view().at( 0 );
-
-            if( ch == '\\' ) {
-               static const std::unordered_map< char, char > mappings( {
-                  { 'n', '\n' },
-                  { 'r', '\r' },
-                  { 't', '\t' },
-                  { '\'', '\'' },
-                  { '\"', '\"' },
-                  { '[', '[' },
-                  { ']', ']' },
-                  { '\\', '\\' },
-               } );
-
-               auto iter = mappings.find( n->string_view().at( 1 ) );
-               if( iter != std::end( mappings ) ) {
-                  return iter->second;
-               }
-
-               return static_cast< char >( std::stoi( n->string().substr( 1 ) ) );
-            }
-
-            return ch;
-         }
-
-         void append_char_node( std::string& s, const node_ptr& n )
-         {
-            if( !s.empty() ) {
-               s += ", ";
-            }
-            s += '\'';
-
-            const char c = char_node_to_char( n );
-
-            static const std::unordered_map< char, std::string > escapes( {
-               { '\b', "b" },
-               { '\f', "f" },
-               { '\n', "n" },
-               { '\r', "r" },
-               { '\t', "t" },
-               { '\v', "v" },
-               { '\\', "\\" },
-               { '\'', "\'" },
-            } );
-
-            auto iter = escapes.find( c );
-            if( iter != std::end( escapes ) ) {
-               s += '\\';
-               s += iter->second;
-            }
-            else {
-               s += c;
-            }
-
-            s += '\'';
-         }
-
-      }  // namespace
-
-#if defined( __cpp_exceptions )
-      // Using must_if<> we define a control class which is used for
-      // the parsing run instead of the default control class.
-      //
-      // This improves the errors reported to the user.
-      //
-      // The following turns local errors into global errors, i.e.
-      // if one of the rules for which a custom error message is
-      // defined fails, it throws a parse_error exception (aka global
-      // failure) instead of returning false (aka local failure).
-
-      // clang-format off
-      template< typename > inline constexpr const char* error_message = nullptr;
-
-      template<> inline constexpr auto error_message< peg::grammar::Char > = "unterminated character literal";
-      template<> inline constexpr auto error_message< peg::grammar::Expression > = "unterminated expression";
-      template<> inline constexpr auto error_message< peg::grammar::Grammar > = "unterminated grammar";
-      template<> inline constexpr auto error_message< peg::grammar::Range > = "unterminated range";
-
-      struct error { template< typename Rule > static constexpr auto message = error_message< Rule >; };
-      template< typename Rule > using control = must_if< error >::control< Rule >;
-      // clang-format on
-#else
-      template< typename Rule >
-      using control = normal< Rule >;
-#endif
-
-      // Since we are going to generate a parse tree, we define a
-      // selector that decides which rules will be included in our
-      // parse tree, which rules will be omitted from the parse tree,
-      // and which of the nodes will store the matched content.
-      // Additionally, some nodes will fold when they have exactly
-      // one child node. (see fold_one below)
-
-      template< typename Rule >
-      struct selector
-         : pegtl::parse_tree::selector<
-              Rule,
-              pegtl::parse_tree::store_content::on<
-                 grammar::Definition,
-                 grammar::Prefix,
-                 grammar::Suffix,
-                 grammar::Sequence,
-                 grammar::Expression,
-                 grammar::Class,
-                 grammar::Literal,
-                 grammar::Identifier,
-                 grammar::IdentStart,
-                 grammar::Range,
-                 grammar::Char,
-                 grammar::AND,
-                 grammar::NOT,
-                 grammar::QUESTION,
-                 grammar::STAR,
-                 grammar::PLUS,
-                 grammar::DOT >,
-              pegtl::parse_tree::fold_one::on< grammar::IdentCont > >
-      {
-         template< typename... States >
-         static void transform( node_ptr& n )
-         {
-            // As we use the PEG grammar taken directly from the original PEG
-            // paper, some nodes may have excess content from nodes not included
-            // in the parse tree (e.g. Comment, Space, etc).
-            if( !n->children.empty() ) {
-               n->m_end = n->children.back()->m_end;
-            }
-         }
-      };
-
-      std::string to_string( const node_ptr& n );
-      std::string to_string( const std::vector< node_ptr >& v );
-
-      namespace
-      {
-         std::string get_identifier( const node_ptr& n )
-         {
-            assert( n->is_type< grammar::Identifier >() );
-            std::string v = n->string();
-            std::replace( v.begin(), v.end(), '-', '_' );
-            return v;
-         }
-
-         std::string get_identifier( const node_ptr& n, const bool print_forward_declarations )
-         {
-            std::string v = get_identifier( n );
-            const auto it = find_identifier( identifiers, v );
-            if( it != identifiers.rend() ) {
-               return *it;
-            }
-            if( keywords.count( v ) != 0 || v.find( "__" ) != std::string::npos ) {
-#if defined( __cpp_exceptions )
-               throw parse_error( '\'' + n->string() + "' is a reserved identifier", n->begin() );
-#else
-               std::cerr << '\'' + n->string() + "' is a reserved identifier" << std::endl;
-               std::terminate();
-#endif
-            }
-            if( print_forward_declarations && find_identifier( identifiers_defined, v ) != identifiers_defined.rend() ) {
-               std::cout << "struct " << v << ";\n";
-            }
-            identifiers.push_back( v );
-            return v;
-         }
-
-         std::unordered_map< std::string, parse_tree::node* > previous_identifiers;
-
-      }  // namespace
-
-      template<>
-      struct selector< grammar::Definition >
-         : std::true_type
-      {
-         template< typename... States >
-         static void transform( node_ptr& n )
-         {
-            const auto idname = get_identifier( n->children.front() );
-            assert( n->children.back()->is_type< grammar::Expression >() );
-            if( !previous_identifiers.try_emplace( idname, n.get() ).second ) {
-#if defined( __cpp_exceptions )
-               throw parse_error( "identifier '" + idname + "' is already defined", n->begin() );
-#else
-               std::cerr << "identifier '" + idname + "' is already defined" << std::endl;
-               std::terminate();
-#endif
-            }
-         }
-      };
-
-      // Finally, the generated parse tree for each node is converted to
-      // a C++ source code string.
-
-      struct stringifier
-      {
-         using function_t = std::string ( * )( const node_ptr& n );
-         function_t default_ = nullptr;
-
-         std::unordered_map< std::string_view, function_t > map_;
-
-         template< typename T >
-         void add( const function_t& f )
-         {
-            map_.try_emplace( demangle< T >(), f );
-         }
-
-         std::string operator()( const node_ptr& n ) const
-         {
-            const auto it = map_.find( n->type );
-            if( it != map_.end() ) {
-               return it->second( n );
-            }
-
-            return default_( n );
-         }
-      };
-
-      stringifier make_stringifier()
-      {
-         stringifier nrv;
-         nrv.default_ = []( const node_ptr& n ) -> std::string {
-#if defined( __cpp_exceptions )
-            throw parse_error( "missing to_string() for " + std::string( n->type ), n->begin() );
-#else
-            std::cerr << "missing to_string() for " + std::string( n->type ) << std::endl;
-            std::terminate();
-#endif
-         };
-
-         nrv.add< grammar::Identifier >( []( const node_ptr& n ) { return get_identifier( n, true ); } );
-
-         nrv.add< grammar::Definition >( []( const node_ptr& n ) {
-            return "struct " + get_identifier( n->children.front(), false ) + " : " + to_string( n->children.back() ) + " {};";
-         } );
-
-         nrv.add< grammar::Char >( []( const node_ptr& n ) {
-            std::string s;
-            append_char_node( s, n );
-            return s;
-         } );
-
-         nrv.add< grammar::Sequence >( []( const node_ptr& n ) {
-            if( n->children.size() == 1 ) {
-               return to_string( n->children.front() );
-            }
-
-            return prefix + "seq< " + to_string( n->children ) + " >";
-         } );
-
-         nrv.add< grammar::Expression >( []( const node_ptr& n ) {
-            if( n->children.size() == 1 ) {
-               return to_string( n->children.front() );
-            }
-
-            return prefix + "sor< " + to_string( n->children ) + " >";
-         } );
-
-         nrv.add< grammar::Range >( []( const node_ptr& n ) {
-            if( n->children.size() == 1 ) {
-               return prefix + "one< " + to_string( n->children.front() ) + " >";
-            }
-
-            return prefix + "range< " + to_string( n->children.front() ) + ", " + to_string( n->children.back() ) + " >";
-         } );
-
-         nrv.add< grammar::Class >( []( const node_ptr& n ) {
-            if( n->children.size() == 1 ) {
-               return to_string( n->children.front() );
-            }
-
-            return prefix + "sor < " + to_string( n->children ) + " >";
-         } );
-
-         nrv.add< grammar::Literal >( []( const node_ptr& n ) {
-            if( n->children.size() == 1 ) {
-               return prefix + "one< " + to_string( n->children.front() ) + " >";
-            }
-
-            return prefix + "string< " + to_string( n->children ) + " >";
-         } );
-
-         nrv.add< grammar::Prefix >( []( const node_ptr& n ) {
-            auto sub = to_string( n->children.back() );
-
-            if( n->children.front()->is_type< grammar::AND >() ) {
-               return prefix + "at< " + sub + " >";
-            }
-
-            if( n->children.front()->is_type< grammar::NOT >() ) {
-               return prefix + "not_at< " + sub + " >";
-            }
-
-            assert( n->children.size() == 1 );
-            return sub;
-         } );
-
-         nrv.add< grammar::Suffix >( []( const node_ptr& n ) {
-            auto sub = to_string( n->children.front() );
-
-            if( n->children.back()->is_type< grammar::QUESTION >() ) {
-               return prefix + "opt< " + sub + " >";
-            }
-
-            if( n->children.back()->is_type< grammar::STAR >() ) {
-               return prefix + "star< " + sub + " >";
-            }
-
-            if( n->children.back()->is_type< grammar::PLUS >() ) {
-               return prefix + "plus< " + sub + " >";
-            }
-
-            assert( n->children.size() == 1 );
-            return sub;
-         } );
-
-         nrv.add< grammar::DOT >( []( const node_ptr& /*unused*/ ) {
-            return prefix + "any";
-         } );
-
-         return nrv;
-      }
-
-      std::string to_string( const node_ptr& n )
-      {
-         static stringifier s = make_stringifier();
-         return s( n );
-      }
-
-      std::string to_string( const std::vector< node_ptr >& v )
-      {
-         std::string result;
-         for( const auto& c : v ) {
-            if( !result.empty() ) {
-               result += ", ";
-            }
-            result += to_string( c );
-         }
-         return result;
-      }
-
-   }  // namespace peg
-
-}  // namespace TAO_PEGTL_NAMESPACE
-
-int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
-{
-   using namespace TAO_PEGTL_NAMESPACE;
-
-   if( argc != 2 ) {
-      std::cerr << "Usage: " << argv[ 0 ] << " SOURCE\n";
-      return 1;
-   }
-
-   file_input in( argv[ 1 ] );
-#if defined( __cpp_exceptions )
-   try {
-      const auto root = parse_tree::parse< peg::grammar::Grammar, peg::selector, nothing, peg::control >( in );
-
-      for( const auto& definition : root->children ) {
-         peg::identifiers_defined.push_back( peg::get_identifier( definition->children.front() ) );
-      }
-
-      for( const auto& rule : root->children ) {
-         std::cout << peg::to_string( rule ) << '\n';
-      }
-   }
-   catch( const parse_error& e ) {
-      const auto p = e.positions().front();
-      std::cerr << e.what() << '\n'
-                << in.line_at( p ) << '\n'
-                << std::setw( p.column ) << '^' << '\n';
-   }
-#else
-   if( const auto root = parse_tree::parse< peg::grammar::Grammar, peg::selector, nothing, peg::control >( in ) ) {
-      for( const auto& definition : root->children ) {
-         peg::identifiers_defined.push_back( peg::get_identifier( definition->children.front() ) );
-      }
-
-      for( const auto& rule : root->children ) {
-         std::cout << peg::to_string( rule ) << '\n';
-      }
-   }
-   else {
-      std::cerr << "error occurred" << std::endl;
-      return 1;
-   }
-#endif
-   return 0;
-}
diff --git a/packages/PEGTL/src/example/pegtl/proto3.cpp b/packages/PEGTL/src/example/pegtl/proto3.cpp
index 4283a8b275007a78fb3fe221cd0abc38561df5ce..cb7303787ccc7be48c9e5cc4887b9f511264d974 100644
--- a/packages/PEGTL/src/example/pegtl/proto3.cpp
+++ b/packages/PEGTL/src/example/pegtl/proto3.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
@@ -14,124 +15,7 @@ int main()
 
 #include <tao/pegtl.hpp>
 #include <tao/pegtl/contrib/analyze.hpp>
-
-namespace TAO_PEGTL_NAMESPACE::proto3
-{
-   // clang-format off
-
-   struct comment : seq< two< '/' >, until< eolf > > {};
-   struct sp : sor< space, comment > {};
-   struct sps : star< sp > {};
-
-   struct comma : one< ',' > {};
-   struct dot : one< '.' > {};
-   struct equ : one< '=' > {};
-   struct semi : one< ';' > {};
-
-   struct option;
-   struct message;
-
-   struct odigit : range< '0', '7' > {};
-
-   struct ident_first : ranges< 'a', 'z', 'A', 'Z' > {};  // NOTE: Yes, no '_'.
-   struct ident_other : ranges< 'a', 'z', 'A', 'Z', '0', '9', '_' > {};
-   struct ident : seq< ident_first, star< ident_other > > {};
-   struct full_ident : list_must< ident, dot > {};
-
-   struct oct_lit : seq< one< '0' >, star< odigit > > {};
-   struct hex_lit : seq< one< '0' >, one< 'x', 'X' >, plus< xdigit > > {};
-   struct dec_lit : seq< range< '1', '9' >, star< digit > > {};
-   struct int_lit : sor< dec_lit, hex_lit, oct_lit > {};
-
-   struct hex_escape : if_must< one< 'x', 'X' >, xdigit, xdigit > {};
-   struct oct_escape : if_must< odigit, odigit, odigit > {};
-   struct char_escape : one< 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '\'', '"' > {};
-   struct escape : if_must< one< '\\' >, hex_escape, oct_escape, char_escape > {};
-   struct char_value : sor< escape, not_one< '\n', '\0' > > {};  // NOTE: No need to exclude '\' from not_one<>, see escape rule.
-   template< char Q >
-   struct str_impl : if_must< one< Q >, until< one< Q >, char_value > > {};
-   struct str_lit : sor< str_impl< '\'' >, str_impl< '"' > > {};
-
-   struct bool_lit : seq< sor< string< 't', 'r', 'u', 'e' >, string< 'f', 'a', 'l', 's', 'e' > >, not_at< ident_other > > {};
-
-   struct sign : one< '+', '-' > {};
-   struct constant : sor< bool_lit, full_ident, seq< opt< sign >, int_lit >, str_lit > {};  // TODO: Needs sps after sign?
-
-   struct option_name : seq< sor< ident, if_must< one< '(' >, full_ident, one< ')' > > >, star_must< dot, ident > > {};
-   struct option : if_must< string< 'o', 'p', 't', 'i', 'o', 'n' >, sps, option_name, sps, equ, sps, constant, sps, semi, sps > {};
-
-   struct bool_type : string< 'b', 'o', 'o', 'l' > {};
-   struct bytes_type : string< 'b', 'y', 't', 'e', 's' > {};
-   struct double_type : string< 'd', 'o', 'u', 'b', 'l', 'e' > {};
-   struct float_type : string< 'f', 'l', 'o', 'a', 't' > {};
-   struct string_type : string< 's', 't', 'r', 'i', 'n', 'g' > {};
-
-   struct int32_type : string< 'i', 'n', 't', '3', '2' > {};
-   struct int64_type : string< 'i', 'n', 't', '6', '4' > {};
-   struct sint32_type : string< 's', 'i', 'n', 't', '3', '2' > {};
-   struct sint64_type : string< 's', 'i', 'n', 't', '6', '4' > {};
-   struct uint32_type : string< 'u', 'i', 'n', 't', '3', '2' > {};
-   struct uint64_type : string< 'u', 'i', 'n', 't', '6', '4' > {};
-   struct fixed32_type : string< 'f', 'i', 'x', 'e', 'd', '3', '2' > {};
-   struct fixed64_type : string< 'f', 'i', 'x', 'e', 'd', '6', '4' > {};
-   struct sfixed32_type : string< 's', 'f', 'i', 'x', 'e', 'd', '3', '2' > {};
-   struct sfixed64_type : string< 's', 'f', 'i', 'x', 'e', 'd', '6', '4' > {};
-
-   struct builtin_type : seq< sor< bool_type, bytes_type, double_type, float_type, string_type, int32_type, int64_type, sint32_type, sint64_type, uint32_type, uint64_type, fixed32_type, fixed64_type, sfixed32_type, sfixed64_type >, not_at< ident_other > > {};
-
-   struct defined_type : seq< opt< dot >, full_ident > {};  // NOTE: This replaces both message_type and enum_type -- they have the same syntax.
-
-   struct type : sor< builtin_type, defined_type > {};
-
-   struct field_option : if_must< option_name, sps, equ, sps, constant > {};
-   struct field_options : if_must< one< '[' >, sps, list< field_option, comma, sp >, sps, one< ']' > > {};
-   struct field_name : ident {};
-   struct field_number : int_lit {};
-   struct field : seq< opt< string< 'r', 'e', 'p', 'e', 'a', 't', 'e', 'd' >, sps >, type, sps, field_name, sps, equ, sps, field_number, sps, opt< field_options, sps >, semi > {};
-
-   struct oneof_name : ident {};
-   struct oneof_field : if_must< type, sps, field_name, sps, equ, sps, field_number, sps, opt< field_options, sps >, semi > {};
-   struct oneof_body : sor< oneof_field, semi > {};
-   struct oneof : if_must< string< 'o', 'n', 'e', 'o', 'f' >, sps, oneof_name, sps, one< '{' >, sps, until< one< '}' >, oneof_body, sps >, sps > {};
-
-   struct key_type : seq< sor< bool_type, string_type, int32_type, int64_type, sint32_type, sint64_type, uint32_type, uint64_type, fixed32_type, fixed64_type, sfixed32_type, sfixed64_type >, not_at< ident_other > > {};
-   struct map_name : ident {};
-   struct map_field : if_must< string< 'm', 'a', 'p' >, sps, one< '<' >, sps, key_type, sps, comma, sps, type, sps, one< '>' >, sps, map_name, sps, equ, sps, field_number, sps, opt< field_options, sps >, semi > {};
-
-   struct range : if_must< int_lit, sps, string< 't', 'o' >, sps, sor< int_lit, string< 'm', 'a', 'x' > > > {};
-   struct ranges : list_must< range, comma, sp > {};
-   struct field_names : list_must< field_name, comma, sp > {};
-   struct reserved : if_must< string< 'r', 'e', 's', 'e', 'r', 'v', 'e', 'd' >, sps, sor< ranges, field_names >, sps, semi > {};
-
-   struct enum_name : ident {};
-   struct enum_value_option : seq< option_name, sps, equ, sps, constant > {};
-   struct enum_field : seq< ident, sps, equ, sps, int_lit, sps, opt_must< one< '[' >, sps, list_must< enum_value_option, comma, sp >, sps, one< ']' >, sps >, semi > {};
-   struct enum_body : if_must< one< '{' >, sps, star< sor< option, enum_field, semi >, sps >, one< '}' > > {};
-   struct enum_def : if_must< string< 'e', 'n', 'u', 'm' >, sps, enum_name, sps, enum_body > {};
-
-   struct message_thing : sor< field, enum_def, message, option, oneof, map_field, reserved, semi > {};
-   struct message : if_must< string< 'm', 'e', 's', 's', 'a', 'g', 'e' >, sps, ident, sps, one< '{' >, sps, star< message_thing, sps >, one< '}' >, sps > {};
-
-   struct package : if_must< string< 'p', 'a', 'c', 'k', 'a', 'g', 'e' >, sps, full_ident, sps, semi, sps > {};
-
-   struct import_option : opt< sor< string< 'w', 'e', 'a', 'k' >, string< 'p', 'u', 'b', 'l', 'i', 'c' > > > {};
-   struct import : if_must< string< 'i', 'm', 'p', 'o', 'r', 't' >, sps, import_option, sps, str_lit, sps, semi, sps > {};
-
-   struct rpc_name : ident {};
-   struct rpc_type : if_must< one< '(' >, sps, opt< string< 's', 't', 'r', 'e', 'a', 'm' >, sps >, defined_type, sps, one< ')' > > {};
-   struct rpc_options : if_must< one< '{' >, sps, star< sor< option, semi >, sps >, one< '}' > > {};
-   struct rpc : if_must< string< 'r', 'p', 'c' >, sps, rpc_name, sps, rpc_type, sps, string< 'r', 'e', 't', 'u', 'r', 'n', 's' >, sps, rpc_type, sor< semi, rpc_options > > {};
-   struct service_name : ident {};
-   struct service : if_must< string< 's', 'e', 'r', 'v', 'i', 'c', 'e' >, sps, service_name, sps, one< '{' >, sps, list_must< sor< option, rpc, semi >, comma, sp >, sps, one< '}' > > {};
-
-   struct body : sor< import, package, option, message, enum_def, service, semi > {};
-
-   struct head : if_must< string< 's', 'y', 'n', 't', 'a', 'x' >, sps, equ, sps, string< '"', 'p', 'r', 'o', 't', 'o', '3', '"' >, sps, semi > {};
-   struct proto : must< sps, head, sps, star< body, sps >, eof > {};
-
-   // clang-format on
-
-}  // namespace TAO_PEGTL_NAMESPACE::proto3
+#include <tao/pegtl/contrib/proto3.hpp>
 
 int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
 {
diff --git a/packages/PEGTL/src/example/pegtl/recover.cpp b/packages/PEGTL/src/example/pegtl/recover.cpp
index 15baabd5e543bde94ad412b5ecdd1f201e1649e4..9f6e2076799d5b8e34c58eba022f10340c3f72a9 100644
--- a/packages/PEGTL/src/example/pegtl/recover.cpp
+++ b/packages/PEGTL/src/example/pegtl/recover.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 // This is a small experiment with a grammar that can recover from errors.
 //
diff --git a/packages/PEGTL/src/example/pegtl/s_expression.cpp b/packages/PEGTL/src/example/pegtl/s_expression.cpp
index 28dcace3a280998415d7b1aa2fe713ed244dac61..d0ab218c5928e8c326b3bdf81a22c4108aa84690 100644
--- a/packages/PEGTL/src/example/pegtl/s_expression.cpp
+++ b/packages/PEGTL/src/example/pegtl/s_expression.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/sum.cpp b/packages/PEGTL/src/example/pegtl/sum.cpp
index 6a17863cacafb27d70f62bf7e804c77037002326..9867e3ab84475b9a2c06fbd52ea791d46e36beff 100644
--- a/packages/PEGTL/src/example/pegtl/sum.cpp
+++ b/packages/PEGTL/src/example/pegtl/sum.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <cstdlib>
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/symbol_table.cpp b/packages/PEGTL/src/example/pegtl/symbol_table.cpp
index 673174c9b88e8033b8c7db4d118e6161ca932d7a..5dba51e3b91b1ebab816d25d759854b3e1286a90 100644
--- a/packages/PEGTL/src/example/pegtl/symbol_table.cpp
+++ b/packages/PEGTL/src/example/pegtl/symbol_table.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/token_input.cpp b/packages/PEGTL/src/example/pegtl/token_input.cpp
index 7d2795a3fb9bf453bbb3aa222ab2185d2a2b9517..df35d4aafdbc061753dd69ca6d75a79b766a6ee3 100644
--- a/packages/PEGTL/src/example/pegtl/token_input.cpp
+++ b/packages/PEGTL/src/example/pegtl/token_input.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 #include <string>
diff --git a/packages/PEGTL/src/example/pegtl/unescape.cpp b/packages/PEGTL/src/example/pegtl/unescape.cpp
index f445435b112989cf8215bfa7e0dbae0b2983e012..10f2a1f4071517108dad344cef0a938ec384264b 100644
--- a/packages/PEGTL/src/example/pegtl/unescape.cpp
+++ b/packages/PEGTL/src/example/pegtl/unescape.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 
diff --git a/packages/PEGTL/src/example/pegtl/uri.cpp b/packages/PEGTL/src/example/pegtl/uri.cpp
index 413caf99e6349678ec116735972cd4b8f3d8453e..a964959c41d1b7e9840eceb1b2f9c2ebc737f8da 100644
--- a/packages/PEGTL/src/example/pegtl/uri.cpp
+++ b/packages/PEGTL/src/example/pegtl/uri.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/uri_print_debug.cpp b/packages/PEGTL/src/example/pegtl/uri_print_debug.cpp
index 10e42f2433cbfc6c9afbca6ed5ebd55330b55401..f535e3a11b5c070e95ad64a3b9511b65a5b989c6 100644
--- a/packages/PEGTL/src/example/pegtl/uri_print_debug.cpp
+++ b/packages/PEGTL/src/example/pegtl/uri_print_debug.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/uri_print_names.cpp b/packages/PEGTL/src/example/pegtl/uri_print_names.cpp
index 708c3e71113b6844da410a7d7b46926c61d58b96..95361921756a6d13d4c4a0b73ba6f533e6bf1097 100644
--- a/packages/PEGTL/src/example/pegtl/uri_print_names.cpp
+++ b/packages/PEGTL/src/example/pegtl/uri_print_names.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/example/pegtl/uri_trace.cpp b/packages/PEGTL/src/example/pegtl/uri_trace.cpp
index e1392f85a54f967ffa563b89e82f2530c97d9214..d041d3343a468a1c651fbd6d09edc3e354b6f20a 100644
--- a/packages/PEGTL/src/example/pegtl/uri_trace.cpp
+++ b/packages/PEGTL/src/example/pegtl/uri_trace.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/CMakeLists.txt b/packages/PEGTL/src/test/pegtl/CMakeLists.txt
index 6bfacb4a72caec4e1dd08bdaade1d934e3bac9e0..eae7a9546c6fa5b0cb113cb999788a6cbbd828cc 100644
--- a/packages/PEGTL/src/test/pegtl/CMakeLists.txt
+++ b/packages/PEGTL/src/test/pegtl/CMakeLists.txt
@@ -34,6 +34,7 @@ set(test_sources
   contrib_if_then.cpp
   contrib_instantiate.cpp
   contrib_integer.cpp
+  contrib_iri.cpp
   contrib_json.cpp
   contrib_parse_tree.cpp
   contrib_parse_tree_to_dot.cpp
@@ -45,6 +46,7 @@ set(test_sources
   contrib_remove_last_states.cpp
   contrib_rep_one_min_max.cpp
   contrib_rep_string.cpp
+  contrib_separated_seq.cpp
   contrib_state_control.cpp
   contrib_to_string.cpp
   contrib_trace1.cpp
diff --git a/packages/PEGTL/src/test/pegtl/action_enable.cpp b/packages/PEGTL/src/test/pegtl/action_enable.cpp
index eaead46a3d235a9c6ac867392e5d6b72435680b0..3f7a5cb4228da53c3ec04d1b4938925631b38c22 100644
--- a/packages/PEGTL/src/test/pegtl/action_enable.cpp
+++ b/packages/PEGTL/src/test/pegtl/action_enable.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/action_match.cpp b/packages/PEGTL/src/test/pegtl/action_match.cpp
index f8f448c44fb1cce6fa4939550b4cd888e838d291..af6f5709d0902103073a114981bc0cb065fe641b 100644
--- a/packages/PEGTL/src/test/pegtl/action_match.cpp
+++ b/packages/PEGTL/src/test/pegtl/action_match.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/actions_one.cpp b/packages/PEGTL/src/test/pegtl/actions_one.cpp
index 9e8998c3050b023150ca1351e7db223cb5307319..4b0f4b6fda87e735883ec3e2f5a27c586f7dbbf6 100644
--- a/packages/PEGTL/src/test/pegtl/actions_one.cpp
+++ b/packages/PEGTL/src/test/pegtl/actions_one.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/actions_three.cpp b/packages/PEGTL/src/test/pegtl/actions_three.cpp
index 8f9fa9b351026eeddd2137253ca72242a013dff7..987f764578a77f2c8d9afdcd1983b968d927b318 100644
--- a/packages/PEGTL/src/test/pegtl/actions_three.cpp
+++ b/packages/PEGTL/src/test/pegtl/actions_three.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/actions_two.cpp b/packages/PEGTL/src/test/pegtl/actions_two.cpp
index 40f736757f87ad9ca519800a5a2b4c91a6e4e83e..77687875c6755d5f8558a3fc4f6445ac69ecf2b7 100644
--- a/packages/PEGTL/src/test/pegtl/actions_two.cpp
+++ b/packages/PEGTL/src/test/pegtl/actions_two.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/argv_input.cpp b/packages/PEGTL/src/test/pegtl/argv_input.cpp
index 933e29c06d71351a1401bc8c3388222749b2e5db..0f3c283d20b99ae08f1e94a265a4f7c0ffd440c4 100644
--- a/packages/PEGTL/src/test/pegtl/argv_input.cpp
+++ b/packages/PEGTL/src/test/pegtl/argv_input.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <cstring>
 
diff --git a/packages/PEGTL/src/test/pegtl/ascii_classes.cpp b/packages/PEGTL/src/test/pegtl/ascii_classes.cpp
index 2989634c00709cdcba9c79f14357dd046436e1ab..f9e255c33f11bbe7e47e6f879a8234271faf061d 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_classes.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_classes.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_eol.cpp b/packages/PEGTL/src/test/pegtl/ascii_eol.cpp
index 851eba98f5009ac9ec68bd4120ea85558729f98d..0169eda66cd94f2794254e7990b6dd7e4c93ced5 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_eol.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_eol.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_eolf.cpp b/packages/PEGTL/src/test/pegtl/ascii_eolf.cpp
index c0016df14de850706cf4cbe9bc9c2e03fe0c4fc8..c6033080c516ba6b6eeebd9755511834f9067c6d 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_eolf.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_eolf.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_forty_two.cpp b/packages/PEGTL/src/test/pegtl/ascii_forty_two.cpp
index efcafff31a8e8f646a76f4651ddd5abad1b4822b..bf3edcbdc123c1b33e39f655f05cd15ec51f8d22 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_forty_two.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_forty_two.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_identifier.cpp b/packages/PEGTL/src/test/pegtl/ascii_identifier.cpp
index 2e97e5851660647a367a4e2cfdcea2661d791ecd..8c1a14b852f6d3ea69a194ff625f7eb6b80be8e6 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_identifier.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_identifier.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_istring.cpp b/packages/PEGTL/src/test/pegtl/ascii_istring.cpp
index a5a74e8b84e752222b5f373547ce64eaf63a9926..3f1b1ff09aa1aa19dffc33e55dc256d80baaeaa7 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_istring.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_istring.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_keyword.cpp b/packages/PEGTL/src/test/pegtl/ascii_keyword.cpp
index d4b6dd128622d17847510e9064d025c3865a53ad..5d384c5f120f3eb644533944031d14875058bef6 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_keyword.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_keyword.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_shebang.cpp b/packages/PEGTL/src/test/pegtl/ascii_shebang.cpp
index 3f030ee5e8f98bbe904a5e27789eb62df12b7153..71fc843daa3365e3c2eda3f4c7c7b7e273a87f00 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_shebang.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_shebang.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_string.cpp b/packages/PEGTL/src/test/pegtl/ascii_string.cpp
index 9712d3132c547be18520f65ee2e20000fef14527..e128dca6a002a1f92d10227996d37292da275207 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_string.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_string.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_three.cpp b/packages/PEGTL/src/test/pegtl/ascii_three.cpp
index 2b0b1c03e6e5bf6f6b9d220cceb3fde2c4ed5eb1..68a026debfec176c96238da99298b896669b1610 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_three.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_three.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/ascii_two.cpp b/packages/PEGTL/src/test/pegtl/ascii_two.cpp
index 5326620bdbfa59fca923239148bf68d82ea5d3b0..8c0ff00973cd87f0bf28eb3108aa3ac10e4d5a80 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_two.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_two.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/buffer_input.cpp b/packages/PEGTL/src/test/pegtl/buffer_input.cpp
index e5ab52ba1d502b34ec20923ea59787fe187e7fd3..eff7614e5f347d52cc942487ecf8856643f94e70 100644
--- a/packages/PEGTL/src/test/pegtl/buffer_input.cpp
+++ b/packages/PEGTL/src/test/pegtl/buffer_input.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <string>
 
diff --git a/packages/PEGTL/src/test/pegtl/change_action_and_state.cpp b/packages/PEGTL/src/test/pegtl/change_action_and_state.cpp
index a631f9dc02016cc9267d794f0a9032d08a7f0751..9cf3cce155c0ce5ab17966ad50bb2637f989fcf5 100644
--- a/packages/PEGTL/src/test/pegtl/change_action_and_state.cpp
+++ b/packages/PEGTL/src/test/pegtl/change_action_and_state.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/change_action_and_states.cpp b/packages/PEGTL/src/test/pegtl/change_action_and_states.cpp
index 4bd4f5ffec701333d7bd50767708fd342b77703d..9ea7faa781b2f2aad0ce01633e4e15908dc3d7e0 100644
--- a/packages/PEGTL/src/test/pegtl/change_action_and_states.cpp
+++ b/packages/PEGTL/src/test/pegtl/change_action_and_states.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/change_state.cpp b/packages/PEGTL/src/test/pegtl/change_state.cpp
index bc7840256a7f884b87841cbaad5df6d0d7390743..6061b9178cc9e0ec0a3e5ebcfa2182a5d8a051d8 100644
--- a/packages/PEGTL/src/test/pegtl/change_state.cpp
+++ b/packages/PEGTL/src/test/pegtl/change_state.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/change_states.cpp b/packages/PEGTL/src/test/pegtl/change_states.cpp
index 0dc0f533f90a15ffc727d496c6f348b4dcfcd405..64a4b27620d241d9e23d02860b8dd972e8a66773 100644
--- a/packages/PEGTL/src/test/pegtl/change_states.cpp
+++ b/packages/PEGTL/src/test/pegtl/change_states.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/check_bytes.cpp b/packages/PEGTL/src/test/pegtl/check_bytes.cpp
index b773abed9b0d168bc4502b779617a38a5a3051b0..d5dc2ad00eb8e9c666f352df8a9cf674f59aa021 100644
--- a/packages/PEGTL/src/test/pegtl/check_bytes.cpp
+++ b/packages/PEGTL/src/test/pegtl/check_bytes.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <tao/pegtl/contrib/check_bytes.hpp>
 
diff --git a/packages/PEGTL/src/test/pegtl/contains.cpp b/packages/PEGTL/src/test/pegtl/contains.cpp
index 68b24b73cd3a6e9cea1b3427028e25bd899aa9ad..ee1d9a6fb2a9b57d86c87af48d6fb966354c5d62 100644
--- a/packages/PEGTL/src/test/pegtl/contains.cpp
+++ b/packages/PEGTL/src/test/pegtl/contains.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <type_traits>
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_alphabet.cpp b/packages/PEGTL/src/test/pegtl/contrib_alphabet.cpp
index 1aac0e762a06e36dd7c209ab757578b57606c6b6..73fdecf224063ac5e1e0f3900b35c3b459238f72 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_alphabet.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_alphabet.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_analyze.cpp b/packages/PEGTL/src/test/pegtl/contrib_analyze.cpp
index 438b3a19120909c220aca5c8991da45177dcb423..cce72b544c35918843e7707def84b4f28019709c 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_analyze.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_analyze.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
@@ -35,6 +36,7 @@ namespace TAO_PEGTL_NAMESPACE
       verify_analyze< eolf >( __LINE__, __FILE__, false, false );
       verify_analyze< success >( __LINE__, __FILE__, false, false );
       verify_analyze< failure >( __LINE__, __FILE__, true, false );
+
       // clang-format off
       {
          struct tst : star< tst > {};
diff --git a/packages/PEGTL/src/test/pegtl/contrib_control_action.cpp b/packages/PEGTL/src/test/pegtl/contrib_control_action.cpp
index b60f796e96e0a97125be6ac321ea3dbb0ed2c056..512d1ba541fcccb93543b5c3b0cd3747497f9386 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_control_action.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_control_action.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <string>
 
@@ -48,7 +49,7 @@ namespace TAO_PEGTL_NAMESPACE
       {
          TAO_PEGTL_TEST_UNREACHABLE;
       }
-      // LCOV_EXCL_END
+      // LCOV_EXCL_STOP
    };
 
    template<>
@@ -67,7 +68,7 @@ namespace TAO_PEGTL_NAMESPACE
       {
          TAO_PEGTL_TEST_UNREACHABLE;
       }
-      // LCOV_EXCL_END
+      // LCOV_EXCL_STOP
 
       template< typename ParseInput >
       static void failure( const ParseInput& /*unused*/, int /*unused*/ )
@@ -98,7 +99,7 @@ namespace TAO_PEGTL_NAMESPACE
       {
          TAO_PEGTL_TEST_UNREACHABLE;
       }
-      // LCOV_EXCL_END
+      // LCOV_EXCL_STOP
    };
 
 #if defined( __cpp_exceptions )
@@ -124,7 +125,7 @@ namespace TAO_PEGTL_NAMESPACE
       {
          TAO_PEGTL_TEST_UNREACHABLE;
       }
-      // LCOV_EXCL_END
+      // LCOV_EXCL_STOP
 
       template< typename ParseInput >
       static void unwind( const ParseInput& /*unused*/, int /*unused*/ )
diff --git a/packages/PEGTL/src/test/pegtl/contrib_coverage.cpp b/packages/PEGTL/src/test/pegtl/contrib_coverage.cpp
index c9661624facc95358e6635f51d73582b1ec2380c..b70954e24ec22f59ecf26775aa8d703e6461cd75 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_coverage.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_coverage.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_function.cpp b/packages/PEGTL/src/test/pegtl/contrib_function.cpp
index 3191981de18fbaa6830d203d75edb6969fb7b9ae..3455fe18cc4046c29bb47bfda689ff5dc2ce0e42 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_function.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_function.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_http.cpp b/packages/PEGTL/src/test/pegtl/contrib_http.cpp
index 3fb449d0ae3a17adbfe2694d302159a8775ab097..3bdb52d59c4fa9d9756b5ecf5189fb45da75d8b4 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_http.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_http.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/contrib_if_then.cpp b/packages/PEGTL/src/test/pegtl/contrib_if_then.cpp
index dac262144e0c2577ce753d73bf86772c9b90407d..b98a3ba598dababe65395bf4e85653b369373cc5 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_if_then.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_if_then.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_rule.hpp"
@@ -15,12 +16,12 @@ namespace TAO_PEGTL_NAMESPACE
          if_then< one< 'a' >, one< 'b' >, one< 'c' > >::
          else_if_then< one< 'a' >, one< 'b' > >::
          else_then< one< 'c' > >;
+      // clang-format on
 
       verify_rule< grammar >( __LINE__, __FILE__, "abc", result_type::success, 0 );
       verify_rule< grammar >( __LINE__, __FILE__, "abcd", result_type::success, 1 );
       verify_rule< grammar >( __LINE__, __FILE__, "ab", result_type::local_failure, 2 );
       verify_rule< grammar >( __LINE__, __FILE__, "c", result_type::success, 0 );
-      // clang-format on
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/contrib_instantiate.cpp b/packages/PEGTL/src/test/pegtl/contrib_instantiate.cpp
index fc85dec752d01633ddf3bf9f45642d84b4c896ea..86566822b095f1465fd9ca848e365c7c4bba89da 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_instantiate.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_instantiate.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_integer.cpp b/packages/PEGTL/src/test/pegtl/contrib_integer.cpp
index 3f0f461951bdd37c059b719db709cc43c55a1b52..f3441c0ea0e199e58083c8426d8589ef555def94 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_integer.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_integer.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
@@ -11,6 +12,7 @@ int main()
 
 #include <limits>
 #include <sstream>
+#include <utility>
 
 #include "test.hpp"
 
@@ -64,9 +66,9 @@ namespace TAO_PEGTL_NAMESPACE
    template< typename S >
    std::string lexical_cast( const S s )
    {
-      std::ostringstream o;
-      o << s;
-      return o.str();
+      std::ostringstream oss;
+      oss << s;
+      return std::move( oss ).str();
    }
 
    template< typename S >
@@ -180,55 +182,55 @@ namespace TAO_PEGTL_NAMESPACE
 
       verify_rule< max_seq_rule< 0 > >( __LINE__, __FILE__, "a0b", result_type::success );
       verify_rule< max_seq_rule< 0 > >( __LINE__, __FILE__, "ab", result_type::local_failure );
-      verify_rule< max_seq_rule< 0 > >( __LINE__, __FILE__, "a1b", result_type::global_failure );
-      verify_rule< max_seq_rule< 0 > >( __LINE__, __FILE__, "a9b", result_type::global_failure );
-      verify_rule< max_seq_rule< 0 > >( __LINE__, __FILE__, "a11b", result_type::global_failure );
+      verify_rule< max_seq_rule< 0 > >( __LINE__, __FILE__, "a1b", result_type::local_failure );
+      verify_rule< max_seq_rule< 0 > >( __LINE__, __FILE__, "a9b", result_type::local_failure );
+      verify_rule< max_seq_rule< 0 > >( __LINE__, __FILE__, "a11b", result_type::local_failure );
 
       verify_rule< max_seq_rule< 1 > >( __LINE__, __FILE__, "a0b", result_type::success );
       verify_rule< max_seq_rule< 1 > >( __LINE__, __FILE__, "a1b", result_type::success );
       verify_rule< max_seq_rule< 1 > >( __LINE__, __FILE__, "ab", result_type::local_failure );
-      verify_rule< max_seq_rule< 1 > >( __LINE__, __FILE__, "a2b", result_type::global_failure );
-      verify_rule< max_seq_rule< 1 > >( __LINE__, __FILE__, "a9b", result_type::global_failure );
-      verify_rule< max_seq_rule< 1 > >( __LINE__, __FILE__, "a11b", result_type::global_failure );
+      verify_rule< max_seq_rule< 1 > >( __LINE__, __FILE__, "a2b", result_type::local_failure );
+      verify_rule< max_seq_rule< 1 > >( __LINE__, __FILE__, "a9b", result_type::local_failure );
+      verify_rule< max_seq_rule< 1 > >( __LINE__, __FILE__, "a11b", result_type::local_failure );
 
       verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "a0b", result_type::success );
       verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "a1b", result_type::success );
       verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "a2b", result_type::success );
       verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "ab", result_type::local_failure );
-      verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "a3b", result_type::global_failure );
-      verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "a9b", result_type::global_failure );
-      verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "a11b", result_type::global_failure );
+      verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "a3b", result_type::local_failure );
+      verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "a9b", result_type::local_failure );
+      verify_rule< max_seq_rule< 2 > >( __LINE__, __FILE__, "a11b", result_type::local_failure );
 
       verify_rule< max_seq_rule< 3 > >( __LINE__, __FILE__, "a0b", result_type::success );
       verify_rule< max_seq_rule< 3 > >( __LINE__, __FILE__, "a3b", result_type::success );
       verify_rule< max_seq_rule< 3 > >( __LINE__, __FILE__, "ab", result_type::local_failure );
-      verify_rule< max_seq_rule< 3 > >( __LINE__, __FILE__, "a4b", result_type::global_failure );
-      verify_rule< max_seq_rule< 3 > >( __LINE__, __FILE__, "a11b", result_type::global_failure );
+      verify_rule< max_seq_rule< 3 > >( __LINE__, __FILE__, "a4b", result_type::local_failure );
+      verify_rule< max_seq_rule< 3 > >( __LINE__, __FILE__, "a11b", result_type::local_failure );
 
-      verify_rule< max_seq_rule< 4 > >( __LINE__, __FILE__, "a5b", result_type::global_failure );
-      verify_rule< max_seq_rule< 4 > >( __LINE__, __FILE__, "a11b", result_type::global_failure );
+      verify_rule< max_seq_rule< 4 > >( __LINE__, __FILE__, "a5b", result_type::local_failure );
+      verify_rule< max_seq_rule< 4 > >( __LINE__, __FILE__, "a11b", result_type::local_failure );
 
       verify_rule< max_seq_rule< 9 > >( __LINE__, __FILE__, "a0b", result_type::success );
       verify_rule< max_seq_rule< 9 > >( __LINE__, __FILE__, "a9b", result_type::success );
       verify_rule< max_seq_rule< 9 > >( __LINE__, __FILE__, "ab", result_type::local_failure );
-      verify_rule< max_seq_rule< 9 > >( __LINE__, __FILE__, "a10b", result_type::global_failure );
-      verify_rule< max_seq_rule< 9 > >( __LINE__, __FILE__, "a11b", result_type::global_failure );
+      verify_rule< max_seq_rule< 9 > >( __LINE__, __FILE__, "a10b", result_type::local_failure );
+      verify_rule< max_seq_rule< 9 > >( __LINE__, __FILE__, "a11b", result_type::local_failure );
 
       verify_rule< max_seq_rule< 10 > >( __LINE__, __FILE__, "a0b", result_type::success );
       verify_rule< max_seq_rule< 10 > >( __LINE__, __FILE__, "a9b", result_type::success );
       verify_rule< max_seq_rule< 10 > >( __LINE__, __FILE__, "a10b", result_type::success );
       verify_rule< max_seq_rule< 10 > >( __LINE__, __FILE__, "ab", result_type::local_failure );
-      verify_rule< max_seq_rule< 10 > >( __LINE__, __FILE__, "a11b", result_type::global_failure );
-      verify_rule< max_seq_rule< 10 > >( __LINE__, __FILE__, "a19b", result_type::global_failure );
+      verify_rule< max_seq_rule< 10 > >( __LINE__, __FILE__, "a11b", result_type::local_failure );
+      verify_rule< max_seq_rule< 10 > >( __LINE__, __FILE__, "a19b", result_type::local_failure );
 
       verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a0b", result_type::success );
       verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a9b", result_type::success );
       verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a10b", result_type::success );
       verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a11b", result_type::success );
       verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "ab", result_type::local_failure );
-      verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a12b", result_type::global_failure );
-      verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a13b", result_type::global_failure );
-      verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a111b", result_type::global_failure );
+      verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a12b", result_type::local_failure );
+      verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a13b", result_type::local_failure );
+      verify_rule< max_seq_rule< 11 > >( __LINE__, __FILE__, "a111b", result_type::local_failure );
 
       verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a0b", result_type::success );
       verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a1b", result_type::success );
@@ -237,16 +239,16 @@ namespace TAO_PEGTL_NAMESPACE
       verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a11b", result_type::success );
       verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a12b", result_type::success );
       verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "ab", result_type::local_failure );
-      verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a13b", result_type::global_failure );
-      verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a19b", result_type::global_failure );
-      verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a111b", result_type::global_failure );
+      verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a13b", result_type::local_failure );
+      verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a19b", result_type::local_failure );
+      verify_rule< max_seq_rule< 12 > >( __LINE__, __FILE__, "a111b", result_type::local_failure );
 
       verify_rule< max_seq_rule< 18446744073709551614ULL > >( __LINE__, __FILE__, "a18446744073709551614b", result_type::success );
-      verify_rule< max_seq_rule< 18446744073709551614ULL > >( __LINE__, __FILE__, "a18446744073709551615b", result_type::global_failure );
+      verify_rule< max_seq_rule< 18446744073709551614ULL > >( __LINE__, __FILE__, "a18446744073709551615b", result_type::local_failure );
 
       verify_rule< max_seq_rule< 18446744073709551615ULL > >( __LINE__, __FILE__, "a18446744073709551615b", result_type::success );
-      verify_rule< max_seq_rule< 18446744073709551615ULL > >( __LINE__, __FILE__, "a18446744073709551616b", result_type::global_failure );
-      verify_rule< max_seq_rule< 18446744073709551615ULL > >( __LINE__, __FILE__, "a98446744073709551614b", result_type::global_failure );
+      verify_rule< max_seq_rule< 18446744073709551615ULL > >( __LINE__, __FILE__, "a18446744073709551616b", result_type::local_failure );
+      verify_rule< max_seq_rule< 18446744073709551615ULL > >( __LINE__, __FILE__, "a98446744073709551614b", result_type::local_failure );
 
       verify_analyze< unsigned_rule >( __LINE__, __FILE__, true, false );
       verify_analyze< unsigned_rule_with_action >( __LINE__, __FILE__, true, false );
diff --git a/packages/PEGTL/src/test/pegtl/contrib_iri.cpp b/packages/PEGTL/src/test/pegtl/contrib_iri.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..96890d71c3e66aa9f68db8f7c3f2486c99bcc890
--- /dev/null
+++ b/packages/PEGTL/src/test/pegtl/contrib_iri.cpp
@@ -0,0 +1,58 @@
+// Copyright (c) 2021 Kelvin Hammond
+// Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
+#if !defined( __cpp_exceptions )
+#include <iostream>
+int main()
+{
+   std::cout << "Exception support disabled, skipping test..." << std::endl;
+}
+#else
+
+#include "test.hpp"
+#include "verify_meta.hpp"
+#include "verify_rule.hpp"
+
+#include <tao/pegtl/contrib/iri.hpp>
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   using GRAMMAR = must< iri::IRI, eof >;
+
+   void unit_test()
+   {
+      verify_analyze< GRAMMAR >( __LINE__, __FILE__, true, false );
+
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "https://en.wikipedia.org/wiki/Internationalized_Resource_Identifier", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "https://en.wiktionary.org/wiki/%E1%BF%AC%CF%8C%CE%B4%CE%BF%CF%82", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "https://en.wiktionary.org/wiki/Ῥόδος", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "https://www.myfictionαlbank.com", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "ftp://ftp.is.co.za/rfc/rfc1808.txt", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "file:///C:/Users/Benutzer/Desktop/Uniform%20Resource%20Identifier.html", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "file:///etc/fstab", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "geo:48.33,14.122;u=22.5", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "ldap://[2001:db8::7]/c=GB?objectClass?one", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "gopher://gopher.floodgap.com", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "mailto:John.Doe@example.com", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "sip:911@pbx.mycompany.com", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "news:comp.infosystems.www.servers.unix", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "data:text/plain;charset=iso-8859-7,%be%fa%be", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "tel:+1-816-555-1212", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "telnet://192.0.2.16:80/", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "git://github.com/rails/rails.git", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "crid://broadcaster.com/movies/BestActionMovieEver", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "http://nobody:password@example.org:8080/cgi-bin/script.php?action=submit&pageid=86392001#section_2", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "quake://480fps.com:26000/", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "ftp://300.300.300.300/foo", result_type::success );  // 300.300.300.300 is a valid hostname!
+
+      TAO_PEGTL_TEST_THROWS( parse< GRAMMAR >( memory_input( "", "" ) ) );
+   }
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#include "main.hpp"
+
+#endif
diff --git a/packages/PEGTL/src/test/pegtl/contrib_json.cpp b/packages/PEGTL/src/test/pegtl/contrib_json.cpp
index b7a2945b4253f004afc6f404bad043c01d7c3423..62cf3b778a657c90cfc029cc83b85d97773f61ad 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_json.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_json.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/contrib_parse_tree.cpp b/packages/PEGTL/src/test/pegtl/contrib_parse_tree.cpp
index 2bcd188518fb5a1e4349bf547194d545014aab37..04a8a0a797fae9a1b0967c25af2aa0fb9e8294ac 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_parse_tree.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_parse_tree.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_parse_tree_to_dot.cpp b/packages/PEGTL/src/test/pegtl/contrib_parse_tree_to_dot.cpp
index 98bc76f467799e524adf41a45760974a395fec29..517092ab26237f66b7d563e84c84aebcba0af72b 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_parse_tree_to_dot.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_parse_tree_to_dot.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_partial_trace.cpp b/packages/PEGTL/src/test/pegtl/contrib_partial_trace.cpp
index d4e829852fedabf84738d838efffd19813aae49e..1c8f1a97786d7e4632dc596a573eca742b202641 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_partial_trace.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_partial_trace.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_predicates.cpp b/packages/PEGTL/src/test/pegtl/contrib_predicates.cpp
index 0af55ea6249487986aa669d490afc13e3b443e1c..00403c330686cb07f8dcd52d86192314f0db2588 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_predicates.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_predicates.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/contrib_print.cpp b/packages/PEGTL/src/test/pegtl/contrib_print.cpp
index 8b19e150c32fd3b6c0ec51b7c7f5daa06a76d521..2c2bde7e73dca9565e4c44b1ffcbda2216208e57 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_print.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_print.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_raw_string.cpp b/packages/PEGTL/src/test/pegtl/contrib_raw_string.cpp
index f02f9bc666857746814364a11f4a42db6be4af76..ef6abda59265fd317fc3bded653ceb7559c6767d 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_raw_string.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_raw_string.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/contrib_remove_first_state.cpp b/packages/PEGTL/src/test/pegtl/contrib_remove_first_state.cpp
index bd2aedb94c297e6b1e848cd6d4a5eed29c25a7a9..c31ef5d40739576a07e59fcaad5db9a7a7e2bc07 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_remove_first_state.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_remove_first_state.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_remove_last_states.cpp b/packages/PEGTL/src/test/pegtl/contrib_remove_last_states.cpp
index 76dae2f5eabbde1bb755d861ea0bb0a03bdc0808..240c2abb0001d82c8e7dbc6596740900620c6f33 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_remove_last_states.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_remove_last_states.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_rep_one_min_max.cpp b/packages/PEGTL/src/test/pegtl/contrib_rep_one_min_max.cpp
index 2ee88f010e30eaecd2dae96365d1b7559451e044..69dd862afa5312d7ef385552e2dbf7a7d8ba1af3 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_rep_one_min_max.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_rep_one_min_max.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/contrib_rep_string.cpp b/packages/PEGTL/src/test/pegtl/contrib_rep_string.cpp
index 37b1b891e713ac4241adc14e80b12e1d1917b31b..91eda129eb8fc62b9a70c2caca00947cb4b02dc6 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_rep_string.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_rep_string.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_separated_seq.cpp b/packages/PEGTL/src/test/pegtl/contrib_separated_seq.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1a47a5fbeddf3a2c826a5ab95c96f1086d56f1a3
--- /dev/null
+++ b/packages/PEGTL/src/test/pegtl/contrib_separated_seq.cpp
@@ -0,0 +1,25 @@
+// Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
+
+#include <tao/pegtl/contrib/separated_seq.hpp>
+
+#include <type_traits>
+
+// clang-format off
+struct A {};
+struct B {};
+struct C {};
+struct D {};
+
+struct S {};
+// clang-format on
+
+using namespace TAO_PEGTL_NAMESPACE;
+static_assert( std::is_base_of_v< internal::seq<>, separated_seq< S > > );
+static_assert( std::is_base_of_v< internal::seq< A >, separated_seq< S, A > > );
+static_assert( std::is_base_of_v< internal::seq< A, S, B >, separated_seq< S, A, B > > );
+static_assert( std::is_base_of_v< internal::seq< A, S, B, S, C, S, D >, separated_seq< S, A, B, C, D > > );
+
+int main()
+{}
diff --git a/packages/PEGTL/src/test/pegtl/contrib_state_control.cpp b/packages/PEGTL/src/test/pegtl/contrib_state_control.cpp
index 2a68ace18232645dfb6ed36f219c2b7ea8cc3f2d..7fc3b2b2929a904fe4c30efb1bd3fbc8e107a986 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_state_control.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_state_control.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/contrib_to_string.cpp b/packages/PEGTL/src/test/pegtl/contrib_to_string.cpp
index a956be930d82f62e4b3e9ff01dd03296f2fb6a0f..bd03ed78940f1abbf14f56d7abffd71c6c038717 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_to_string.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_to_string.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_trace1.cpp b/packages/PEGTL/src/test/pegtl/contrib_trace1.cpp
index 6a0212b023ad0cf91d4cd18427c3c485e727e401..31cc5c866e3d732f0c26a53276f534a1aca443f6 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_trace1.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_trace1.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_trace2.cpp b/packages/PEGTL/src/test/pegtl/contrib_trace2.cpp
index a8a35a6b0bc027d8c24bf9d1a324e208e5b7d913..90112f7e896c7a3af151d23d884cf9990ccc9621 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_trace2.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_trace2.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_unescape.cpp b/packages/PEGTL/src/test/pegtl/contrib_unescape.cpp
index 7b010b6b1b815e8bbf04c1cd8c90ce6325a2d45e..9efda4b9001406bcfd82d09cc5a6aedb25b01f89 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_unescape.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_unescape.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_uri.cpp b/packages/PEGTL/src/test/pegtl/contrib_uri.cpp
index 7ae74cd9038d4e2b0353acf62ac0a77d47a62855..9162abe33b8b81c83a92a0d468ff3f686fcbd1ff 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_uri.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_uri.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
@@ -23,23 +24,25 @@ namespace TAO_PEGTL_NAMESPACE
    {
       verify_analyze< GRAMMAR >( __LINE__, __FILE__, true, false );
 
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "http://de.wikipedia.org/wiki/Uniform_Resource_Identifier", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "ftp://ftp.is.co.za/rfc/rfc1808.txt", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "file:///C:/Users/Benutzer/Desktop/Uniform%20Resource%20Identifier.html", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "file:///etc/fstab", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "geo:48.33,14.122;u=22.5", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "ldap://[2001:db8::7]/c=GB?objectClass?one", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "gopher://gopher.floodgap.com", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "mailto:John.Doe@example.com", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "sip:911@pbx.mycompany.com", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "news:comp.infosystems.www.servers.unix", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "data:text/plain;charset=iso-8859-7,%be%fa%be", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "tel:+1-816-555-1212", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "telnet://192.0.2.16:80/", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "git://github.com/rails/rails.git", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "crid://broadcaster.com/movies/BestActionMovieEver", result_type::success, 0 );
-      verify_rule< GRAMMAR >( __LINE__, __FILE__, "http://nobody:password@example.org:8080/cgi-bin/script.php?action=submit&pageid=86392001#section_2", result_type::success, 0 );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "http://de.wikipedia.org/wiki/Uniform_Resource_Identifier", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "ftp://ftp.is.co.za/rfc/rfc1808.txt", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "file:///C:/Users/Benutzer/Desktop/Uniform%20Resource%20Identifier.html", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "file:///etc/fstab", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "geo:48.33,14.122;u=22.5", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "ldap://[2001:db8::7]/c=GB?objectClass?one", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "gopher://gopher.floodgap.com", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "mailto:John.Doe@example.com", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "sip:911@pbx.mycompany.com", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "news:comp.infosystems.www.servers.unix", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "data:text/plain;charset=iso-8859-7,%be%fa%be", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "tel:+1-816-555-1212", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "telnet://192.0.2.16:80/", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "urn:oasis:names:specification:docbook:dtd:xml:4.1.2", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "git://github.com/rails/rails.git", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "crid://broadcaster.com/movies/BestActionMovieEver", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "http://nobody:password@example.org:8080/cgi-bin/script.php?action=submit&pageid=86392001#section_2", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "quake://480fps.com:26000/", result_type::success );
+      verify_rule< GRAMMAR >( __LINE__, __FILE__, "ftp://300.300.300.300/foo", result_type::success );  // 300.300.300.300 is a valid hostname!
 
       TAO_PEGTL_TEST_THROWS( parse< GRAMMAR >( memory_input( "", "" ) ) );
    }
diff --git a/packages/PEGTL/src/test/pegtl/control_unwind.cpp b/packages/PEGTL/src/test/pegtl/control_unwind.cpp
index b07dcdcccd91882f5db6d5514bc59ad6636e4ffc..468678e23268f6bd7d830d6f9a83a83cd4487acd 100644
--- a/packages/PEGTL/src/test/pegtl/control_unwind.cpp
+++ b/packages/PEGTL/src/test/pegtl/control_unwind.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/data_cstring.cpp b/packages/PEGTL/src/test/pegtl/data_cstring.cpp
index fc1e5c270c48c1b833781650b3def2e6ed342fe7..f8e86171e85e0e00ac5ec5a0b0c706eb6097e222 100644
--- a/packages/PEGTL/src/test/pegtl/data_cstring.cpp
+++ b/packages/PEGTL/src/test/pegtl/data_cstring.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/demangle.cpp b/packages/PEGTL/src/test/pegtl/demangle.cpp
index c1ae7396e4c798e478f09547163bf4c54fd8c766..b7acf79c27f62ec3e8f1058213f34e29e284b7ac 100644
--- a/packages/PEGTL/src/test/pegtl/demangle.cpp
+++ b/packages/PEGTL/src/test/pegtl/demangle.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/discard_input.cpp b/packages/PEGTL/src/test/pegtl/discard_input.cpp
index 996d75c8a86512cf30ed192920c3212031f696a4..3dac97076d12635e3cb8ea9f209b262a3aedd043 100644
--- a/packages/PEGTL/src/test/pegtl/discard_input.cpp
+++ b/packages/PEGTL/src/test/pegtl/discard_input.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <string>
 
diff --git a/packages/PEGTL/src/test/pegtl/enable_control.cpp b/packages/PEGTL/src/test/pegtl/enable_control.cpp
index 29c76c1379a51cb120d4f3bcaee0b8dcc9a58a62..49429e39142d7a05b5074af83a41b5300e118dda 100644
--- a/packages/PEGTL/src/test/pegtl/enable_control.cpp
+++ b/packages/PEGTL/src/test/pegtl/enable_control.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <tao/pegtl.hpp>
 
diff --git a/packages/PEGTL/src/test/pegtl/error_message.cpp b/packages/PEGTL/src/test/pegtl/error_message.cpp
index 8e19e9ae85f4ad0bc7f076def9b1b193d968ff1b..220cfbc5292fb517020a67b5f256e5ad643ea3fd 100644
--- a/packages/PEGTL/src/test/pegtl/error_message.cpp
+++ b/packages/PEGTL/src/test/pegtl/error_message.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
@@ -22,11 +23,17 @@ namespace test1
 
    template< typename > inline constexpr const char* error_message = nullptr;
    template<> inline constexpr auto error_message< test1::b > = "test123";
-
-   struct error { template< typename Rule > static constexpr auto message = error_message< Rule >; };
-   template< typename Rule > using control = must_if< error >::control< Rule >;
    // clang-format on
 
+   struct error
+   {
+      template< typename Rule >
+      static constexpr auto message = error_message< Rule >;
+   };
+
+   template< typename Rule >
+   using control = must_if< error >::control< Rule >;
+
 }  // namespace test1
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/file_cstream.cpp b/packages/PEGTL/src/test/pegtl/file_cstream.cpp
index 93051028300e02b64231ee21f0fb8bbba8427969..d51dbd2b08f669e6a1edda1b18f82219fc8ab656 100644
--- a/packages/PEGTL/src/test/pegtl/file_cstream.cpp
+++ b/packages/PEGTL/src/test/pegtl/file_cstream.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <clocale>
 #include <cstdio>
diff --git a/packages/PEGTL/src/test/pegtl/file_file.cpp b/packages/PEGTL/src/test/pegtl/file_file.cpp
index 61abd5e31a1e1a73ae8b8cc8f0e139088fdc455a..fcc20af53b7d42e61ae4362a2a409d8b469be792 100644
--- a/packages/PEGTL/src/test/pegtl/file_file.cpp
+++ b/packages/PEGTL/src/test/pegtl/file_file.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_file.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/file_istream.cpp b/packages/PEGTL/src/test/pegtl/file_istream.cpp
index 41d1a84eb74e99e265c149fd9c32041e1a0f9933..cb4e3cae24b090ec563cd5c19279bf2a326f4820 100644
--- a/packages/PEGTL/src/test/pegtl/file_istream.cpp
+++ b/packages/PEGTL/src/test/pegtl/file_istream.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <cerrno>
 #include <fstream>
diff --git a/packages/PEGTL/src/test/pegtl/file_mmap.cpp b/packages/PEGTL/src/test/pegtl/file_mmap.cpp
index e9fd5804d06db85350aeadb05ccde36859561c96..afa83d1c3a635c36122aae00ca22010fabf00bf1 100644
--- a/packages/PEGTL/src/test/pegtl/file_mmap.cpp
+++ b/packages/PEGTL/src/test/pegtl/file_mmap.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 // this include gives us _POSIX_MAPPED_FILES to test and mmap_input<> if it is set
 #include <tao/pegtl/file_input.hpp>
diff --git a/packages/PEGTL/src/test/pegtl/file_read.cpp b/packages/PEGTL/src/test/pegtl/file_read.cpp
index 4e2ee3bb7e15d15fedfe770cb2ca6812bd515dc4..963120e7d66733c080c98cfefa17a703d6934879 100644
--- a/packages/PEGTL/src/test/pegtl/file_read.cpp
+++ b/packages/PEGTL/src/test/pegtl/file_read.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_file.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/icu_general.cpp b/packages/PEGTL/src/test/pegtl/icu_general.cpp
index 7191cbbe4188c627ac35ee08505f51f91fa8dbc1..15e6e62cd7424d3c1c00687ea44675b607ae1c2e 100644
--- a/packages/PEGTL/src/test/pegtl/icu_general.cpp
+++ b/packages/PEGTL/src/test/pegtl/icu_general.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/internal_endian.cpp b/packages/PEGTL/src/test/pegtl/internal_endian.cpp
index 50296fd176bab4dca5a457de3248e2834d072bd1..f1bb6b1cb57b838aea115e93aa0b424931a758e5 100644
--- a/packages/PEGTL/src/test/pegtl/internal_endian.cpp
+++ b/packages/PEGTL/src/test/pegtl/internal_endian.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <tao/pegtl/contrib/internal/endian.hpp>
 
diff --git a/packages/PEGTL/src/test/pegtl/internal_file_mapper.cpp b/packages/PEGTL/src/test/pegtl/internal_file_mapper.cpp
index 2cf26fa40dbeb88f855407e24698e1c6a2ceb9df..94f6e3f5bf94eb71c1e4e0bbdbb6e0dd0e653e91 100644
--- a/packages/PEGTL/src/test/pegtl/internal_file_mapper.cpp
+++ b/packages/PEGTL/src/test/pegtl/internal_file_mapper.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions ) || !defined( _POSIX_MAPPED_FILES )
 #include <iostream>
@@ -22,7 +23,7 @@ namespace TAO_PEGTL_NAMESPACE
          // LCOV_EXCL_START
          std::cerr << "pegtl: unit test failed for [ internal::file_mapper ]" << std::endl;
          ++failed;
-         // LCOV_EXCL_END
+         // LCOV_EXCL_STOP
       }
       catch( const internal::filesystem::filesystem_error& ) {
       }
@@ -31,7 +32,7 @@ namespace TAO_PEGTL_NAMESPACE
          std::cerr << "pegtl: unit test failed for [ internal::file_mapper ] with unexpected exception" << std::endl;
          ++failed;
       }
-      // LCOV_EXCL_END
+      // LCOV_EXCL_STOP
 
       const std::string s = "dummy content\n";
       const std::string dummy_content = s + s + s + s + s + s + s + s + s + s + s;
diff --git a/packages/PEGTL/src/test/pegtl/internal_file_opener.cpp b/packages/PEGTL/src/test/pegtl/internal_file_opener.cpp
index 33f7aba203bc5de016b09656095aec901adf2363..1ca75d9609c386e2fd1945b25c24868d29fb5b94 100644
--- a/packages/PEGTL/src/test/pegtl/internal_file_opener.cpp
+++ b/packages/PEGTL/src/test/pegtl/internal_file_opener.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions ) || !defined( _POSIX_MAPPED_FILES )
 #include <iostream>
@@ -25,7 +26,7 @@ namespace TAO_PEGTL_NAMESPACE
          // LCOV_EXCL_START
          std::cerr << "pegtl: unit test failed for [ internal::file_opener ] " << std::endl;
          ++failed;
-         // LCOV_EXCL_END
+         // LCOV_EXCL_STOP
       }
       catch( const std::exception& ) {
       }
diff --git a/packages/PEGTL/src/test/pegtl/limit_bytes.cpp b/packages/PEGTL/src/test/pegtl/limit_bytes.cpp
index 94e66c3f69bebc1748b9d86a322ee5518012492c..f376ad835650bd77f27cd284bf3323ef11bb7afa 100644
--- a/packages/PEGTL/src/test/pegtl/limit_bytes.cpp
+++ b/packages/PEGTL/src/test/pegtl/limit_bytes.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <tao/pegtl/contrib/limit_bytes.hpp>
 
diff --git a/packages/PEGTL/src/test/pegtl/limit_depth.cpp b/packages/PEGTL/src/test/pegtl/limit_depth.cpp
index 8fb1b2713c3091278d716a4857a9f34e3e1f2fe4..3f9df51639fef2d3eef3d5414845fd29c4163666 100644
--- a/packages/PEGTL/src/test/pegtl/limit_depth.cpp
+++ b/packages/PEGTL/src/test/pegtl/limit_depth.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <tao/pegtl/contrib/limit_depth.hpp>
 
diff --git a/packages/PEGTL/src/test/pegtl/main.hpp b/packages/PEGTL/src/test/pegtl/main.hpp
index f216e73dc4bbfe40be8f883eaa81a97bee648c26..cb09aebbe7c1832dce27c9f46c8a370d97a3ef47 100644
--- a/packages/PEGTL/src/test/pegtl/main.hpp
+++ b/packages/PEGTL/src/test/pegtl/main.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_MAIN_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_MAIN_HPP
diff --git a/packages/PEGTL/src/test/pegtl/parse_error.cpp b/packages/PEGTL/src/test/pegtl/parse_error.cpp
index 6141e3efc7549ac1e48e8de36cb33ec5dcacd2cc..7fda8f61eea8ab51a9b077e7b8791097aba6e442 100644
--- a/packages/PEGTL/src/test/pegtl/parse_error.cpp
+++ b/packages/PEGTL/src/test/pegtl/parse_error.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/pegtl_string_t.cpp b/packages/PEGTL/src/test/pegtl/pegtl_string_t.cpp
index ff3cc3ac8498e245134c5cc775803c2d548b49a8..41147a373d26bdb40c23f3cf6f600b544e212293 100644
--- a/packages/PEGTL/src/test/pegtl/pegtl_string_t.cpp
+++ b/packages/PEGTL/src/test/pegtl/pegtl_string_t.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <type_traits>
 
diff --git a/packages/PEGTL/src/test/pegtl/position.cpp b/packages/PEGTL/src/test/pegtl/position.cpp
index 666f182ab5bf79f2ab98ebfa07705a68cc099eb1..60590e47492627cc48448d98308871d62dc046e5 100644
--- a/packages/PEGTL/src/test/pegtl/position.cpp
+++ b/packages/PEGTL/src/test/pegtl/position.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/restart_input.cpp b/packages/PEGTL/src/test/pegtl/restart_input.cpp
index 3cb964c88bc0019aa15c24201980b044659fcfa1..648d118289f8a425f61627e4ea9d2d9f206b6b43 100644
--- a/packages/PEGTL/src/test/pegtl/restart_input.cpp
+++ b/packages/PEGTL/src/test/pegtl/restart_input.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/result_type.hpp b/packages/PEGTL/src/test/pegtl/result_type.hpp
index 91b84a6e0906bb44bea4c375558d1537ea298705..9f8448f3e7de52869b388ab776d5e18101391415 100644
--- a/packages/PEGTL/src/test/pegtl/result_type.hpp
+++ b/packages/PEGTL/src/test/pegtl/result_type.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_RESULT_TYPE_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_RESULT_TYPE_HPP
diff --git a/packages/PEGTL/src/test/pegtl/rule_action.cpp b/packages/PEGTL/src/test/pegtl/rule_action.cpp
index cdaae0c7c4df719f4d45cba93636eaa0cb51af17..c9e5a9f600340fdbf1f9c7cf3e46fb8f1e11b3be 100644
--- a/packages/PEGTL/src/test/pegtl/rule_action.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_action.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_apply.cpp b/packages/PEGTL/src/test/pegtl/rule_apply.cpp
index 722fe096c632c7eca37e76c2fd53ef426ed7ee45..6ae99f88a5b216b01eea245915289baeda386896 100644
--- a/packages/PEGTL/src/test/pegtl/rule_apply.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_apply.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
@@ -60,7 +61,7 @@ namespace TAO_PEGTL_NAMESPACE
          {
             TAO_PEGTL_TEST_ASSERT( false );
          }
-         // LCOV_EXCL_END
+         // LCOV_EXCL_STOP
       };
 
    }  // namespace test1
diff --git a/packages/PEGTL/src/test/pegtl/rule_apply0.cpp b/packages/PEGTL/src/test/pegtl/rule_apply0.cpp
index 010fca8d29aca776cd0c80e1d7599327c7fe4df1..2c4fdfffe86bfe68f384ab84b53355d8a7b74fa3 100644
--- a/packages/PEGTL/src/test/pegtl/rule_apply0.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_apply0.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
@@ -55,7 +56,7 @@ namespace TAO_PEGTL_NAMESPACE
          {
             TAO_PEGTL_TEST_ASSERT( false );
          }
-         // LCOV_EXCL_END
+         // LCOV_EXCL_STOP
       };
 
    }  // namespace test1
diff --git a/packages/PEGTL/src/test/pegtl/rule_at.cpp b/packages/PEGTL/src/test/pegtl/rule_at.cpp
index 8d24f4e217fdcc1105129a1d5d49637355cdfc54..996081fda56252f71c57de571a4ade36b4bc0f12 100644
--- a/packages/PEGTL/src/test/pegtl/rule_at.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_at.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_bof.cpp b/packages/PEGTL/src/test/pegtl/rule_bof.cpp
index e7e563ee7999af7fbd2ea856e67a139531d5baf6..7462e560b4edfeec65c4ae2960a58790beef7875 100644
--- a/packages/PEGTL/src/test/pegtl/rule_bof.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_bof.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_bol.cpp b/packages/PEGTL/src/test/pegtl/rule_bol.cpp
index 34c63020bcedddef349c9428aab458df37b93ef0..837fcc331eebf5a82b9f6703c781d331470086f4 100644
--- a/packages/PEGTL/src/test/pegtl/rule_bol.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_bol.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_bytes.cpp b/packages/PEGTL/src/test/pegtl/rule_bytes.cpp
index 2caa4834a28deb9ba00c0b2ca4830030fd961f3d..806640778857cfa885171e8a6046d8eb6f71c86d 100644
--- a/packages/PEGTL/src/test/pegtl/rule_bytes.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_bytes.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_control.cpp b/packages/PEGTL/src/test/pegtl/rule_control.cpp
index baaef260eab0c59bb4ab438ddb156993fd5b5757..1b69ebd453c300e8de8cb37ce0543f3985b0e9a8 100644
--- a/packages/PEGTL/src/test/pegtl/rule_control.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_control.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_disable.cpp b/packages/PEGTL/src/test/pegtl/rule_disable.cpp
index acbbb9f3f1a9a0832f878b86557a07579468bbb7..65570121b73fad4b103dae193cbb019180f982cc 100644
--- a/packages/PEGTL/src/test/pegtl/rule_disable.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_disable.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_discard.cpp b/packages/PEGTL/src/test/pegtl/rule_discard.cpp
index 8e5b93d1e408edd016a1668c37e8522054244f3e..bce896337e55c24232496ef593ef926c1277cc5b 100644
--- a/packages/PEGTL/src/test/pegtl/rule_discard.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_discard.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_enable.cpp b/packages/PEGTL/src/test/pegtl/rule_enable.cpp
index 9741545fe7390a753566f0b04b6ddb43fcb26c1b..63c4d1560339faaa290c9ead46c2b82517340528 100644
--- a/packages/PEGTL/src/test/pegtl/rule_enable.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_enable.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_eof.cpp b/packages/PEGTL/src/test/pegtl/rule_eof.cpp
index b5098b40cf178a1329d0fa27d21de660a1503b12..8ed986e9b6a6d40821811d8dbe061b74cac8bd26 100644
--- a/packages/PEGTL/src/test/pegtl/rule_eof.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_eof.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_failure.cpp b/packages/PEGTL/src/test/pegtl/rule_failure.cpp
index c1481d4d1d6c7d259fc00260553a31c514760434..19044b3446f767d1af60e7c46daf353d3486f8ab 100644
--- a/packages/PEGTL/src/test/pegtl/rule_failure.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_failure.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_if_apply.cpp b/packages/PEGTL/src/test/pegtl/rule_if_apply.cpp
index 4297e9a8c80d473dd765f800288cfd1e8ced903f..74602f9a77e384c5dc69eb3266d3bbaa32de5755 100644
--- a/packages/PEGTL/src/test/pegtl/rule_if_apply.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_if_apply.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_seqs.hpp"
@@ -61,7 +62,7 @@ namespace TAO_PEGTL_NAMESPACE
          {
             TAO_PEGTL_TEST_ASSERT( false );
          }
-         // LCOV_EXCL_END
+         // LCOV_EXCL_STOP
       };
 
       template< typename Rule >
diff --git a/packages/PEGTL/src/test/pegtl/rule_if_must.cpp b/packages/PEGTL/src/test/pegtl/rule_if_must.cpp
index 6dd3754232ce1b333215ab30dc0cd496ae7920d8..13f7332ae56146a674760412432539bc0c494e85 100644
--- a/packages/PEGTL/src/test/pegtl/rule_if_must.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_if_must.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/rule_if_must_else.cpp b/packages/PEGTL/src/test/pegtl/rule_if_must_else.cpp
index 0b7f370596405829571a99bdc956953e7a71c204..fa28715d0f1b4170f3607d94383a97e9820a0b27 100644
--- a/packages/PEGTL/src/test/pegtl/rule_if_must_else.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_if_must_else.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/rule_if_then_else.cpp b/packages/PEGTL/src/test/pegtl/rule_if_then_else.cpp
index dc15f6fb0688e270487563f40fe501707caa2aa1..b1596f69085b28266f70d966bc52e8dcb7d1f2b2 100644
--- a/packages/PEGTL/src/test/pegtl/rule_if_then_else.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_if_then_else.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_ifmt.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_list.cpp b/packages/PEGTL/src/test/pegtl/rule_list.cpp
index 106942f6dcc348a55c45d7f0fa958b64bfc50160..2abf474ee895277bf3d88d7d6a0e11e9ea4c5c11 100644
--- a/packages/PEGTL/src/test/pegtl/rule_list.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_list.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_list_must.cpp b/packages/PEGTL/src/test/pegtl/rule_list_must.cpp
index 9de21172d522e07c5d71980b4965116b952f0708..0fab79e8a76524d455a32f45b3f39c0e9f052607 100644
--- a/packages/PEGTL/src/test/pegtl/rule_list_must.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_list_must.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/rule_list_tail.cpp b/packages/PEGTL/src/test/pegtl/rule_list_tail.cpp
index d6e9dd59b8a507916550c7ca6e43f8506f286f18..41296111d8b3356af4771f20bd934d575e87d8ff 100644
--- a/packages/PEGTL/src/test/pegtl/rule_list_tail.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_list_tail.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_minus.cpp b/packages/PEGTL/src/test/pegtl/rule_minus.cpp
index 4fd5f5722022b4b29f0ab9238338fa0cc0bdeffc..66c37197343fe2a3376554d14dc0e94827d639ec 100644
--- a/packages/PEGTL/src/test/pegtl/rule_minus.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_minus.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2016-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_must.cpp b/packages/PEGTL/src/test/pegtl/rule_must.cpp
index f1d481132352e07f857cf5cd728a858934215f27..1065457c58dca1c5796bf9b15c6a88f27081fafa 100644
--- a/packages/PEGTL/src/test/pegtl/rule_must.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_must.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/rule_not_at.cpp b/packages/PEGTL/src/test/pegtl/rule_not_at.cpp
index 77cc80fc64e807d35d24687accc75779df600218..aa5ba3b6641a13a1f2943f98887f149361e96c98 100644
--- a/packages/PEGTL/src/test/pegtl/rule_not_at.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_not_at.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_opt.cpp b/packages/PEGTL/src/test/pegtl/rule_opt.cpp
index c07d22a4f619d5031b42a118d054f1f7f1ad2892..f05a738b7514319540fd3bf6f03f075bebbe5065 100644
--- a/packages/PEGTL/src/test/pegtl/rule_opt.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_opt.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_opt_must.cpp b/packages/PEGTL/src/test/pegtl/rule_opt_must.cpp
index 5c1ce6e45daf75561ee38a232041913139276390..0adb157f657ccc8fb822782eab8d56c99bab3926 100644
--- a/packages/PEGTL/src/test/pegtl/rule_opt_must.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_opt_must.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/rule_pad.cpp b/packages/PEGTL/src/test/pegtl/rule_pad.cpp
index 918b1c67b8e5c987b4362d568d66eb6c068b5e6e..2bbfc254b2294c58d678e30540717005fb450174 100644
--- a/packages/PEGTL/src/test/pegtl/rule_pad.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_pad.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_pad_opt.cpp b/packages/PEGTL/src/test/pegtl/rule_pad_opt.cpp
index 5ff4ded637a490f8e82cfb6d5b5df0d586c3454c..d21bb55d8ef48f307918216618941aa71e7db600 100644
--- a/packages/PEGTL/src/test/pegtl/rule_pad_opt.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_pad_opt.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_plus.cpp b/packages/PEGTL/src/test/pegtl/rule_plus.cpp
index d1a82ad3462177a93b7f34c6c64fb2b75463ff86..f0b0942d34399a8d700f7c234e54829f23cc628c 100644
--- a/packages/PEGTL/src/test/pegtl/rule_plus.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_plus.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_raise.cpp b/packages/PEGTL/src/test/pegtl/rule_raise.cpp
index 25a67c8136e84fa840775efba6f2487c7121f856..7eaa05d97f5cdb1bc7f73c8c0c19e2934fad116e 100644
--- a/packages/PEGTL/src/test/pegtl/rule_raise.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_raise.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/rule_rematch.cpp b/packages/PEGTL/src/test/pegtl/rule_rematch.cpp
index 22fca401d93226b98e27ff5f0ee98858d50168a3..3c505d0b0cd627d47e4bd71d415edc9c64d260f1 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rematch.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rematch.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2019-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep.cpp b/packages/PEGTL/src/test/pegtl/rule_rep.cpp
index 7d7ef941f44228ffc9119e370c2d197cc6a1bc75..ce259438a70d88b1c3030ebaa95b4d1d47c6c614 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep_max.cpp b/packages/PEGTL/src/test/pegtl/rule_rep_max.cpp
index 0b4a2c0d2c0c767e1f00281350aababa89fd8e49..2ff63e9cc90302325b70e90ffd2f53302b89fa0c 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep_max.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep_max.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep_min.cpp b/packages/PEGTL/src/test/pegtl/rule_rep_min.cpp
index 8564aabbf972551159d06b6b3c2e2fb72e20db8d..614db7363e91a188c27cd03e8f38523e3b28b2be 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep_min.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep_min.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep_min_max.cpp b/packages/PEGTL/src/test/pegtl/rule_rep_min_max.cpp
index 95d3db6311e1eaa0efb867d53f439821d23d6e1e..ebf85c500b889a5df9b39322f85c1336b04cf7e1 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep_min_max.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep_min_max.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep_opt.cpp b/packages/PEGTL/src/test/pegtl/rule_rep_opt.cpp
index 0f5a6b6779f9b86fb9c5a0908c9d8a43e7ce3e57..c94a11ab7c0186463b1d714eea5e2b8d3b5f3917 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep_opt.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep_opt.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_require.cpp b/packages/PEGTL/src/test/pegtl/rule_require.cpp
index e6598a4ee577bac69252a3997db216887a89a61d..b93e2e3a1c31ed9059de670ec98c3360a49697af 100644
--- a/packages/PEGTL/src/test/pegtl/rule_require.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_require.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2017-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_seq.cpp b/packages/PEGTL/src/test/pegtl/rule_seq.cpp
index b80f478b4bbcc85ab12208163b0f3fb4109216b6..f5eac6bbdbaad5755b2bbe5f62ec99885f3e569c 100644
--- a/packages/PEGTL/src/test/pegtl/rule_seq.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_seq.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_sor.cpp b/packages/PEGTL/src/test/pegtl/rule_sor.cpp
index ec4258c2c8f93c5735441b6828bad3fcc5702c0f..7d087cb73c1a279bfb1beff03e7f7d8d52a7f9a9 100644
--- a/packages/PEGTL/src/test/pegtl/rule_sor.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_sor.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_star.cpp b/packages/PEGTL/src/test/pegtl/rule_star.cpp
index c6f46e0001cca3de8950ea8a5bb3a0e8efa467aa..df7d63e08164efe27e35c2572c2d7a9fe0f04dde 100644
--- a/packages/PEGTL/src/test/pegtl/rule_star.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_star.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_star_must.cpp b/packages/PEGTL/src/test/pegtl/rule_star_must.cpp
index 20747861a808d444e93ffb956fd9f64ecf8e961d..6b22eac70ffe94e4180473135e29fa7d68374941 100644
--- a/packages/PEGTL/src/test/pegtl/rule_star_must.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_star_must.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/rule_state.cpp b/packages/PEGTL/src/test/pegtl/rule_state.cpp
index 8d92565b91103aaf6675bed55e5aa21e55674aa2..f29701262982d0eb081acbc42725dbd792b09aa7 100644
--- a/packages/PEGTL/src/test/pegtl/rule_state.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_state.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_success.cpp b/packages/PEGTL/src/test/pegtl/rule_success.cpp
index cf810c685992cd7c41b2903472ea57ceaa43805f..03e0835369ed22bb8981190a6284b90acb729421 100644
--- a/packages/PEGTL/src/test/pegtl/rule_success.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_success.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_try_catch.cpp b/packages/PEGTL/src/test/pegtl/rule_try_catch.cpp
index c9a5103045946788beb63bc1a66a5ffb7054d887..909cc77d9943b7c76d1259cd9da90ceeec3d225e 100644
--- a/packages/PEGTL/src/test/pegtl/rule_try_catch.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_try_catch.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #if !defined( __cpp_exceptions )
 #include <iostream>
diff --git a/packages/PEGTL/src/test/pegtl/rule_until.cpp b/packages/PEGTL/src/test/pegtl/rule_until.cpp
index 2d8c7faccdadfefb58824b1111b4ceb43aa495b4..12385c15a5ee30481d2118d1df11bd327d4ea8e5 100644
--- a/packages/PEGTL/src/test/pegtl/rule_until.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_until.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_meta.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/test.hpp b/packages/PEGTL/src/test/pegtl/test.hpp
index 95c6a59b36da917cfc19d5f97720ba6af3c2051a..e25635ee2c02fbfe6ced5f1cfcd1079e9ec611d0 100644
--- a/packages/PEGTL/src/test/pegtl/test.hpp
+++ b/packages/PEGTL/src/test/pegtl/test.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_TEST_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_TEST_HPP
diff --git a/packages/PEGTL/src/test/pegtl/test_empty.cpp b/packages/PEGTL/src/test/pegtl/test_empty.cpp
index b213f1ea81482c359ad0911c0daa12370a82a16c..bfc9967778d806fb6ee34979cba1ee8232a17c88 100644
--- a/packages/PEGTL/src/test/pegtl/test_empty.cpp
+++ b/packages/PEGTL/src/test/pegtl/test_empty.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 
diff --git a/packages/PEGTL/src/test/pegtl/test_result.cpp b/packages/PEGTL/src/test/pegtl/test_result.cpp
index 2bf7c5355c693e6c6e3473efb5bc3e289afead5f..86554fb558a4cad9e4a3fdb75af72188cee44082 100644
--- a/packages/PEGTL/src/test/pegtl/test_result.cpp
+++ b/packages/PEGTL/src/test/pegtl/test_result.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <sstream>
 
diff --git a/packages/PEGTL/src/test/pegtl/test_setup.cpp b/packages/PEGTL/src/test/pegtl/test_setup.cpp
index a6d87ed887155c48a31282948a19e32c9d3f5816..20c3737bfa10b1986bdca50114aeb613fc25cb75 100644
--- a/packages/PEGTL/src/test/pegtl/test_setup.cpp
+++ b/packages/PEGTL/src/test/pegtl/test_setup.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <iostream>
 #include <utility>
diff --git a/packages/PEGTL/src/test/pegtl/uint16_general.cpp b/packages/PEGTL/src/test/pegtl/uint16_general.cpp
index b1073af47f985541d612e2c3d4dfed8937feede3..134ecbb9baf87083b59a0b516cc1059d07d42a2f 100644
--- a/packages/PEGTL/src/test/pegtl/uint16_general.cpp
+++ b/packages/PEGTL/src/test/pegtl/uint16_general.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/uint32_general.cpp b/packages/PEGTL/src/test/pegtl/uint32_general.cpp
index 18dfd34e6d5450f5c6e9325a71447e055f03e5ee..f5f259df1b85902578c1fcb058fdd2011322d8bd 100644
--- a/packages/PEGTL/src/test/pegtl/uint32_general.cpp
+++ b/packages/PEGTL/src/test/pegtl/uint32_general.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/uint64_general.cpp b/packages/PEGTL/src/test/pegtl/uint64_general.cpp
index 54aba3821ce31a376b168940221aea6d7fb2c526..91daedb35648d9bef7a01ed482e5e4f92c31ffaf 100644
--- a/packages/PEGTL/src/test/pegtl/uint64_general.cpp
+++ b/packages/PEGTL/src/test/pegtl/uint64_general.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/uint8_general.cpp b/packages/PEGTL/src/test/pegtl/uint8_general.cpp
index 211fd7399410bd8af471eaf0e89bcbe8b70afb38..ee6baff86d76ca5992d7171392a293c2a9761111 100644
--- a/packages/PEGTL/src/test/pegtl/uint8_general.cpp
+++ b/packages/PEGTL/src/test/pegtl/uint8_general.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2018-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/utf16_general.cpp b/packages/PEGTL/src/test/pegtl/utf16_general.cpp
index 12461ca55093149fcd76f465ed54a43c75b9c4e6..7e32a8cce8a3e4de9d92dc00bc419fe98c885edb 100644
--- a/packages/PEGTL/src/test/pegtl/utf16_general.cpp
+++ b/packages/PEGTL/src/test/pegtl/utf16_general.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2015-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_rule.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/utf32_general.cpp b/packages/PEGTL/src/test/pegtl/utf32_general.cpp
index c63a44dfeb1edc2a2c55663bd8a47a4eab8f5945..21895a9dfb3534c4a639c6a3c2d7d54a3cbc4876 100644
--- a/packages/PEGTL/src/test/pegtl/utf32_general.cpp
+++ b/packages/PEGTL/src/test/pegtl/utf32_general.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_rule.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/utf8_general.cpp b/packages/PEGTL/src/test/pegtl/utf8_general.cpp
index c61663e1bf1a857480509cc452e595fac5557564..cab108dc67bfa4766f7b83f505a268eb5177cbd4 100644
--- a/packages/PEGTL/src/test/pegtl/utf8_general.cpp
+++ b/packages/PEGTL/src/test/pegtl/utf8_general.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include "test.hpp"
 #include "verify_char.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/verify_char.hpp b/packages/PEGTL/src/test/pegtl/verify_char.hpp
index 4f48fabc0aca00da4f63137ccdcef7780ab01694..32290bc4f82a36d1bf4763c1a5d4bb11205fb122 100644
--- a/packages/PEGTL/src/test/pegtl/verify_char.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_char.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_CHAR_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_CHAR_HPP
diff --git a/packages/PEGTL/src/test/pegtl/verify_file.hpp b/packages/PEGTL/src/test/pegtl/verify_file.hpp
index c35c972488173d7559ec0fd719eba548fc6a3c68..b8a27230c97778eb286be3387d25dffb02b6efb9 100644
--- a/packages/PEGTL/src/test/pegtl/verify_file.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_file.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_FILE_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_FILE_HPP
diff --git a/packages/PEGTL/src/test/pegtl/verify_ifmt.hpp b/packages/PEGTL/src/test/pegtl/verify_ifmt.hpp
index c957693b54fe1492dafe2412eb1dc1e452433173..1dffef16e4b02cbfd93e8e6983cb0e14c5afbfcc 100644
--- a/packages/PEGTL/src/test/pegtl/verify_ifmt.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_ifmt.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_IFMT_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_IFMT_HPP
diff --git a/packages/PEGTL/src/test/pegtl/verify_impl.hpp b/packages/PEGTL/src/test/pegtl/verify_impl.hpp
index 312f2785ab01f76458d12e3cdb1fd40a10b33da8..6a4a93f898f08aa29558eb334bc0b0e4d11326df 100644
--- a/packages/PEGTL/src/test/pegtl/verify_impl.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_impl.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_IMPL_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_IMPL_HPP
@@ -36,7 +37,7 @@ namespace TAO_PEGTL_NAMESPACE
       catch( ... ) {
          TAO_PEGTL_TEST_UNREACHABLE;
       }
-      // LCOV_EXCL_END
+      // LCOV_EXCL_STOP
 
 #else
 
diff --git a/packages/PEGTL/src/test/pegtl/verify_meta.hpp b/packages/PEGTL/src/test/pegtl/verify_meta.hpp
index e7350f132df2111ac520b6a088f73f355bfba0a0..40d871210d756f4c90579c75281d5420395bac56 100644
--- a/packages/PEGTL/src/test/pegtl/verify_meta.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_meta.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_META_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_META_HPP
diff --git a/packages/PEGTL/src/test/pegtl/verify_rule.hpp b/packages/PEGTL/src/test/pegtl/verify_rule.hpp
index 390a5a369ae670a094512aed100c9a611049f035..d24076b9d4eb38d08fa8b4b0d4be52c6e7238474 100644
--- a/packages/PEGTL/src/test/pegtl/verify_rule.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_rule.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_RULE_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_RULE_HPP
diff --git a/packages/PEGTL/src/test/pegtl/verify_seqs.hpp b/packages/PEGTL/src/test/pegtl/verify_seqs.hpp
index 1b01aad29e054acc426ae17be48db5854651c103..487c995276b64ccbd82fd75496229b6fa5289e9f 100644
--- a/packages/PEGTL/src/test/pegtl/verify_seqs.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_seqs.hpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2014-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_SEQS_HPP
 #define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_SEQS_HPP
diff --git a/packages/PEGTL/src/test/pegtl/visit.cpp b/packages/PEGTL/src/test/pegtl/visit.cpp
index 2bdcb86e2b0229a8f1bcdc6ec5b36f27b6b9640c..2b30442f05dcceaa0c7de9fba8b8d901de8e5a94 100644
--- a/packages/PEGTL/src/test/pegtl/visit.cpp
+++ b/packages/PEGTL/src/test/pegtl/visit.cpp
@@ -1,5 +1,6 @@
 // Copyright (c) 2020-2021 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Distributed under the Boost Software License, Version 1.0.
+// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
 
 #include <string>
 #include <vector>
diff --git a/packages/kokkos/.clang-format-ignore b/packages/kokkos/.clang-format-ignore
index b163a2bfeaf4e865925fb96f46260fd3ec2cfcb9..43d242c3106a29c063e0258bbd4e4553f66f883c 100644
--- a/packages/kokkos/.clang-format-ignore
+++ b/packages/kokkos/.clang-format-ignore
@@ -1,2 +1,3 @@
 core/unit_test/config/results/*
 tpls/gtest/gtest/*
+core/src/desul/*
diff --git a/packages/kokkos/.clang-tidy b/packages/kokkos/.clang-tidy
index 207a105c5bdf60b807db528d612aac89e6bb88b6..2b0d6e51d438948c2d5ef85e100c97ca16184e9b 100644
--- a/packages/kokkos/.clang-tidy
+++ b/packages/kokkos/.clang-tidy
@@ -1,3 +1,3 @@
-Checks: '-*,kokkos-*,modernize-use-using,modernize-use-nullptr'
+Checks: '-*,kokkos-*,modernize-use-using,modernize-use-nullptr,cppcoreguidelines-pro-type-cstyle-cast'
 FormatStyle: file
 HeaderFilterRegex: '.*/*.hpp'
diff --git a/packages/kokkos/.github/workflows/continuous-integration-workflow.yml b/packages/kokkos/.github/workflows/continuous-integration-workflow.yml
index 0e5f523ccf77014b18a034659b450f7036901747..b76167f330a87eaf79af25f706c33d3e910865d1 100644
--- a/packages/kokkos/.github/workflows/continuous-integration-workflow.yml
+++ b/packages/kokkos/.github/workflows/continuous-integration-workflow.yml
@@ -19,16 +19,19 @@ jobs:
             cxx: 'icpc'
             cmake_build_type: 'Debug'
             openmp: 'ON'
-          - distro: 'fedora:intel-oneapi'
+          - distro: 'fedora:intel'
             cxx: 'icpx'
             cmake_build_type: 'Release'
             openmp: 'ON'
-          - distro: 'fedora:intel-oneapi'
+          - distro: 'fedora:intel'
             cxx: 'icpx'
             cmake_build_type: 'Debug'
             openmp: 'ON'
     runs-on: ubuntu-latest
-    container: ghcr.io/kokkos/ci-containers/${{ matrix.distro }}
+    container:
+      image: ghcr.io/kokkos/ci-containers/${{ matrix.distro }}
+      # see https://github.com/actions/virtual-environments/issues/3812
+      options: --security-opt seccomp=unconfined
     steps:
       - name: Checkout code
         uses: actions/checkout@v2.2.0
@@ -37,29 +40,29 @@ jobs:
           path: ~/.ccache
           key: kokkos-${{ matrix.distro }}-${{ matrix.cxx }}-${{ matrix.cmake_build_type }}-${{ matrix.openmp }}-${github.ref}-${{ github.sha }}
           restore-keys: kokkos-${{ matrix.distro }}-${{ matrix.cxx }}-${{ matrix.cmake_build_type }}-${{ matrix.openmp }}-${{github.ref}}
-      - name: Get trial license
-        if: ${{ matrix.cxx == 'icpc' }}
-        run: |
-          mkdir ~/Licenses
-          curl https://dynamicinstaller.intel.com/api/v2/license > ~/Licenses/intel.lic
       - name: maybe_disable_death_tests
         if: ${{ matrix.distro == 'fedora:rawhide' }}
         run: echo "GTEST_FILTER=-*DeathTest*" >> $GITHUB_ENV
-      - name: build-and-test
+      - name: CMake
         run: |
-          ccache -z
-          cmake \
+          cmake -B builddir \
             -DCMAKE_INSTALL_PREFIX=/usr \
             -DKokkos_ENABLE_HWLOC=ON \
             -DKokkos_ENABLE_OPENMP=${{ matrix.openmp }} \
             -DKokkos_ENABLE_TESTS=ON \
             -DKokkos_ENABLE_EXAMPLES=ON \
+            -DKokkos_ENABLE_DEPRECATED_CODE_3=ON \
+            -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF \
             -DCMAKE_CXX_COMPILER=${{ matrix.cxx }} \
-            -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }} \
-            -DBUILD_NAME=${{ matrix.distro }}-${{ matrix.cxx }} \
-            -DBUILD_JOBS=2 -DBINARY_DIR=builddir -DSITE=GitHub-Linux \
-            -P cmake/KokkosCI.cmake
+            -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }}
+      - name: Build
+        run: |
+          ccache -z
+          cmake --build builddir --parallel 2
           ccache -s
+      - name: Tests
+        working-directory: builddir
+        run: ctest --output-on-failure
       - name: Test DESTDIR Install
         run: DESTDIR=${PWD}/install cmake --build builddir --target install && rm -rf ${PWD}/install/usr && rmdir ${PWD}/install
       - name: Install
diff --git a/packages/kokkos/.github/workflows/osx.yml b/packages/kokkos/.github/workflows/osx.yml
index 855b557c829a609f34b82c7e5f307eef60cf0ede..178af12405cc2f6fc24a5ae46adf034b1c73a94e 100644
--- a/packages/kokkos/.github/workflows/osx.yml
+++ b/packages/kokkos/.github/workflows/osx.yml
@@ -21,15 +21,19 @@ jobs:
 
     steps:
       - uses: actions/checkout@v2
-      - name: build-and-test
+      - name: configure
         run:
-          cmake
+          cmake -B build .
             -DKokkos_ENABLE_${{ matrix.backend }}=On
             -DCMAKE_CXX_FLAGS="-Werror"
             -DCMAKE_CXX_STANDARD=14
             -DKokkos_ENABLE_COMPILER_WARNINGS=ON
+            -DKokkos_ENABLE_DEPRECATED_CODE_3=OFF
             -DKokkos_ENABLE_TESTS=On
             -DCMAKE_BUILD_TYPE=${{ matrix.cmake_build_type }}
-            -DBUILD_NAME=macOS-${{ matrix.backend }}
-            -DTARGET=install -DBUILD_JOBS=2 -DSITE=GitHub-OSX
-            -P cmake/KokkosCI.cmake
+      - name: build
+        run:
+          cmake --build build --parallel 2
+      - name: test
+        working-directory: build
+        run: ctest --output-on-failure
diff --git a/packages/kokkos/.gitrepo b/packages/kokkos/.gitrepo
index 6dd4101e5bdf1210d26ef2ff0a34f557416c532b..bfbe5e6fd3ec3ae381fe5adbd8b39d0797bff2fa 100644
--- a/packages/kokkos/.gitrepo
+++ b/packages/kokkos/.gitrepo
@@ -6,7 +6,7 @@
 [subrepo]
 	remote = git@github.com:kokkos/kokkos.git
 	branch = master
-	commit = 4b97a22ff7be7635116930bb97173058d6079202
-	parent = f2fc77ba9037b2a2032ab980fb445175441f6d1f
+	commit = 2879e23507bcb21adb739d6317b3430f665de4a6
+	parent = 36833c0c0fc1a841eaed63df6b7d34609307f2a5
 	method = merge
 	cmdver = 0.4.3
diff --git a/packages/kokkos/.jenkins b/packages/kokkos/.jenkins
index 001171d648e7cfb2236d17439720562707faaab4..09e8515e96f2bb8255bcaba7304780b484f303ea 100644
--- a/packages/kokkos/.jenkins
+++ b/packages/kokkos/.jenkins
@@ -5,9 +5,12 @@ pipeline {
         CCACHE_DIR = '/tmp/ccache'
         CCACHE_MAXSIZE = '10G'
         CCACHE_CPP2 = 'true'
-        BUILD_JOBS = 8
-        SITE = 'Jenkins'
     }
+
+    options {
+        timeout(time: 6, unit: 'HOURS')
+    }
+
     stages {
         stage('Clang-Format') {
             agent {
@@ -36,7 +39,7 @@ pipeline {
                     }
                     steps {
                         sh 'ccache --zero-stats'
-                        sh '''rm -rf build && \
+                        sh '''rm -rf build && mkdir -p build && cd build && \
                               cmake \
                                 -DCMAKE_BUILD_TYPE=Release \
                                 -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
@@ -44,13 +47,15 @@ pipeline {
                                 -DCMAKE_CXX_FLAGS="-Werror -Wno-unknown-cuda-version -Wno-gnu-zero-variadic-macro-arguments" \
                                 -DKokkos_ARCH_VOLTA70=ON \
                                 -DKokkos_ENABLE_COMPILER_WARNINGS=ON \
+                                -DKokkos_ENABLE_DEPRECATED_CODE_3=ON \
+                                -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF \
                                 -DKokkos_ENABLE_EXAMPLES=ON \
                                 -DKokkos_ENABLE_TESTS=ON \
                                 -DKokkos_ENABLE_SYCL=ON \
                                 -DKokkos_ENABLE_UNSUPPORTED_ARCHS=ON \
                                 -DCMAKE_CXX_STANDARD=17 \
-                                -DBUILD_NAME=${STAGE_NAME} \
-                              -P cmake/KokkosCI.cmake'''
+                              .. && \
+                              make -j8 && ctest --verbose'''
                     }
                     post {
                         always {
@@ -58,12 +63,12 @@ pipeline {
                         }
                     }
                 }
-                stage('HIP-ROCm-3.8-C++14') {
+                stage('HIP-ROCm-4.2-C++14') {
                     agent {
                         dockerfile {
                             filename 'Dockerfile.hipcc'
                             dir 'scripts/docker'
-                            additionalBuildArgs '--build-arg BASE=rocm/dev-ubuntu-20.04:3.8'
+                            additionalBuildArgs '--build-arg BASE=rocm/dev-ubuntu-20.04:4.2'
                             label 'rocm-docker && vega'
                             args '-v /tmp/ccache.kokkos:/tmp/ccache --device=/dev/kfd --device=/dev/dri --security-opt seccomp=unconfined --group-add video --env HIP_VISIBLE_DEVICES=$HIP_VISIBLE_DEVICES'
                         }
@@ -72,24 +77,23 @@ pipeline {
                         OMP_NUM_THREADS = 8
                         OMP_PLACES = 'threads'
                         OMP_PROC_BIND = 'spread'
-                        LC_ALL = 'C'
                     }
                     steps {
                         sh 'ccache --zero-stats'
                         sh 'echo "/opt/rocm/llvm/lib" > /etc/ld.so.conf.d/llvm.conf && ldconfig'
-                        sh '''rm -rf build && \
+                        sh '''rm -rf build && mkdir -p build && cd build && \
                               cmake \
                                 -DCMAKE_BUILD_TYPE=Debug \
                                 -DCMAKE_CXX_COMPILER=hipcc \
                                 -DCMAKE_CXX_FLAGS="-Werror -Wno-unused-command-line-argument -DNDEBUG" \
                                 -DCMAKE_CXX_STANDARD=14 \
                                 -DKokkos_ENABLE_COMPILER_WARNINGS=ON \
+                                -DKokkos_ENABLE_DEPRECATED_CODE_3=OFF \
                                 -DKokkos_ENABLE_TESTS=ON \
                                 -DKokkos_ENABLE_HIP=ON \
-                                -DKokkos_ARCH_VEGA906=ON \
                                 -DKokkos_ENABLE_OPENMP=ON \
-                                -DBUILD_NAME=${STAGE_NAME} \
-                              -P cmake/KokkosCI.cmake'''
+                              .. && \
+                              make -j8 && ctest --verbose'''
                     }
                     post {
                         always {
@@ -97,33 +101,73 @@ pipeline {
                         }
                     }
                 }
-                stage('HIP-ROCm-3.8-C++17') {
+                stage('HIP-ROCm-4.2-C++17') {
                     agent {
                         dockerfile {
                             filename 'Dockerfile.hipcc'
                             dir 'scripts/docker'
-                            additionalBuildArgs '--build-arg BASE=rocm/dev-ubuntu-20.04:3.8'
+                            additionalBuildArgs '--build-arg BASE=rocm/dev-ubuntu-20.04:4.2'
                             label 'rocm-docker && vega'
                             args '-v /tmp/ccache.kokkos:/tmp/ccache --device=/dev/kfd --device=/dev/dri --security-opt seccomp=unconfined --group-add video --env HIP_VISIBLE_DEVICES=$HIP_VISIBLE_DEVICES'
                         }
                     }
-                    environment {
-                        LC_ALL = 'C'
-                    }
                     steps {
                         sh 'ccache --zero-stats'
-                        sh '''rm -rf build && \
+                        sh '''rm -rf build && mkdir -p build && cd build && \
                               cmake \
                                 -DCMAKE_BUILD_TYPE=RelWithDebInfo \
                                 -DCMAKE_CXX_COMPILER=hipcc \
                                 -DCMAKE_CXX_FLAGS="-Werror -Wno-unused-command-line-argument" \
                                 -DCMAKE_CXX_STANDARD=17 \
                                 -DKokkos_ENABLE_COMPILER_WARNINGS=ON \
+                                -DKokkos_ENABLE_DEPRECATED_CODE_3=ON \
+                                -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF \
                                 -DKokkos_ENABLE_TESTS=ON \
                                 -DKokkos_ENABLE_HIP=ON \
+                              .. && \
+                              make -j8 && ctest --verbose'''
+                    }
+                    post {
+                        always {
+                            sh 'ccache --show-stats'
+                        }
+                    }
+                }
+                stage('OPENMPTARGET-ROCm-4.2') {
+                    agent {
+                        dockerfile {
+                            filename 'Dockerfile.hipcc'
+                            dir 'scripts/docker'
+                            additionalBuildArgs '--build-arg BASE=rocm/dev-ubuntu-20.04:4.2'
+                            label 'rocm-docker && vega && AMD_Radeon_Instinct_MI60'
+                            args '-v /tmp/ccache.kokkos:/tmp/ccache --device=/dev/kfd --device=/dev/dri --security-opt seccomp=unconfined --group-add video --env HIP_VISIBLE_DEVICES=$HIP_VISIBLE_DEVICES'
+                        }
+                    }
+                    environment {
+                        OMP_NUM_THREADS = 8
+                        OMP_PLACES = 'threads'
+                        OMP_PROC_BIND = 'spread'
+                        LC_ALL = 'C'
+                    }
+                    steps {
+                        sh 'ccache --zero-stats'
+                        sh 'echo "/opt/rocm/llvm/lib" > /etc/ld.so.conf.d/llvm.conf && ldconfig'
+                        sh '''rm -rf build && \
+                              cmake \
+                                -Bbuild \
+                                -DCMAKE_BUILD_TYPE=Debug \
+                                -DCMAKE_CXX_COMPILER=/opt/rocm/llvm/bin/clang++ \
+                                -DCMAKE_CXX_STANDARD=17 \
+                                -DKokkos_ENABLE_COMPILER_WARNINGS=ON \
+                                -DKokkos_ENABLE_DEPRECATED_CODE_3=OFF \
+                                -DKokkos_ENABLE_TESTS=ON \
+                                -DKokkos_ENABLE_OPENMPTARGET=ON \
+                                -DKokkos_ENABLE_OPENMP=ON \
                                 -DKokkos_ARCH_VEGA906=ON \
-                                -DBUILD_NAME=${STAGE_NAME} \
-                              -P cmake/KokkosCI.cmake'''
+                              && \
+                              cmake --build build --parallel ${BUILD_JOBS} && \
+                              cd build && ctest --output-on-failure
+                        '''
                     }
                     post {
                         always {
@@ -142,19 +186,21 @@ pipeline {
                     }
                     steps {
                         sh 'ccache --zero-stats'
-                        sh '''rm -rf build && \
+                        sh '''rm -rf build && mkdir -p build && cd build && \
                               cmake \
                                 -DCMAKE_BUILD_TYPE=RelWithDebInfo \
                                 -DCMAKE_CXX_COMPILER=clang++ \
                                 -DCMAKE_CXX_FLAGS="-Wno-unknown-cuda-version -Werror -Wno-undefined-internal -Wno-pass-failed" \
                                 -DKokkos_ENABLE_COMPILER_WARNINGS=ON \
+                                -DKokkos_ENABLE_DEPRECATED_CODE_3=ON \
+                                -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF \
                                 -DKokkos_ENABLE_TESTS=ON \
                                 -DKokkos_ENABLE_TUNING=ON \
                                 -DKokkos_ENABLE_OPENMPTARGET=ON \
                                 -DKokkos_ARCH_VOLTA70=ON \
                                 -DCMAKE_CXX_STANDARD=17 \
-                                -DBUILD_NAME=${STAGE_NAME} \
-                              -P cmake/KokkosCI.cmake'''
+                              .. && \
+                              make -j8 && ctest --verbose'''
                     }
                     post {
                         always {
@@ -173,7 +219,7 @@ pipeline {
                     }
                     steps {
                         sh 'ccache --zero-stats'
-                        sh '''rm -rf build && \
+                        sh '''rm -rf build && mkdir -p build && cd build && \
                               cmake \
                                 -DCMAKE_BUILD_TYPE=Release \
                                 -DCMAKE_CXX_CLANG_TIDY="clang-tidy;-warnings-as-errors=*" \
@@ -182,13 +228,15 @@ pipeline {
                                 -DCMAKE_CXX_FLAGS=-Werror \
                                 -DCMAKE_CXX_STANDARD=14 \
                                 -DKokkos_ENABLE_COMPILER_WARNINGS=ON \
+                                -DKokkos_ENABLE_DEPRECATED_CODE_3=ON \
+                                -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF \
                                 -DKokkos_ENABLE_TESTS=ON \
                                 -DKokkos_ENABLE_CUDA=ON \
                                 -DKokkos_ENABLE_CUDA_LAMBDA=ON \
                                 -DKokkos_ENABLE_TUNING=ON \
                                 -DKokkos_ARCH_VOLTA70=ON \
-                                -DBUILD_NAME=${STAGE_NAME} \
-                              -P cmake/KokkosCI.cmake'''
+                              .. && \
+                              make -j8 && ctest --verbose'''
                     }
                     post {
                         always {
@@ -244,7 +292,7 @@ pipeline {
                     steps {
                         sh 'ccache --zero-stats'
                         sh '''rm -rf install && mkdir -p install && \
-                              rm -rf build && \
+                              rm -rf build && mkdir -p build && cd build && \
                               cmake \
                                 -DCMAKE_BUILD_TYPE=Release \
                                 -DCMAKE_CXX_COMPILER=g++-8 \
@@ -256,10 +304,12 @@ pipeline {
                                 -DKokkos_ENABLE_CUDA_LAMBDA=OFF \
                                 -DKokkos_ENABLE_CUDA_UVM=ON \
                                 -DKokkos_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE=ON \
-                                -DCMAKE_INSTALL_PREFIX=${PWD}/install \
-                                -DBUILD_NAME=${STAGE_NAME} \
-                                -DTARGET=install \
-                              -P cmake/KokkosCI.cmake && \
+                                -DKokkos_ENABLE_DEPRECATED_CODE_3=ON \
+                                -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF \
+                                -DCMAKE_INSTALL_PREFIX=${PWD}/../install \
+                              .. && \
+                              make -j8 install && \
+                              cd .. && \
                               rm -rf build-tests && mkdir -p build-tests && cd build-tests && \
                               export CMAKE_PREFIX_PATH=${PWD}/../install && \
                               cmake \
@@ -302,7 +352,7 @@ pipeline {
                     }
                     steps {
                         sh 'ccache --zero-stats'
-                        sh '''rm -rf build && \
+                        sh '''rm -rf build && mkdir -p build && cd build && \
                               cmake \
                                 -DCMAKE_BUILD_TYPE=Debug \
                                 -DCMAKE_CXX_COMPILER_LAUNCHER=ccache \
@@ -312,14 +362,14 @@ pipeline {
                                 -DKokkos_ENABLE_COMPILER_WARNINGS=ON \
                                 -DKokkos_ENABLE_DEBUG=ON \
                                 -DKokkos_ENABLE_DEBUG_BOUNDS_CHECK=ON \
+                                -DKokkos_ENABLE_DEPRECATED_CODE_3=OFF \
                                 -DKokkos_ENABLE_TESTS=ON \
                                 -DKokkos_ENABLE_CUDA=ON \
                                 -DKokkos_ENABLE_CUDA_LAMBDA=ON \
                                 -DKokkos_ENABLE_LIBDL=OFF \
-                                -DBUILD_NAME=${STAGE_NAME} \
-                                -DTARGET=install \
-                              -P cmake/KokkosCI.cmake && \
-                              cd example/build_cmake_in_tree && \
+                              .. && \
+                              make -j8 && ctest --verbose && \
+                              cd ../example/build_cmake_in_tree && \
                               rm -rf build && mkdir -p build && cd build && \
                               cmake -DCMAKE_CXX_STANDARD=14 .. && make -j8 && ctest --verbose'''
                     }
@@ -342,18 +392,21 @@ pipeline {
                         OMP_PROC_BIND = 'true'
                     }
                     steps {
-                        sh '''rm -rf build && \
+                        sh '''rm -rf build && mkdir -p build && cd build && \
                               cmake \
                                 -DCMAKE_BUILD_TYPE=Release \
                                 -DCMAKE_CXX_STANDARD=14 \
                                 -DCMAKE_CXX_FLAGS=-Werror \
                                 -DKokkos_ENABLE_COMPILER_WARNINGS=ON \
+                                -DKokkos_ENABLE_DEPRECATED_CODE_3=ON \
+                                -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF \
                                 -DKokkos_ENABLE_TESTS=ON \
                                 -DKokkos_ENABLE_OPENMP=ON \
                                 -DKokkos_ENABLE_LIBDL=OFF \
-                                -DBUILD_NAME=${STAGE_NAME} \
-                              -P cmake/KokkosCI.cmake && \
-                              gcc -I$PWD/core/src core/unit_test/tools/TestCInterface.c'''
+                                -DKokkos_ENABLE_LIBQUADMATH=ON \
+                                -DCMAKE_PREFIX_PATH=/usr/local/lib/gcc/x86_64-unknown-linux-gnu/5.3.0 \
+                              .. && \
+                              make -j8 && ctest --verbose && gcc -I$PWD/../core/src/ ../core/unit_test/tools/TestCInterface.c'''
                     }
                 }
             }
diff --git a/packages/kokkos/.travis.yml b/packages/kokkos/.travis.yml
index 04ef01c1602cf87aae3e39225037d65f49651f62..87d0fd5cf6ed8b9ebc158d1eb8bbe91d56101963 100644
--- a/packages/kokkos/.travis.yml
+++ b/packages/kokkos/.travis.yml
@@ -67,14 +67,13 @@ install:
 
 before_script:
   - ccache -z
-  - if [[ ${COVERAGE} ]]; then export CXX="${CXX} --coverage"; export BUILD_NAME_SUFFIX="-Coverage"; fi
+  - if [[ ${COVERAGE} ]]; then export CXX="${CXX} --coverage"; fi
   - if [[ ! ${CMAKE_BUILD_TYPE} ]]; then export CXXFLAGS="${CXXFLAGS} -O2"; fi
 
 script:
   - export OMP_NUM_THREADS=2
   - export OMP_PLACES=threads
   - export OMP_PROC_BIND=spread
-  - export BUILD_JOBS=2
   # LD_LIBRARY_PATH workaround to find clang's libomp: https://github.com/travis-ci/travis-ci/issues/8613
   - if [[ ${CC} = clang ]]; then export LD_LIBRARY_PATH=/usr/local/clang/lib${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH; fi
   # enable ccache for clang on linux and add CCACHE_CPP2 to avoid 'Argument unused during compilation -I...' warning
@@ -82,17 +81,17 @@ script:
       ln -s /usr/bin/ccache $HOME/bin/clang++;
       export CCACHE_CPP2=yes;
     fi
-  - cmake
+  - mkdir build &&
+    pushd build &&
+    cmake ..
           ${BACKEND:+-DKokkos_ENABLE_${BACKEND}=On}
           -DCMAKE_CXX_FLAGS="${CXXFLAGS} -Werror"
           -DCMAKE_CXX_STANDARD=14
           -DKokkos_ENABLE_COMPILER_WARNINGS=ON
           -DKokkos_ENABLE_TESTS=On
-          ${CMAKE_BUILD_TYPE:+-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}}
-          -DBUILD_NAME="${CC}-${BACKEND}${BUILD_NAME_SUFFIX}"
-          -DSITE=Travis
-          -P cmake/KokkosCI.cmake &&
-    pushd build &&
+          ${CMAKE_BUILD_TYPE:+-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}} &&
+    make VERBOSE=1 -j2 &&
+    travis_wait 60 make test CTEST_OUTPUT_ON_FAILURE=1 &&
     make install DESTDIR=${PWD}/install && rm -rf ${PWD}/install/usr/local && rmdir ${PWD}/install/usr &&
     popd
 
diff --git a/packages/kokkos/BUILD.md b/packages/kokkos/BUILD.md
index e1f0e3e472762fa7c78a68350da9e9bc74f41de1..bb1a31f266ec487c27daced9971d481e41d81c2d 100644
--- a/packages/kokkos/BUILD.md
+++ b/packages/kokkos/BUILD.md
@@ -262,6 +262,9 @@ Architecture-specific optimizations can be enabled by specifying `-DKokkos_ARCH_
 * Kokkos_ARCH_ZEN2
     * Whether to optimize for the Zen2 architecture
     * BOOL Default: OFF
+* Kokkos_ARCH_ZEN3
+    * Whether to optimize for the Zen3 architecture
+    * BOOL Default: OFF
 * Kokkos_ARCH_HSW
     * Whether to optimize for the HSW architecture
     * BOOL Default: OFF
diff --git a/packages/kokkos/CHANGELOG.md b/packages/kokkos/CHANGELOG.md
index 3ce38c37d866dacc25528f5597461e7629175e00..2e779791dde2a83394662f640f90626f66696f28 100644
--- a/packages/kokkos/CHANGELOG.md
+++ b/packages/kokkos/CHANGELOG.md
@@ -1,5 +1,180 @@
 # Change Log
 
+## [3.5.00](https://github.com/kokkos/kokkos/tree/3.5.00) (2021-10-19)
+[Full Changelog](https://github.com/kokkos/kokkos/compare/3.4.01...3.5.00)
+
+### Features:
+
+- Add support for quad-precision math functions/traits [\#4098](https://github.com/kokkos/kokkos/pull/4098)
+- Adding ExecutionSpace partitioning function [\#4096](https://github.com/kokkos/kokkos/pull/4096)
+- Improve Python Interop Capabilities [\#4065](https://github.com/kokkos/kokkos/pull/4065)
+- Add half_t Kokkos::rand specialization [\#3922](https://github.com/kokkos/kokkos/pull/3922)
+- Add math special functions: erf, erfcx, expint1, Bessel functions, Hankel functions [\#3920](https://github.com/kokkos/kokkos/pull/3920)
+- Add missing common mathematical functions [\#4043](https://github.com/kokkos/kokkos/pull/4043) [\#4036](https://github.com/kokkos/kokkos/pull/4036) [\#4034](https://github.com/kokkos/kokkos/pull/4034)
+- Let the numeric traits be SFINAE-friendly [\#4038](https://github.com/kokkos/kokkos/pull/4038)
+- Add Desul atomics - enabling memory-order and memory-scope parameters [\#3247](https://github.com/kokkos/kokkos/pull/3247)
+- Add detection idiom from the C++ standard library extension version 2 [\#3980](https://github.com/kokkos/kokkos/pull/3980)
+- Fence Profiling Support in all backends [\#3966](https://github.com/kokkos/kokkos/pull/3966) [\#4304](https://github.com/kokkos/kokkos/pull/4304) [\#4258](https://github.com/kokkos/kokkos/pull/4258) [\#4232](https://github.com/kokkos/kokkos/pull/4232)
+- Significant SYCL enhancements (see below)
+
+### Deprecations:
+
+- Deprecate CUDA_SAFE_CALL and HIP_SAFE_CALL [\#4249](https://github.com/kokkos/kokkos/pull/4249)
+- Deprecate Kokkos::Impl::Timer (Kokkos::Timer has been available for a long time) [\#4201](https://github.com/kokkos/kokkos/pull/4201)
+- Deprecate Experimental::MasterLock [\#4094](https://github.com/kokkos/kokkos/pull/4094)
+- Deprecate Kokkos_TaskPolicy.hpp (headers got reorganized, doesn't remove functionality) [\#4011](https://github.com/kokkos/kokkos/pull/4011)
+- Deprecate backward compatibility features [\#3978](https://github.com/kokkos/kokkos/pull/3978)
+- Update and deprecate is_space::host_memory/execution/mirror_space [\#3973](https://github.com/kokkos/kokkos/pull/3973)
+
+
+### Backends and Archs Enhancements:
+
+- Enabling constbitset constructors in kernels [\#4296](https://github.com/kokkos/kokkos/pull/4296)
+- Use ZeroMemset in View constructor to improve performance [\#4226](https://github.com/kokkos/kokkos/pull/4226)
+- Use memset in deep_copy [\#3944](https://github.com/kokkos/kokkos/pull/3944)
+- Add missing fence() calls in resize(View) that effectively do deep_copy(resized, orig) [\#4212](https://github.com/kokkos/kokkos/pull/4212)
+- Avoid allocations in resize and realloc [\#4207](https://github.com/kokkos/kokkos/pull/4207)
+- StaticCsrGraph: use device type instead of execution space to construct views [\#3991](https://github.com/kokkos/kokkos/pull/3991)
+- Consider std::sort when view is accessible from host [\#3929](https://github.com/kokkos/kokkos/pull/3929)
+- Fix CPP20 warnings except for volatile [\#4312](https://github.com/kokkos/kokkos/pull/4312)
+
+#### SYCL:
+- Introduce SYCLHostUSMSpace [\#4268](https://github.com/kokkos/kokkos/pull/4268)
+- Implement SYCL TeamPolicy for vector_size > 1 [\#4183](https://github.com/kokkos/kokkos/pull/4183)
+- Enable 64bit ranges for SYCL [\#4211](https://github.com/kokkos/kokkos/pull/4211)
+- Don't print SYCL device info in execution space intialization [\#4168](https://github.com/kokkos/kokkos/pull/4168)
+- Improve SYCL MDRangePolicy performance [\#4161](https://github.com/kokkos/kokkos/pull/4161)
+- Use sub_groups in SYCL parallel_scan [\#4147](https://github.com/kokkos/kokkos/pull/4147)
+- Implement subgroup reduction for SYCL RangePolicy parallel_reduce [\#3940](https://github.com/kokkos/kokkos/pull/3940)
+- Use DPC++ broadcast extension in SYCL team_broadcast [\#4103](https://github.com/kokkos/kokkos/pull/4103)
+- Only fence in SYCL parallel_reduce for non-device-accessible result_ptr [\#4089](https://github.com/kokkos/kokkos/pull/4089)
+- Improve fencing behavior in SYCL backend [\#4088](https://github.com/kokkos/kokkos/pull/4088)
+- Fence all registered SYCL queues before deallocating memory [\#4086](https://github.com/kokkos/kokkos/pull/4086)
+- Implement SYCL::print_configuration [\#3992](https://github.com/kokkos/kokkos/pull/3992)
+- Reuse scratch memory in parallel_scan and TeamPolicy (decreases memory footprint) [\#3899](https://github.com/kokkos/kokkos/pull/3899) [\#3889](https://github.com/kokkos/kokkos/pull/3889)
+
+#### CUDA:
+- Cuda improve heuristic for blocksize [\#4271](https://github.com/kokkos/kokkos/pull/4271)
+- Don't use [[deprecated]] for nvcc [\#4229](https://github.com/kokkos/kokkos/pull/4229)
+- Improve error message for NVHPC as host compiler [\#4227](https://github.com/kokkos/kokkos/pull/4227)
+- Update support for cuda reductions to work with types < 4bytes [\#4156](https://github.com/kokkos/kokkos/pull/4156)
+- Fix incompatible team size deduction in rare cases parallel_reduce [\#4142](https://github.com/kokkos/kokkos/pull/4142)
+- Remove UVM usage in DynamicView [\#4129](https://github.com/kokkos/kokkos/pull/4129)
+- Remove dependency between core and containers [\#4114](https://github.com/kokkos/kokkos/pull/4114)
+- Adding opt-in CudaMallocSync support when using CUDA version >= 11.2 [\#4026](https://github.com/kokkos/kokkos/pull/4026) [\#4233](https://github.com/kokkos/kokkos/pull/4233)
+- Fix a potential race condition in the CUDA backend [\#3999](https://github.com/kokkos/kokkos/pull/3999)
+
+#### HIP:
+- Implement new blocksize deduction method for HIP Backend [\#3953](https://github.com/kokkos/kokkos/pull/3953)
+- Add multiple LaunchMechanism [\#3820](https://github.com/kokkos/kokkos/pull/3820)
+- Make HIP backend thread-safe [\#4170](https://github.com/kokkos/kokkos/pull/4170)
+
+#### Serial:
+- Refactor Serial backend and fix thread-safety issue [\#4053](https://github.com/kokkos/kokkos/pull/4053)
+
+#### OpenMPTarget:
+- OpenMPTarget: support array reductions in RangePolicy [\#4040](https://github.com/kokkos/kokkos/pull/4040)
+- OpenMPTarget: add MDRange parallel_reduce [\#4032](https://github.com/kokkos/kokkos/pull/4032)
+- OpenMPTarget: Fix bug in for the case of a reducer. [\#4044](https://github.com/kokkos/kokkos/pull/4044)
+- OpenMPTarget: verify process fix [\#4041](https://github.com/kokkos/kokkos/pull/4041)
+
+### Implemented enhancements BuildSystem
+
+#### Important BuildSystem Updates:
+- Use hipcc architecture autodetection when Kokkos_ARCH is not set [\#3941](https://github.com/kokkos/kokkos/pull/3941)
+- Introduce Kokkos_ENABLE_DEPRECATION_WARNINGS and remove deprecated code with Kokkos_ENABLE_DEPRECATED_CODE_3 [\#4106](https://github.com/kokkos/kokkos/pull/4106) [\#3855](https://github.com/kokkos/kokkos/pull/3855)
+
+#### Other Improvements:
+- Add allow-unsupported-compiler flag to nvcc-wrapper [\#4298](https://github.com/kokkos/kokkos/pull/4298)
+- nvcc_wrapper: fix errors in argument handling [\#3993](https://github.com/kokkos/kokkos/pull/3993)
+- Adds support for -time=<file> and -time <file> in nvcc_wrapper [\#4015](https://github.com/kokkos/kokkos/pull/4015)
+- nvcc_wrapper: suppress duplicates of GPU architecture and RDC flags [\#3968](https://github.com/kokkos/kokkos/pull/3968)
+- Fix TMPDIR support in nvcc_wrapper [\#3792](https://github.com/kokkos/kokkos/pull/3792)
+- NVHPC: update PGI compiler arch flags [\#4133](https://github.com/kokkos/kokkos/pull/4133)
+- Replace PGI with NVHPC (works for both) [\#4196](https://github.com/kokkos/kokkos/pull/4196)
+- Make sure that KOKKOS_CXX_HOST_COMPILER_ID is defined [\#4235](https://github.com/kokkos/kokkos/pull/4235)
+- Add options to Makefile builds for deprecated code and warnings [\#4215](https://github.com/kokkos/kokkos/pull/4215)
+- Use KOKKOS_CXX_HOST_COMPILER_ID for identifying CPU arch flags [\#4199](https://github.com/kokkos/kokkos/pull/4199)
+- Added support for Cray Clang to Makefile.kokkos [\#4176](https://github.com/kokkos/kokkos/pull/4176)
+- Add XLClang as compiler [\#4120](https://github.com/kokkos/kokkos/pull/4120)
+- Keep quoted compiler flags when passing to Trilinos [\#3987](https://github.com/kokkos/kokkos/pull/3987)
+- Add support for AMD Zen3 CPU architecture [\#3972](https://github.com/kokkos/kokkos/pull/3972)
+- Rename IntelClang to IntelLLVM [\#3945](https://github.com/kokkos/kokkos/pull/3945)
+- Add cppcoreguidelines-pro-type-cstyle-cast to clang-tidy [\#3522](https://github.com/kokkos/kokkos/pull/3522)
+- Add sve bit size definition for A64FX [\#3947](https://github.com/kokkos/kokkos/pull/3947) [\#3946](https://github.com/kokkos/kokkos/pull/3946)
+- Remove KOKKOS_ENABLE_DEBUG_PRINT_KERNEL_NAMES [\#4150](https://github.com/kokkos/kokkos/pull/4150)
+
+### Other Changes:
+
+#### Tool Enhancements:
+
+- Retrieve original value from a point in a MultidimensionalSparseTuningProblem [\#3977](https://github.com/kokkos/kokkos/pull/3977)
+- Allow extension of built-in tuners with additional tuning axes [\#3961](https://github.com/kokkos/kokkos/pull/3961)
+- Added a categorical tuner [\#3955](https://github.com/kokkos/kokkos/pull/3955)
+
+
+#### Miscellaneous:
+
+- hpcbind: Use double quotes around $@ when invoking user command [\#4284](https://github.com/kokkos/kokkos/pull/4284)
+- Add file and line to error message [\#3985](https://github.com/kokkos/kokkos/pull/3985)
+- Fix compiler warnings when compiling with nvc++ [\#4198](https://github.com/kokkos/kokkos/pull/4198)
+- Add OpenMPTarget CI build on AMD GPUs [\#4055](https://github.com/kokkos/kokkos/pull/4055)
+- CI: icpx is now part of intel container [\#4002](https://github.com/kokkos/kokkos/pull/4002)
+
+### Incompatibilities:
+
+- Remove pre CUDA 9 KOKKOS_IMPL_CUDA_* macros [\#4138](https://github.com/kokkos/kokkos/pull/4138)
+
+### Bug Fixes:
+- UnorderedMap::clear() should zero the size() [\#4130](https://github.com/kokkos/kokkos/pull/4130)
+- Add memory fence for HostSharedPtr::cleanup() [\#4144](https://github.com/kokkos/kokkos/pull/4144)
+- SYCL: Fix race conditions in TeamPolicy::parallel_reduce [\#4418](https://github.com/kokkos/kokkos/pull/4418)
+- Adding missing memory fence to serial exec space fence. [\#4292](https://github.com/kokkos/kokkos/pull/4292)
+- Fix using external SYCL queues in tests [\#4291](https://github.com/kokkos/kokkos/pull/4291)
+- Fix digits10 bug [\#4281](https://github.com/kokkos/kokkos/pull/4281)
+- Fixes constexpr errors with frounding-math on gcc < 10. [\#4278](https://github.com/kokkos/kokkos/pull/4278)
+- Fix compiler flags for PGI/NVHPC [\#4264](https://github.com/kokkos/kokkos/pull/4264)
+- Fix Zen2/3 also implying Zen Arch with Makefiles [\#4260](https://github.com/kokkos/kokkos/pull/4260)
+- Kokkos_Cuda.hpp: Fix shadow warning with cuda/11.0 [\#4252](https://github.com/kokkos/kokkos/pull/4252)
+- Fix issue w/ static initialization of function attributes [\#4242](https://github.com/kokkos/kokkos/pull/4242)
+- Disable long double hypot test on Power systems [\#4221](https://github.com/kokkos/kokkos/pull/4221)
+- Fix false sharing in random pool [\#4218](https://github.com/kokkos/kokkos/pull/4218)
+- Fix a missing memory_fence for debug shared alloc code [\#4216](https://github.com/kokkos/kokkos/pull/4216)
+- Fix two xl issues [\#4179](https://github.com/kokkos/kokkos/pull/4179)
+- Makefile.kokkos: fix (standard_in) 1: syntax error [\#4173](https://github.com/kokkos/kokkos/pull/4173)
+- Fixes for query_device example [\#4172](https://github.com/kokkos/kokkos/pull/4172)
+- Fix a bug when using HIP atomic with Kokkos::Complex [\#4159](https://github.com/kokkos/kokkos/pull/4159)
+- Fix mistaken logic in pthread creation [\#4157](https://github.com/kokkos/kokkos/pull/4157)
+- Define KOKKOS_ENABLE_AGGRESSIVE_VECTORIZATION when requesting Kokkos_ENABLE_AGGRESSIVE_VECTORIZATION=ON [\#4107](https://github.com/kokkos/kokkos/pull/4107)
+- Fix compilation with latest MSVC version [\#4102](https://github.com/kokkos/kokkos/pull/4102)
+- Fix incorrect macro definitions when compiling with Intel compiler on Windows [\#4087](https://github.com/kokkos/kokkos/pull/4087)
+- Fixup global buffer overflow in hand rolled string manipulation [\#4070](https://github.com/kokkos/kokkos/pull/4070)
+- Fixup heap buffer overflow in cmd line args parsing unit tests [\#4069](https://github.com/kokkos/kokkos/pull/4069)
+- Only add quotes in compiler flags for Trilinos if necessary [\#4067](https://github.com/kokkos/kokkos/pull/4067)
+- Fixed invocation of tools init callbacks [\#4061](https://github.com/kokkos/kokkos/pull/4061)
+- Work around SYCL JIT compiler issues with static variables [\#4013](https://github.com/kokkos/kokkos/pull/4013)
+- Fix TestDetectionIdiom.cpp test inclusion for Trilinos/TriBITS [\#4010](https://github.com/kokkos/kokkos/pull/4010)
+- Fixup allocation headers with OpenMPTarget backend [\#4003](https://github.com/kokkos/kokkos/pull/4003)
+- Add missing specialization for OMPT to Kokkos Random [\#3967](https://github.com/kokkos/kokkos/pull/3967)
+- Disable hypot long double test on power arches [\#3962](https://github.com/kokkos/kokkos/pull/3962)
+- Use different EBO workaround for MSVC (rebased) [\#3924](https://github.com/kokkos/kokkos/pull/3924)
+- Fix SYCL Kokkos::Profiling::(de)allocateData calls [\#3928](https://github.com/kokkos/kokkos/pull/3928)
+
+## [3.4.01](https://github.com/kokkos/kokkos/tree/3.4.01) (2021-05-19)
+[Full Changelog](https://github.com/kokkos/kokkos/compare/3.4.00...3.4.01)
+
+**Bug Fixes:**
+- Windows: Remove atomic_compare_exchange_strong overload conflicts with Windows [\#4024](https://github.com/kokkos/kokkos/pull/4024)
+- OpenMPTarget: Fixup allocation headers with OpenMPTarget backend [\#4020](https://github.com/kokkos/kokkos/pull/4020)
+- OpenMPTarget: Add missing specailization for OMPT to Kokkos Random [\#4022](https://github.com/kokkos/kokkos/pull/4022)
+- AMD: Add support for AMD Zen3 CPU architecture [\#4021](https://github.com/kokkos/kokkos/pull/4021)
+- SYCL: Implement SYCL::print_configuration [\#4012](https://github.com/kokkos/kokkos/pull/4012)
+- Containers: staticcsrgraph: use device type instead of execution space to construct views [\#3998](https://github.com/kokkos/kokkos/pull/3998)
+- nvcc_wrapper: fix errors in argument handling, suppress duplicates of GPU architecture and RDC flags [\#4006](https://github.com/kokkos/kokkos/pull/4006)
+- CI: Add icpx testing to intel container [\#4004](https://github.com/kokkos/kokkos/pull/4004)
+- CMake/TRIBITS: Keep quoted compiler flags when passing to Trilinos [\#4007](https://github.com/kokkos/kokkos/pull/4007)
+- CMake: Rename IntelClang to IntelLLVM [\#3945](https://github.com/kokkos/kokkos/pull/3945)
+
 ## [3.4.00](https://github.com/kokkos/kokkos/tree/3.4.00) (2021-04-25)
 [Full Changelog](https://github.com/kokkos/kokkos/compare/3.3.01...3.4.00)
 
diff --git a/packages/kokkos/CMakeLists.txt b/packages/kokkos/CMakeLists.txt
index 6fc1bf7d2f7fd3b02a785b1184923cde07b438b2..1b6753f983db34e64bc1e10bfc3f008c6fec5ede 100644
--- a/packages/kokkos/CMakeLists.txt
+++ b/packages/kokkos/CMakeLists.txt
@@ -111,7 +111,7 @@ ENDIF()
 
 
 set(Kokkos_VERSION_MAJOR 3)
-set(Kokkos_VERSION_MINOR 4)
+set(Kokkos_VERSION_MINOR 5)
 set(Kokkos_VERSION_PATCH 00)
 set(Kokkos_VERSION "${Kokkos_VERSION_MAJOR}.${Kokkos_VERSION_MINOR}.${Kokkos_VERSION_PATCH}")
 math(EXPR KOKKOS_VERSION "${Kokkos_VERSION_MAJOR} * 10000 + ${Kokkos_VERSION_MINOR} * 100 + ${Kokkos_VERSION_PATCH}")
@@ -206,8 +206,18 @@ ENDIF()
 IF (KOKKOS_HAS_TRILINOS)
   # Overwrite the old flags at the top-level
   # Because Tribits doesn't use lists, it uses spaces for the list of CXX flags
-  # we have to match the annoying behavior
-  STRING(REPLACE ";" " " KOKKOSCORE_COMPILE_OPTIONS "${KOKKOS_COMPILE_OPTIONS}")
+  # we have to match the annoying behavior, also we have to preserve quotes
+  # which needs another workaround.
+  SET(KOKKOS_COMPILE_OPTIONS_TMP)
+  FOREACH(OPTION ${KOKKOS_COMPILE_OPTIONS})
+    STRING(FIND "${OPTION}" " " OPTION_HAS_WHITESPACE)
+    IF(OPTION_HAS_WHITESPACE EQUAL -1)
+      LIST(APPEND KOKKOS_COMPILE_OPTIONS_TMP "${OPTION}")
+    ELSE()
+      LIST(APPEND KOKKOS_COMPILE_OPTIONS_TMP "\"${OPTION}\"")
+    ENDIF()
+  ENDFOREACH()
+  STRING(REPLACE ";" " " KOKKOSCORE_COMPILE_OPTIONS "${KOKKOS_COMPILE_OPTIONS_TMP}")
   LIST(APPEND KOKKOS_ALL_COMPILE_OPTIONS ${KOKKOS_COMPILE_OPTIONS})
   IF (KOKKOS_ENABLE_CUDA)
     LIST(APPEND KOKKOS_ALL_COMPILE_OPTIONS ${KOKKOS_CUDA_OPTIONS})
diff --git a/packages/kokkos/Makefile.kokkos b/packages/kokkos/Makefile.kokkos
index 2599121d70ada48567c61fdc63ba94925a402267..7ab18f5894e8880bd0584a4815f2260e07772cfb 100644
--- a/packages/kokkos/Makefile.kokkos
+++ b/packages/kokkos/Makefile.kokkos
@@ -1,20 +1,20 @@
 # Default settings common options.
 
 KOKKOS_VERSION_MAJOR = 3
-KOKKOS_VERSION_MINOR = 4
+KOKKOS_VERSION_MINOR = 5
 KOKKOS_VERSION_PATCH = 00
 KOKKOS_VERSION = $(shell echo $(KOKKOS_VERSION_MAJOR)*10000+$(KOKKOS_VERSION_MINOR)*100+$(KOKKOS_VERSION_PATCH) | bc)
 
-# Options: Cuda,HIP,OpenMP,Pthread,Serial
+# Options: Cuda,HIP,SYCL,OpenMPTarget,OpenMP,Pthread,Serial
 #KOKKOS_DEVICES ?= "OpenMP"
 KOKKOS_DEVICES ?= "Pthread"
-# Options: 
+# Options:
 # Intel:    KNC,KNL,SNB,HSW,BDW,SKX
 # NVIDIA:   Kepler,Kepler30,Kepler32,Kepler35,Kepler37,Maxwell,Maxwell50,Maxwell52,Maxwell53,Pascal60,Pascal61,Volta70,Volta72,Turing75,Ampere80,Ampere86
 # ARM:      ARMv80,ARMv81,ARMv8-ThunderX,ARMv8-TX2,A64FX
 # IBM:      BGQ,Power7,Power8,Power9
-# AMD-GPUS: Vega900,Vega906,Vega908
-# AMD-CPUS: AMDAVX,Zen,Zen2
+# AMD-GPUS: Vega900,Vega906,Vega908,Vega90A
+# AMD-CPUS: AMDAVX,Zen,Zen2,Zen3
 KOKKOS_ARCH ?= ""
 # Options: yes,no
 KOKKOS_DEBUG ?= "no"
@@ -22,7 +22,7 @@ KOKKOS_DEBUG ?= "no"
 KOKKOS_USE_TPLS ?= ""
 # Options: c++14,c++1y,c++17,c++1z,c++2a
 KOKKOS_CXX_STANDARD ?= "c++14"
-# Options: aggressive_vectorization,disable_profiling,enable_large_mem_tests,disable_complex_align
+# Options: aggressive_vectorization,disable_profiling,enable_large_mem_tests,disable_complex_align,disable_deprecated_code,enable_deprecation_warnings
 KOKKOS_OPTIONS ?= ""
 KOKKOS_CMAKE ?= "no"
 KOKKOS_TRIBITS ?= "no"
@@ -70,7 +70,7 @@ KOKKOS_INTERNAL_USE_MEMKIND := $(call kokkos_has_string,$(KOKKOS_USE_TPLS),exper
 
 # Check for advanced settings.
 KOKKOS_INTERNAL_ENABLE_COMPILER_WARNINGS := $(call kokkos_has_string,$(KOKKOS_OPTIONS),compiler_warnings)
-KOKKOS_INTERNAL_OPT_RANGE_AGGRESSIVE_VECTORIZATION := $(call kokkos_has_string,$(KOKKOS_OPTIONS),aggressive_vectorization)
+KOKKOS_INTERNAL_AGGRESSIVE_VECTORIZATION := $(call kokkos_has_string,$(KOKKOS_OPTIONS),aggressive_vectorization)
 KOKKOS_INTERNAL_ENABLE_TUNING := $(call kokkos_has_string,$(KOKKOS_OPTIONS),enable_tuning)
 KOKKOS_INTERNAL_DISABLE_COMPLEX_ALIGN := $(call kokkos_has_string,$(KOKKOS_OPTIONS),disable_complex_align)
 KOKKOS_INTERNAL_DISABLE_DUALVIEW_MODIFY_CHECK := $(call kokkos_has_string,$(KOKKOS_OPTIONS),disable_dualview_modify_check)
@@ -82,6 +82,9 @@ KOKKOS_INTERNAL_CUDA_USE_RELOC := $(call kokkos_has_string,$(KOKKOS_CUDA_OPTIONS
 KOKKOS_INTERNAL_CUDA_USE_LAMBDA := $(call kokkos_has_string,$(KOKKOS_CUDA_OPTIONS),enable_lambda)
 KOKKOS_INTERNAL_CUDA_USE_CONSTEXPR := $(call kokkos_has_string,$(KOKKOS_CUDA_OPTIONS),enable_constexpr)
 KOKKOS_INTERNAL_HPX_ENABLE_ASYNC_DISPATCH := $(call kokkos_has_string,$(KOKKOS_HPX_OPTIONS),enable_async_dispatch)
+KOKKOS_INTERNAL_ENABLE_DESUL_ATOMICS := $(call kokkos_has_string,$(KOKKOS_OPTIONS),enable_desul_atomics)
+KOKKOS_INTERNAL_DISABLE_DEPRECATED_CODE := $(call kokkos_has_string,$(KOKKOS_OPTIONS),disable_deprecated_code)
+KOKKOS_INTERNAL_ENABLE_DEPRECATION_WARNINGS := $(call kokkos_has_string,$(KOKKOS_OPTIONS),enable_deprecation_warnings)
 
 KOKKOS_INTERNAL_HIP_USE_RELOC := $(call kokkos_has_string,$(KOKKOS_HIP_OPTIONS),rdc)
 
@@ -102,6 +105,7 @@ endif
 # Check for other Execution Spaces.
 KOKKOS_INTERNAL_USE_CUDA := $(call kokkos_has_string,$(KOKKOS_DEVICES),Cuda)
 KOKKOS_INTERNAL_USE_HIP := $(call kokkos_has_string,$(KOKKOS_DEVICES),HIP)
+KOKKOS_INTERNAL_USE_SYCL := $(call kokkos_has_string,$(KOKKOS_DEVICES),SYCL)
 KOKKOS_INTERNAL_USE_OPENMPTARGET := $(call kokkos_has_string,$(KOKKOS_DEVICES),OpenMPTarget)
 
 KOKKOS_DEVICELIST =
@@ -123,11 +127,18 @@ endif
 ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1)
   KOKKOS_DEVICELIST += HIP
 endif
+KOKKOS_INTERNAL_HAVE_CXX17_OR_NEWER := $(shell expr $(KOKKOS_INTERNAL_ENABLE_CXX17) \
+                                                  + $(KOKKOS_INTERNAL_ENABLE_CXX20) \
+                                                  + $(KOKKOS_INTERNAL_ENABLE_CXX2A))
+ifeq ($(KOKKOS_INTERNAL_USE_SYCL), 1)
+  KOKKOS_DEVICELIST += SYCL
+  ifneq ($(KOKKOS_INTERNAL_HAVE_CXX17_OR_NEWER), 1)
+    $(error SYCL backend requires C++17 or newer)
+  endif
+
+endif
 ifeq ($(KOKKOS_INTERNAL_USE_OPENMPTARGET), 1)
   KOKKOS_DEVICELIST += OPENMPTARGET
-  KOKKOS_INTERNAL_HAVE_CXX17_OR_NEWER := $(shell expr $(KOKKOS_INTERNAL_ENABLE_CXX17) \
-                                                    + $(KOKKOS_INTERNAL_ENABLE_CXX20) \
-                                                    + $(KOKKOS_INTERNAL_ENABLE_CXX2A))
   ifneq ($(KOKKOS_INTERNAL_HAVE_CXX17_OR_NEWER), 1)
     $(error OpenMPTarget backend requires C++17 or newer)
   endif
@@ -158,6 +169,8 @@ KOKKOS_INTERNAL_COMPILER_XL          := $(strip $(shell $(CXX) -qversion       2
 KOKKOS_INTERNAL_COMPILER_CRAY        := $(strip $(shell $(CXX) -craype-verbose 2>&1 | grep -c "CC-"))
 KOKKOS_INTERNAL_COMPILER_NVCC        := $(strip $(shell echo "$(shell export OMPI_CXX=$(OMPI_CXX); export MPICH_CXX=$(MPICH_CXX); $(CXX) --version 2>&1 | grep -c nvcc)>0" | bc))
 KOKKOS_INTERNAL_COMPILER_CLANG       := $(call kokkos_has_string,$(KOKKOS_CXX_VERSION),clang)
+KOKKOS_INTERNAL_COMPILER_CRAY_CLANG  := $(strip $(shell $(CXX) -craype-verbose 2>&1 | grep -c "clang++"))
+KOKKOS_INTERNAL_COMPILER_INTEL_CLANG := $(call kokkos_has_string,$(KOKKOS_CXX_VERSION),oneAPI)
 KOKKOS_INTERNAL_COMPILER_APPLE_CLANG := $(call kokkos_has_string,$(KOKKOS_CXX_VERSION),Apple clang)
 KOKKOS_INTERNAL_COMPILER_HCC         := $(call kokkos_has_string,$(KOKKOS_CXX_VERSION),HCC)
 KOKKOS_INTERNAL_COMPILER_GCC         := $(call kokkos_has_string,$(KOKKOS_CXX_VERSION),GCC)
@@ -237,7 +250,11 @@ ifeq ($(KOKKOS_INTERNAL_COMPILER_PGI), 1)
   KOKKOS_INTERNAL_OPENMP_FLAG := -mp
 else
   ifeq ($(KOKKOS_INTERNAL_COMPILER_CLANG), 1)
+    ifeq ($(KOKKOS_INTERNAL_COMPILER_CRAY_CLANG), 1)
+    KOKKOS_INTERNAL_OPENMP_FLAG := -fopenmp
+    else
     KOKKOS_INTERNAL_OPENMP_FLAG := -fopenmp=libomp
+    endif
   else
     ifeq ($(KOKKOS_INTERNAL_COMPILER_APPLE_CLANG), 1)
       KOKKOS_INTERNAL_OPENMP_FLAG := -fopenmp=libomp
@@ -249,7 +266,11 @@ else
           # OpenMP is turned on by default in Cray compiler environment.
           KOKKOS_INTERNAL_OPENMP_FLAG :=
         else
-          KOKKOS_INTERNAL_OPENMP_FLAG := -fopenmp
+          ifeq ($(KOKKOS_INTERNAL_COMPILER_INTEL_CLANG), 1)
+            KOKKOS_INTERNAL_OPENMP_FLAG := -fiopenmp
+          else
+            KOKKOS_INTERNAL_OPENMP_FLAG := -fopenmp
+          endif
         endif
       endif
     endif
@@ -307,6 +328,13 @@ KOKKOS_INTERNAL_USE_ARCH_BDW := $(call kokkos_has_string,$(KOKKOS_ARCH),BDW)
 KOKKOS_INTERNAL_USE_ARCH_SKX := $(call kokkos_has_string,$(KOKKOS_ARCH),SKX)
 KOKKOS_INTERNAL_USE_ARCH_KNL := $(call kokkos_has_string,$(KOKKOS_ARCH),KNL)
 
+KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen)
+KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN9 := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen9)
+KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN11 := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen11)
+KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN12LP := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelGen12LP)
+KOKKOS_INTERNAL_USE_ARCH_INTEL_DG1 := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelDG1)
+KOKKOS_INTERNAL_USE_ARCH_INTEL_XEHP := $(call kokkos_has_string,$(KOKKOS_ARCH),IntelXeHP)
+
 # NVIDIA based.
 NVCC_WRAPPER := $(KOKKOS_PATH)/bin/nvcc_wrapper
 KOKKOS_INTERNAL_USE_ARCH_KEPLER30 := $(call kokkos_has_string,$(KOKKOS_ARCH),Kepler30)
@@ -372,21 +400,27 @@ KOKKOS_INTERNAL_USE_ARCH_IBM := $(strip $(shell echo $(KOKKOS_INTERNAL_USE_ARCH_
 
 # AMD based.
 KOKKOS_INTERNAL_USE_ARCH_AMDAVX := $(call kokkos_has_string,$(KOKKOS_ARCH),AMDAVX)
+KOKKOS_INTERNAL_USE_ARCH_ZEN3 := $(call kokkos_has_string,$(KOKKOS_ARCH),Zen3)
 KOKKOS_INTERNAL_USE_ARCH_ZEN2 := $(call kokkos_has_string,$(KOKKOS_ARCH),Zen2)
-KOKKOS_INTERNAL_USE_ARCH_ZEN := $(call kokkos_has_string,$(KOKKOS_ARCH),Zen)
+ifeq ($(KOKKOS_INTERNAL_USE_ARCH_ZEN3), 0)
+  ifeq ($(KOKKOS_INTERNAL_USE_ARCH_ZEN2), 0)
+    KOKKOS_INTERNAL_USE_ARCH_ZEN := $(call kokkos_has_string,$(KOKKOS_ARCH),Zen)
+  endif
+endif
 KOKKOS_INTERNAL_USE_ARCH_VEGA900 := $(call kokkos_has_string,$(KOKKOS_ARCH),Vega900)
 KOKKOS_INTERNAL_USE_ARCH_VEGA906 := $(call kokkos_has_string,$(KOKKOS_ARCH),Vega906)
 KOKKOS_INTERNAL_USE_ARCH_VEGA908 := $(call kokkos_has_string,$(KOKKOS_ARCH),Vega908)
+KOKKOS_INTERNAL_USE_ARCH_VEGA90A := $(call kokkos_has_string,$(KOKKOS_ARCH),Vega90A)
 
 # Any AVX?
 KOKKOS_INTERNAL_USE_ARCH_SSE42      := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_WSM))
 KOKKOS_INTERNAL_USE_ARCH_AVX        := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_SNB) + $(KOKKOS_INTERNAL_USE_ARCH_AMDAVX))
-KOKKOS_INTERNAL_USE_ARCH_AVX2       := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_HSW) + $(KOKKOS_INTERNAL_USE_ARCH_BDW) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN2))
+KOKKOS_INTERNAL_USE_ARCH_AVX2       := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_HSW) + $(KOKKOS_INTERNAL_USE_ARCH_BDW) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN2) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN3))
 KOKKOS_INTERNAL_USE_ARCH_AVX512MIC  := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_KNL))
 KOKKOS_INTERNAL_USE_ARCH_AVX512XEON := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_SKX))
 
 # Decide what ISA level we are able to support.
-KOKKOS_INTERNAL_USE_ISA_X86_64    := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_WSM) + $(KOKKOS_INTERNAL_USE_ARCH_SNB) + $(KOKKOS_INTERNAL_USE_ARCH_HSW) + $(KOKKOS_INTERNAL_USE_ARCH_BDW) + $(KOKKOS_INTERNAL_USE_ARCH_KNL) + $(KOKKOS_INTERNAL_USE_ARCH_SKX) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN2))
+KOKKOS_INTERNAL_USE_ISA_X86_64    := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_WSM) + $(KOKKOS_INTERNAL_USE_ARCH_SNB) + $(KOKKOS_INTERNAL_USE_ARCH_HSW) + $(KOKKOS_INTERNAL_USE_ARCH_BDW) + $(KOKKOS_INTERNAL_USE_ARCH_KNL) + $(KOKKOS_INTERNAL_USE_ARCH_SKX) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN2) + $(KOKKOS_INTERNAL_USE_ARCH_ZEN3))
 KOKKOS_INTERNAL_USE_ISA_KNC       := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_KNC))
 KOKKOS_INTERNAL_USE_ISA_POWERPCLE := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_POWER8) + $(KOKKOS_INTERNAL_USE_ARCH_POWER9))
 KOKKOS_INTERNAL_USE_ISA_POWERPCBE := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_POWER7))
@@ -395,8 +429,8 @@ KOKKOS_INTERNAL_USE_ISA_POWERPCBE := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_POW
 KOKKOS_INTERNAL_USE_TM            := $(shell expr $(KOKKOS_INTERNAL_USE_ARCH_BDW) + $(KOKKOS_INTERNAL_USE_ARCH_SKX))
 
 # Incompatible flags?
-KOKKOS_INTERNAL_USE_ARCH_MULTIHOST := $(strip $(shell echo "$(KOKKOS_INTERNAL_USE_ARCH_SSE42)+$(KOKKOS_INTERNAL_USE_ARCH_AVX)+$(KOKKOS_INTERNAL_USE_ARCH_AVX2)+$(KOKKOS_INTERNAL_USE_ARCH_AVX512MIC)+$(KOKKOS_INTERNAL_USE_ARCH_AVX512XEON)+$(KOKKOS_INTERNAL_USE_ARCH_KNC)+$(KOKKOS_INTERNAL_USE_ARCH_IBM)+$(KOKKOS_INTERNAL_USE_ARCH_ARM)>1" | bc ))
-KOKKOS_INTERNAL_USE_ARCH_MULTIGPU := $(strip $(shell echo "$(KOKKOS_INTERNAL_USE_ARCH_NVIDIA)>1" | bc))
+KOKKOS_INTERNAL_USE_ARCH_MULTIHOST := $(strip $(shell echo "$(KOKKOS_INTERNAL_USE_ARCH_SSE42)+$(KOKKOS_INTERNAL_USE_ARCH_AVX)+$(KOKKOS_INTERNAL_USE_ARCH_AVX2)+$(KOKKOS_INTERNAL_USE_ARCH_AVX512MIC)+$(KOKKOS_INTERNAL_USE_ARCH_AVX512XEON)+$(KOKKOS_INTERNAL_USE_ARCH_KNC)+$(KOKKOS_INTERNAL_USE_ARCH_IBM)+$(KOKKOS_INTERNAL_USE_ARCH_ARM)>1") | bc)
+KOKKOS_INTERNAL_USE_ARCH_MULTIGPU := $(strip $(shell echo "$(KOKKOS_INTERNAL_USE_ARCH_NVIDIA)>1") | bc)
 
 ifeq ($(KOKKOS_INTERNAL_USE_ARCH_MULTIHOST), 1)
   $(error Defined Multiple Host architectures: KOKKOS_ARCH=$(KOKKOS_ARCH) )
@@ -431,6 +465,10 @@ KOKKOS_LINK_FLAGS =
 KOKKOS_SRC =
 KOKKOS_HEADERS =
 
+#ifeq ($(KOKKOS_INTERNAL_COMPILER_GCC), 1)
+  KOKKOS_LIBS += -latomic
+#endif
+
 # Generating the KokkosCore_config.h file.
 
 KOKKOS_INTERNAL_CONFIG_TMP=KokkosCore_config.tmp
@@ -467,6 +505,10 @@ ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1)
   tmp := $(call kokkos_append_header,'$H''define KOKKOS_ENABLE_HIP')
 endif
 
+ifeq ($(KOKKOS_INTERNAL_USE_SYCL), 1)
+  tmp := $(call kokkos_append_header,'$H''define KOKKOS_ENABLE_SYCL')
+endif
+
 ifeq ($(KOKKOS_INTERNAL_USE_OPENMPTARGET), 1)
   tmp := $(call kokkos_append_header,'$H''define KOKKOS_ENABLE_OPENMPTARGET')
   ifeq ($(KOKKOS_INTERNAL_COMPILER_GCC), 1)
@@ -522,6 +564,12 @@ endif
 
 #only add the c++ standard flags if this is not CMake
 tmp := $(call kokkos_append_header,"/* General Settings */")
+ifneq ($(KOKKOS_INTERNAL_DISABLE_DEPRECATED_CODE), 1)
+  tmp := $(call kokkos_append_header,"$H""define KOKKOS_ENABLE_DEPRECATED_CODE_3")
+endif
+ifeq ($(KOKKOS_INTERNAL_ENABLE_DEPRECATION_WARNINGS), 1)
+  tmp := $(call kokkos_append_header,"$H""define KOKKOS_ENABLE_DEPRECATION_WARNINGS")
+endif
 ifeq ($(KOKKOS_INTERNAL_ENABLE_CXX14), 1)
 ifneq ($(KOKKOS_STANDALONE_CMAKE), yes)
   KOKKOS_CXXFLAGS += $(KOKKOS_INTERNAL_CXX14_FLAG)
@@ -624,8 +672,10 @@ endif
 
 tmp := $(call kokkos_append_header,"/* Optimization Settings */")
 
-ifeq ($(KOKKOS_INTERNAL_OPT_RANGE_AGGRESSIVE_VECTORIZATION), 1)
+ifeq ($(KOKKOS_INTERNAL_AGGRESSIVE_VECTORIZATION), 1)
+  # deprecated
   tmp := $(call kokkos_append_header,"$H""define KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION")
+  tmp := $(call kokkos_append_header,"$H""define KOKKOS_ENABLE_AGGRESSIVE_VECTORIZATION")
 endif
 
 tmp := $(call kokkos_append_header,"/* Cuda Settings */")
@@ -780,6 +830,19 @@ ifeq ($(KOKKOS_INTERNAL_USE_ARCH_ZEN2), 1)
   endif
 endif
 
+ifeq ($(KOKKOS_INTERNAL_USE_ARCH_ZEN3), 1)
+  tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_AMD_ZEN3")
+  tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_AMD_AVX2")
+
+  ifeq ($(KOKKOS_INTERNAL_COMPILER_INTEL), 1)
+    KOKKOS_CXXFLAGS += -mavx2
+    KOKKOS_LDFLAGS += -mavx2
+  else
+    KOKKOS_CXXFLAGS += -march=znver3 -mtune=znver3
+    KOKKOS_LDFLAGS += -march=znver3 -mtune=znver3
+  endif
+endif
+
 ifeq ($(KOKKOS_INTERNAL_USE_ARCH_ARMV8_THUNDERX), 1)
   tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_ARMV80")
   tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_ARMV8_THUNDERX")
@@ -1142,6 +1205,11 @@ ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1)
     tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_VEGA908")
     KOKKOS_INTERNAL_HIP_ARCH_FLAG := --amdgpu-target=gfx908
   endif
+  ifeq ($(KOKKOS_INTERNAL_USE_ARCH_VEGA90A), 1)
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_HIP 90A")
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_VEGA90A")
+    KOKKOS_INTERNAL_HIP_ARCH_FLAG := --amdgpu-target=gfx90a
+  endif
 
 
   KOKKOS_SRC += $(wildcard $(KOKKOS_PATH)/core/src/HIP/*.cpp)
@@ -1160,6 +1228,52 @@ ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1)
   endif
 endif
 
+# Figure out the architecture flag for SYCL.
+ifeq ($(KOKKOS_INTERNAL_USE_SYCL), 1)
+  # Lets start with adding architecture defines
+  ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN), 1)
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GPU")
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GEN")
+    KOKKOS_INTERNAL_SYCL_ARCH_FLAG := -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device gen9-"
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN9), 1)
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GPU")
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GEN9")
+    KOKKOS_INTERNAL_SYCL_ARCH_FLAG := -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device gen9"
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN11), 1)
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GPU")
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GEN11")
+    KOKKOS_INTERNAL_SYCL_ARCH_FLAG := -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device gen11"
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_GEN12LP), 1)
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GPU")
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GEN12LP")
+    KOKKOS_INTERNAL_SYCL_ARCH_FLAG := -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device gen12lp"
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_DG1), 1)
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GPU")
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_DG1")
+    KOKKOS_INTERNAL_SYCL_ARCH_FLAG := -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device dg1"
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_ARCH_INTEL_XEHP), 1)
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_GPU")
+    tmp := $(call kokkos_append_header,"$H""define KOKKOS_ARCH_INTEL_XEHP")
+    KOKKOS_INTERNAL_SYCL_ARCH_FLAG := -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device xehp"
+  endif
+
+  KOKKOS_SRC += $(wildcard $(KOKKOS_PATH)/core/src/SYCL/*.cpp)
+  KOKKOS_HEADERS += $(wildcard $(KOKKOS_PATH)/core/src/SYCL/*.hpp)
+
+  KOKKOS_CXXFLAGS+=-fsycl -fno-sycl-id-queries-fit-in-int -fsycl-unnamed-lambda
+  KOKKOS_CXXFLAGS+=$(KOKKOS_INTERNAL_SYCL_ARCH_FLAG)
+  KOKKOS_LDFLAGS+=-fsycl
+  KOKKOS_LDFLAGS+=$(KOKKOS_INTERNAL_SYCL_ARCH_FLAG)
+endif
+
+ifeq ($(KOKKOS_INTERNAL_ENABLE_DESUL_ATOMICS), 1)
+  tmp := $(call kokkos_append_header,"$H""define KOKKOS_ENABLE_IMPL_DESUL_ATOMICS")
+endif
 
 KOKKOS_INTERNAL_LS_CONFIG := $(shell ls KokkosCore_config.h 2>&1)
 
@@ -1171,57 +1285,63 @@ endif
 
 ifneq ($(KOKKOS_INTERNAL_NEW_CONFIG), 0)
   tmp := $(shell cp KokkosCore_config.tmp KokkosCore_config.h)
-endif
 
-# Functions for generating config header file
-kokkos_start_config_header = $(shell sed 's~@INCLUDE_NEXT_FILE@~~g' $(KOKKOS_PATH)/cmake/KokkosCore_Config_HeaderSet.in > $1)
-kokkos_update_config_header = $(shell sed 's~@HEADER_GUARD_TAG@~$1~g' $2 > $3)
-kokkos_append_config_header = $(shell echo $1 >> $2))
-tmp := $(call kokkos_start_config_header, "KokkosCore_Config_FwdBackend.tmp")
-tmp := $(call kokkos_start_config_header, "KokkosCore_Config_SetupBackend.tmp")
-tmp := $(call kokkos_start_config_header, "KokkosCore_Config_DeclareBackend.tmp")
-tmp := $(call kokkos_start_config_header, "KokkosCore_Config_PostInclude.tmp")
-tmp := $(call kokkos_update_config_header, KOKKOS_FWD_HPP_, "KokkosCore_Config_FwdBackend.tmp", "KokkosCore_Config_FwdBackend.hpp")
-tmp := $(call kokkos_update_config_header, KOKKOS_SETUP_HPP_, "KokkosCore_Config_SetupBackend.tmp", "KokkosCore_Config_SetupBackend.hpp")
-tmp := $(call kokkos_update_config_header, KOKKOS_DECLARE_HPP_, "KokkosCore_Config_DeclareBackend.tmp", "KokkosCore_Config_DeclareBackend.hpp")
-tmp := $(call kokkos_update_config_header, KOKKOS_POST_INCLUDE_HPP_, "KokkosCore_Config_PostInclude.tmp", "KokkosCore_Config_PostInclude.hpp")
-ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1)
-   tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_CUDA.hpp>","KokkosCore_Config_FwdBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_CUDA.hpp>","KokkosCore_Config_DeclareBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <setup/Kokkos_Setup_Cuda.hpp>","KokkosCore_Config_SetupBackend.hpp")
-   ifeq ($(KOKKOS_INTERNAL_CUDA_USE_UVM), 1)
-   else
-   endif
-endif
-ifeq ($(KOKKOS_INTERNAL_USE_OPENMPTARGET), 1)
-   tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_OPENMPTARGET.hpp>","KokkosCore_Config_FwdBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_OPENMPTARGET.hpp>","KokkosCore_Config_DeclareBackend.hpp")
-endif
-ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1)
-   tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_HIP.hpp>","KokkosCore_Config_FwdBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_HIP.hpp>","KokkosCore_Config_DeclareBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <setup/Kokkos_Setup_HIP.hpp>","KokkosCore_Config_SetupBackend.hpp")
-endif
-ifeq ($(KOKKOS_INTERNAL_USE_OPENMP), 1)
-   tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_OPENMP.hpp>","KokkosCore_Config_FwdBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_OPENMP.hpp>","KokkosCore_Config_DeclareBackend.hpp")
-endif
-ifeq ($(KOKKOS_INTERNAL_USE_PTHREADS), 1)
-   tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_THREADS.hpp>","KokkosCore_Config_FwdBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_THREADS.hpp>","KokkosCore_Config_DeclareBackend.hpp")
-endif
-ifeq ($(KOKKOS_INTERNAL_USE_HPX), 1)
-   tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_HPX.hpp>","KokkosCore_Config_FwdBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_HPX.hpp>","KokkosCore_Config_DeclareBackend.hpp")
-endif
-ifeq ($(KOKKOS_INTERNAL_USE_SERIAL), 1)
-   tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_SERIAL.hpp>","KokkosCore_Config_FwdBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_SERIAL.hpp>","KokkosCore_Config_DeclareBackend.hpp")
-endif
-ifeq ($(KOKKOS_INTERNAL_USE_MEMKIND), 1)
-   tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_HBWSpace.hpp>","KokkosCore_Config_FwdBackend.hpp")
-   tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_HBWSpace.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+  # Functions for generating config header file
+  kokkos_start_config_header = $(shell sed 's~@INCLUDE_NEXT_FILE@~~g' $(KOKKOS_PATH)/cmake/KokkosCore_Config_HeaderSet.in > $1)
+  kokkos_update_config_header = $(shell sed 's~@HEADER_GUARD_TAG@~$1~g' $2 > $3)
+  kokkos_append_config_header = $(shell echo $1 >> $2))
+  tmp := $(call kokkos_start_config_header, "KokkosCore_Config_FwdBackend.tmp")
+  tmp := $(call kokkos_start_config_header, "KokkosCore_Config_SetupBackend.tmp")
+  tmp := $(call kokkos_start_config_header, "KokkosCore_Config_DeclareBackend.tmp")
+  tmp := $(call kokkos_start_config_header, "KokkosCore_Config_PostInclude.tmp")
+  tmp := $(call kokkos_update_config_header, KOKKOS_FWD_HPP_, "KokkosCore_Config_FwdBackend.tmp", "KokkosCore_Config_FwdBackend.hpp")
+  tmp := $(call kokkos_update_config_header, KOKKOS_SETUP_HPP_, "KokkosCore_Config_SetupBackend.tmp", "KokkosCore_Config_SetupBackend.hpp")
+  tmp := $(call kokkos_update_config_header, KOKKOS_DECLARE_HPP_, "KokkosCore_Config_DeclareBackend.tmp", "KokkosCore_Config_DeclareBackend.hpp")
+  tmp := $(call kokkos_update_config_header, KOKKOS_POST_INCLUDE_HPP_, "KokkosCore_Config_PostInclude.tmp", "KokkosCore_Config_PostInclude.hpp")
+  ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1)
+    tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_CUDA.hpp>","KokkosCore_Config_FwdBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_CUDA.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <setup/Kokkos_Setup_Cuda.hpp>","KokkosCore_Config_SetupBackend.hpp")
+    ifeq ($(KOKKOS_INTERNAL_CUDA_USE_UVM), 1)
+    else
+    endif
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_OPENMPTARGET), 1)
+    tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_OPENMPTARGET.hpp>","KokkosCore_Config_FwdBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_OPENMPTARGET.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_SYCL), 1)
+    tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_SYCL.hpp>","KokkosCore_Config_FwdBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_SYCL.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <setup/Kokkos_Setup_SYCL.hpp>","KokkosCore_Config_SetupBackend.hpp")
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1)
+    tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_HIP.hpp>","KokkosCore_Config_FwdBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_HIP.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <setup/Kokkos_Setup_HIP.hpp>","KokkosCore_Config_SetupBackend.hpp")
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_OPENMP), 1)
+    tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_OPENMP.hpp>","KokkosCore_Config_FwdBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_OPENMP.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_PTHREADS), 1)
+    tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_THREADS.hpp>","KokkosCore_Config_FwdBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_THREADS.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_HPX), 1)
+    tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_HPX.hpp>","KokkosCore_Config_FwdBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_HPX.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_SERIAL), 1)
+    tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_SERIAL.hpp>","KokkosCore_Config_FwdBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_SERIAL.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+  endif
+  ifeq ($(KOKKOS_INTERNAL_USE_MEMKIND), 1)
+    tmp := $(call kokkos_append_config_header,"$H""include <fwd/Kokkos_Fwd_HBWSpace.hpp>","KokkosCore_Config_FwdBackend.hpp")
+    tmp := $(call kokkos_append_config_header,"$H""include <decl/Kokkos_Declare_HBWSpace.hpp>","KokkosCore_Config_DeclareBackend.hpp")
+  endif
 endif
+
 KOKKOS_HEADERS += $(wildcard $(KOKKOS_PATH)/core/src/*.hpp)
 KOKKOS_HEADERS += $(wildcard $(KOKKOS_PATH)/core/src/impl/*.hpp)
 KOKKOS_HEADERS += $(wildcard $(KOKKOS_PATH)/containers/src/*.hpp)
@@ -1233,6 +1353,9 @@ KOKKOS_SRC += $(wildcard $(KOKKOS_PATH)/containers/src/impl/*.cpp)
 
 ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1)
   KOKKOS_SRC += $(wildcard $(KOKKOS_PATH)/core/src/Cuda/*.cpp)
+  ifeq ($(KOKKOS_INTERNAL_ENABLE_DESUL_ATOMICS), 1)
+    KOKKOS_SRC += $(KOKKOS_PATH)/core/src/desul/src/Lock_Array_CUDA.cpp
+  endif
   KOKKOS_HEADERS += $(wildcard $(KOKKOS_PATH)/core/src/Cuda/*.hpp)
   ifneq ($(CUDA_PATH),)
     KOKKOS_CPPLAGS += -I$(CUDA_PATH)/include
diff --git a/packages/kokkos/Makefile.targets b/packages/kokkos/Makefile.targets
index cf9fc242420e1dbbb519b3312cf1a4c3b4354738..93854d0cf150c97d5058422b7ca9ff28ce2ba8b6 100644
--- a/packages/kokkos/Makefile.targets
+++ b/packages/kokkos/Makefile.targets
@@ -48,6 +48,17 @@ Kokkos_Cuda_Task.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/Cuda/Kokkos_Cu
 	$(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/Cuda/Kokkos_Cuda_Task.cpp
 Kokkos_Cuda_Locks.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/Cuda/Kokkos_Cuda_Locks.cpp
 	$(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/Cuda/Kokkos_Cuda_Locks.cpp
+Lock_Array_CUDA.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/desul/src/Lock_Array_CUDA.cpp
+	$(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/desul/src/Lock_Array_CUDA.cpp
+endif
+
+ifeq ($(KOKKOS_INTERNAL_USE_SYCL), 1)
+Kokkos_SYCL.o : $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/SYCL/Kokkos_SYCL.cpp
+	$(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/SYCL/Kokkos_SYCL.cpp
+Kokkos_SYCL_Space.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/SYCL/Kokkos_SYCL_Space.cpp
+	$(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/SYCL/Kokkos_SYCL_Space.cpp
+Kokkos_SYCL_Instance.o: $(KOKKOS_CPP_DEPENDS) $(KOKKOS_PATH)/core/src/SYCL/Kokkos_SYCL_Instance.cpp
+	$(CXX) $(KOKKOS_CPPFLAGS) $(KOKKOS_CXXFLAGS) $(CXXFLAGS) -c $(KOKKOS_PATH)/core/src/SYCL/Kokkos_SYCL_Instance.cpp
 endif
 
 ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1)
diff --git a/packages/kokkos/README.md b/packages/kokkos/README.md
index d55ef2caac93ae6803aa97925ea7b081d3f05ca3..673f4627125223f0d09f1e184d9942c0e6a0b7ff 100644
--- a/packages/kokkos/README.md
+++ b/packages/kokkos/README.md
@@ -7,7 +7,7 @@ applications targeting all major HPC platforms. For that purpose it provides
 abstractions for both parallel execution of code and data management.
 Kokkos is designed to target complex node architectures with N-level memory
 hierarchies and multiple types of execution resources. It currently can use
-CUDA, HPX, OpenMP and Pthreads as backend programming models with several other
+CUDA, HIP, SYCL, HPX, OpenMP and C++ threads as backend programming models with several other
 backends in development.
 
 Kokkos Core is part of the Kokkos C++ Performance Portability Programming EcoSystem,
@@ -16,29 +16,19 @@ profiling and debugging tools (https://github.com/kokkos/kokkos-tools).
 
 # Learning about Kokkos
 
-A programming guide can be found on the Wiki, the API reference is under development.
+The best way to start learning about Kokkos is going through the Kokkos Lectures.
+They are online available at https://kokkos.link/the-lectures and contain a mix
+of lecture videos and hands-on exercises covering all the important Kokkos Ecosystem
+capabilities.
+
+A programming guide and API reference can be found on the Wiki
+(https://github.com/kokkos/kokkos/wiki).
 
 For questions find us on Slack: https://kokkosteam.slack.com or open a github issue.
 
 For non-public questions send an email to
 crtrott(at)sandia.gov
 
-A separate repository with extensive tutorial material can be found under
-https://github.com/kokkos/kokkos-tutorials.
-
-Furthermore, the 'example/tutorial' directory provides step by step tutorial
-examples which explain many of the features of Kokkos. They work with
-simple Makefiles. To build with g++ and OpenMP simply type 'make'
-in the 'example/tutorial' directory. This will build all examples in the
-subfolders. To change the build options refer to the Programming Guide
-in the compilation section.
-
-To learn more about Kokkos consider watching one of our presentations:
-* GTC 2015:
-  - http://on-demand.gputechconf.com/gtc/2015/video/S5166.html
-  - http://on-demand.gputechconf.com/gtc/2015/presentation/S5166-H-Carter-Edwards.pdf
-
-
 # Contributing to Kokkos
 
 We are open and try to encourage contributions from external developers.
@@ -53,57 +43,40 @@ For specifics see the LICENSE file contained in the repository or distribution.
 
 # Requirements
 
-### Primary tested compilers on X86 are:
-* GCC 5.3.0
-* GCC 5.4.0
-* GCC 5.5.0
-* GCC 6.1.0
-* GCC 7.2.0
-* GCC 7.3.0
-* GCC 8.1.0
-* Intel 17.0.1
-* Intel 17.4.196
-* Intel 18.2.128
-* Clang 4.0.0
-* Clang 6.0.0 for CUDA (CUDA Toolkit 9.0)
-* Clang 7.0.0 for CUDA (CUDA Toolkit 9.1)
-* Clang 8.0.0 for CUDA (CUDA Toolkit 9.2)
-* PGI 18.7
-* NVCC 9.1 for CUDA (with gcc 6.1.0)
-* NVCC 9.2 for CUDA (with gcc 7.2.0)
-* NVCC 10.0 for CUDA (with gcc 7.4.0)
-* NVCC 10.1 for CUDA (with gcc 7.4.0)
-* NVCC 11.0 for CUDA (with gcc 8.4.0)
-
-### Primary tested compilers on Power 8 are:
-* GCC 6.4.0 (OpenMP,Serial)
-* GCC 7.2.0 (OpenMP,Serial)
-* IBM XL 16.1.0 (OpenMP, Serial)
-* NVCC 9.2.88 for CUDA (with gcc 7.2.0 and XL 16.1.0)
-
-### Primary tested compilers on Intel KNL are:
-* Intel 17.2.174 (with gcc 6.2.0 and 6.4.0)
-* Intel 18.2.199 (with gcc 6.2.0 and 6.4.0)
-
-### Primary tested compilers on ARM (Cavium ThunderX2)
-* GCC 7.2.0
-* ARM/Clang 18.4.0
-
-### Other compilers working:
-* X86:
-    * Cygwin 2.1.0 64bit with gcc 4.9.3
-    * GCC 8.1.0 (not warning free)
-
-### Known non-working combinations:
-* Power8:
-    * Pthreads backend
-* ARM
-    * Pthreads backend
+### Minimum Compiler Versions
+
+Generally Kokkos should work with all compiler versions newer than the minimum.
+However as in all sufficiently complex enough code, we have to work around compiler
+bugs with almost all compilers. So compiler versions we don't test may have issues
+we are unaware off.
+
+* GCC: 5.3.0
+* Clang: 4.0.0
+* Intel: 17.0.1
+* NVCC: 9.2.88
+* NVC++: 21.5
+* ROCM: 4.3
+* MSVC: 19.29
+* IBM XL: 16.1.1
+* Fujitsu: 4.5.0
+* ARM/Clang 20.1
+
+### Primary Tested Compilers
+
+* GCC: 5.3.0, 6.1.0, 7.3.0, 8.3, 9.2, 10.0
+* NVCC: 9.2.88, 10.1, 11.0
+* Clang: 8.0.0, 9.0.0, 10.0.0, 12.0.0
+* Intel 17.4, 18.1, 19.5
+* MSVC: 19.29
+* ARM/Clang: 20.1
+* IBM XL: 16.1.1
+* ROCM: 4.3.0
 
 ### Build system:
-* CMake >= 3.10: required
-* CMake >= 3.13: recommended
+
+* CMake >= 3.16: required
 * CMake >= 3.18: Fortran linkage. This does not affect most mixed Fortran/Kokkos builds. See [build issues](BUILD.md#KnownIssues).
+* CMake >= 3.21.1 for NVC++
 
 Primary tested compiler are passing in release mode
 with warnings as errors. They also are tested with a comprehensive set of
@@ -153,7 +126,6 @@ cmake $srcdir \
   -DCMAKE_INSTALL_PREFIX=$path_to_install \
   -DKokkos_ENABLE_OPENMP=On \
   -DKokkos_ARCH_HSW=On \
-  -DKokkos_ENABLE_HWLOC=On \
   -DKokkos_HWLOC_DIR=$path_to_hwloc
 ````
 then simply type `make install`. The Kokkos CMake package will then be installed in `$path_to_install` to be used by downstream packages.
@@ -212,23 +184,8 @@ where `...` is the unique spec identifying the particular Kokkos configuration a
 Some more details can found in the Kokkos spack [documentation](Spack.md) or the Spack [website](https://spack.readthedocs.io/en/latest).
 
 ## Raw Makefile
-A bash script is provided to generate raw makefiles.
-To install Kokkos as a library create a build directory and run the following
-````bash
-> $KOKKOS_PATH/generate_makefile.bash --prefix=$path_to_install
-````
-Once the Makefile is generated, run:
-````bash
-> make kokkoslib
-> make install
-````
-To additionally run the unit tests:
-````bash
-> make build-test
-> make test
-````
-Run `generate_makefile.bash --help` for more detailed options such as
-changing the device type for which to build.
+
+Raw Makefiles are only supported via inline builds. See below.
 
 ## Inline Builds vs. Installed Package
 For individual projects, it may be preferable to build Kokkos inline rather than link to an installed package.
@@ -268,6 +225,35 @@ more than a single GPU is used by a single process.
 
 If you publish work which mentions Kokkos, please cite the following paper:
 
+````BibTex
+@ARTICLE{9485033,
+  author={Trott, Christian R. and Lebrun-Grandié, Damien and Arndt, Daniel and Ciesko, Jan and Dang, Vinh and Ellingwood, Nathan and Gayatri, Rahulkumar and Harvey, Evan and Hollman, Daisy S. and Ibanez, Dan and Liber, Nevin and Madsen, Jonathan and Miles, Jeff and Poliakoff, David and Powell, Amy and Rajamanickam, Sivasankaran and Simberg, Mikael and Sunderland, Dan and Turcksin, Bruno and Wilke, Jeremiah},
+  journal={IEEE Transactions on Parallel and Distributed Systems},
+  title={Kokkos 3: Programming Model Extensions for the Exascale Era},
+  year={2022},
+  volume={33},
+  number={4},
+  pages={805-817},
+  doi={10.1109/TPDS.2021.3097283}}
+````
+
+If you use more than one Kokkos EcoSystem package, please also cite:
+
+````BibTex
+@ARTICLE{9502936,
+  author={Trott, Christian and Berger-Vergiat, Luc and Poliakoff, David and Rajamanickam, Sivasankaran and Lebrun-Grandie, Damien and Madsen, Jonathan and Al Awar, Nader and Gligoric, Milos and Shipman, Galen and Womeldorff, Geoff},
+  journal={Computing in Science   Engineering},
+  title={The Kokkos EcoSystem: Comprehensive Performance Portability for High Performance Computing},
+  year={2021},
+  volume={23},
+  number={5},
+  pages={10-18},
+  doi={10.1109/MCSE.2021.3098509}}
+````
+
+
+And if you feel generous: feel free to cite the original Kokkos paper which describes most of the basic Kokkos concepts:
+
 ````BibTeX
 @article{CarterEdwards20143202,
   title = "Kokkos: Enabling manycore performance portability through polymorphic memory access patterns ",
diff --git a/packages/kokkos/algorithms/CMakeLists.txt b/packages/kokkos/algorithms/CMakeLists.txt
index fd099054ba457e2b1a0557fd08be835f50eef939..eb54db8a556453f4afed4c95cf20321e8cbe211e 100644
--- a/packages/kokkos/algorithms/CMakeLists.txt
+++ b/packages/kokkos/algorithms/CMakeLists.txt
@@ -5,10 +5,10 @@ KOKKOS_SUBPACKAGE(Algorithms)
 IF (NOT Kokkos_INSTALL_TESTING)
   ADD_SUBDIRECTORY(src)
 ENDIF()
-
-KOKKOS_ADD_TEST_DIRECTORIES(unit_tests)
+IF(NOT (KOKKOS_ENABLE_OPENMPTARGET AND KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC))
+  KOKKOS_ADD_TEST_DIRECTORIES(unit_tests)
+ENDIF()
 
 KOKKOS_SUBPACKAGE_POSTPROCESS()
 
 
-
diff --git a/packages/kokkos/algorithms/src/Kokkos_Random.hpp b/packages/kokkos/algorithms/src/Kokkos_Random.hpp
index 904cf5ccb967037d94ac9b4a06144a4f7333dd3d..46b8ab87fabfbeabda12beb3ddabf0eb6aab3482 100644
--- a/packages/kokkos/algorithms/src/Kokkos_Random.hpp
+++ b/packages/kokkos/algorithms/src/Kokkos_Random.hpp
@@ -447,6 +447,25 @@ struct rand<Generator, unsigned long long> {
   }
 };
 
+#if defined(KOKKOS_HALF_T_IS_FLOAT) && !KOKKOS_HALF_T_IS_FLOAT
+template <class Generator>
+struct rand<Generator, Kokkos::Experimental::half_t> {
+  using half = Kokkos::Experimental::half_t;
+  KOKKOS_INLINE_FUNCTION
+  static half max() { return half(1.0); }
+  KOKKOS_INLINE_FUNCTION
+  static half draw(Generator& gen) { return half(gen.frand()); }
+  KOKKOS_INLINE_FUNCTION
+  static half draw(Generator& gen, const half& range) {
+    return half(gen.frand(float(range)));
+  }
+  KOKKOS_INLINE_FUNCTION
+  static half draw(Generator& gen, const half& start, const half& end) {
+    return half(gen.frand(float(start), float(end)));
+  }
+};
+#endif  // defined(KOKKOS_HALF_T_IS_FLOAT) && !KOKKOS_HALF_T_IS_FLOAT
+
 template <class Generator>
 struct rand<Generator, float> {
   KOKKOS_INLINE_FUNCTION
@@ -600,7 +619,7 @@ struct Random_XorShift1024_UseCArrayState<Kokkos::Experimental::OpenMPTarget>
 
 template <class ExecutionSpace>
 struct Random_UniqueIndex {
-  using locks_view_type = View<int*, ExecutionSpace>;
+  using locks_view_type = View<int**, ExecutionSpace>;
   KOKKOS_FUNCTION
   static int get_state_idx(const locks_view_type) {
 #ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST
@@ -615,7 +634,7 @@ struct Random_UniqueIndex {
 #ifdef KOKKOS_ENABLE_CUDA
 template <>
 struct Random_UniqueIndex<Kokkos::Cuda> {
-  using locks_view_type = View<int*, Kokkos::Cuda>;
+  using locks_view_type = View<int**, Kokkos::Cuda>;
   KOKKOS_FUNCTION
   static int get_state_idx(const locks_view_type& locks_) {
 #ifdef __CUDA_ARCH__
@@ -625,7 +644,7 @@ struct Random_UniqueIndex<Kokkos::Cuda> {
                  blockDim.x * blockDim.y * blockDim.z +
              i_offset) %
             locks_.extent(0);
-    while (Kokkos::atomic_compare_exchange(&locks_(i), 0, 1)) {
+    while (Kokkos::atomic_compare_exchange(&locks_(i, 0), 0, 1)) {
       i += blockDim.x * blockDim.y * blockDim.z;
       if (i >= static_cast<int>(locks_.extent(0))) {
         i = i_offset;
@@ -643,7 +662,7 @@ struct Random_UniqueIndex<Kokkos::Cuda> {
 #ifdef KOKKOS_ENABLE_HIP
 template <>
 struct Random_UniqueIndex<Kokkos::Experimental::HIP> {
-  using locks_view_type = View<int*, Kokkos::Experimental::HIP>;
+  using locks_view_type = View<int**, Kokkos::Experimental::HIP>;
   KOKKOS_FUNCTION
   static int get_state_idx(const locks_view_type& locks_) {
 #ifdef __HIP_DEVICE_COMPILE__
@@ -653,7 +672,7 @@ struct Random_UniqueIndex<Kokkos::Experimental::HIP> {
                  blockDim.x * blockDim.y * blockDim.z +
              i_offset) %
             locks_.extent(0);
-    while (Kokkos::atomic_compare_exchange(&locks_(i), 0, 1)) {
+    while (Kokkos::atomic_compare_exchange(&locks_(i, 0), 0, 1)) {
       i += blockDim.x * blockDim.y * blockDim.z;
       if (i >= static_cast<int>(locks_.extent(0))) {
         i = i_offset;
@@ -671,15 +690,15 @@ struct Random_UniqueIndex<Kokkos::Experimental::HIP> {
 #ifdef KOKKOS_ENABLE_SYCL
 template <>
 struct Random_UniqueIndex<Kokkos::Experimental::SYCL> {
-  using locks_view_type = View<int*, Kokkos::Experimental::SYCL>;
+  using locks_view_type = View<int**, Kokkos::Experimental::SYCL>;
   KOKKOS_FUNCTION
   static int get_state_idx(const locks_view_type& locks_) {
-#ifdef KOKKOS_ARCH_INTEL_GEN
+#ifdef KOKKOS_ARCH_INTEL_GPU
     int i = Kokkos::Impl::clock_tic() % locks_.extent(0);
 #else
     int i = 0;
 #endif
-    while (Kokkos::atomic_compare_exchange(&locks_(i), 0, 1)) {
+    while (Kokkos::atomic_compare_exchange(&locks_(i, 0), 0, 1)) {
       i = (i + 1) % static_cast<int>(locks_.extent(0));
     }
     return i;
@@ -687,6 +706,24 @@ struct Random_UniqueIndex<Kokkos::Experimental::SYCL> {
 };
 #endif
 
+#ifdef KOKKOS_ENABLE_OPENMPTARGET
+template <>
+struct Random_UniqueIndex<Kokkos::Experimental::OpenMPTarget> {
+  using locks_view_type = View<int**, Kokkos::Experimental::OpenMPTarget>;
+  KOKKOS_FUNCTION
+  static int get_state_idx(const locks_view_type& locks) {
+    const int team_size = omp_get_num_threads();
+    int i               = omp_get_team_num() * team_size + omp_get_thread_num();
+    const int lock_size = locks.extent_int(0);
+
+    while (Kokkos::atomic_compare_exchange(&locks(i, 0), 0, 1)) {
+      i = (i + 1) % lock_size;
+    }
+    return i;
+  }
+};
+#endif
+
 }  // namespace Impl
 
 template <class DeviceType>
@@ -838,18 +875,22 @@ template <class DeviceType = Kokkos::DefaultExecutionSpace>
 class Random_XorShift64_Pool {
  private:
   using execution_space = typename DeviceType::execution_space;
-  using locks_type      = View<int*, execution_space>;
-  using state_data_type = View<uint64_t*, DeviceType>;
+  using locks_type      = View<int**, execution_space>;
+  using state_data_type = View<uint64_t**, DeviceType>;
   locks_type locks_;
   state_data_type state_;
   int num_states_;
+  int padding_;
 
  public:
   using generator_type = Random_XorShift64<DeviceType>;
   using device_type    = DeviceType;
 
   KOKKOS_INLINE_FUNCTION
-  Random_XorShift64_Pool() { num_states_ = 0; }
+  Random_XorShift64_Pool() {
+    num_states_ = 0;
+    padding_    = 0;
+  }
   Random_XorShift64_Pool(uint64_t seed) {
     num_states_ = 0;
 
@@ -865,16 +906,22 @@ class Random_XorShift64_Pool {
     locks_      = src.locks_;
     state_      = src.state_;
     num_states_ = src.num_states_;
+    padding_    = src.padding_;
     return *this;
   }
 
   void init(uint64_t seed, int num_states) {
     if (seed == 0) seed = uint64_t(1318319);
-
+    // I only want to pad on CPU like archs (less than 1000 threads). 64 is a
+    // magic number, or random number I just wanted something not too large and
+    // not too small. 64 sounded fine.
+    padding_    = num_states < 1000 ? 64 : 1;
     num_states_ = num_states;
 
-    locks_ = locks_type("Kokkos::Random_XorShift64::locks", num_states_);
-    state_ = state_data_type("Kokkos::Random_XorShift64::state", num_states_);
+    locks_ =
+        locks_type("Kokkos::Random_XorShift64::locks", num_states, padding_);
+    state_ = state_data_type("Kokkos::Random_XorShift64::state", num_states_,
+                             padding_);
 
     typename state_data_type::HostMirror h_state = create_mirror_view(state_);
     typename locks_type::HostMirror h_lock       = create_mirror_view(locks_);
@@ -884,15 +931,15 @@ class Random_XorShift64_Pool {
         gen(seed, 0);
     for (int i = 0; i < 17; i++) gen.rand();
     for (int i = 0; i < num_states_; i++) {
-      int n1     = gen.rand();
-      int n2     = gen.rand();
-      int n3     = gen.rand();
-      int n4     = gen.rand();
-      h_state(i) = (((static_cast<uint64_t>(n1)) & 0xffff) << 00) |
-                   (((static_cast<uint64_t>(n2)) & 0xffff) << 16) |
-                   (((static_cast<uint64_t>(n3)) & 0xffff) << 32) |
-                   (((static_cast<uint64_t>(n4)) & 0xffff) << 48);
-      h_lock(i) = 0;
+      int n1        = gen.rand();
+      int n2        = gen.rand();
+      int n3        = gen.rand();
+      int n4        = gen.rand();
+      h_state(i, 0) = (((static_cast<uint64_t>(n1)) & 0xffff) << 00) |
+                      (((static_cast<uint64_t>(n2)) & 0xffff) << 16) |
+                      (((static_cast<uint64_t>(n3)) & 0xffff) << 32) |
+                      (((static_cast<uint64_t>(n4)) & 0xffff) << 48);
+      h_lock(i, 0) = 0;
     }
     deep_copy(state_, h_state);
     deep_copy(locks_, h_lock);
@@ -902,19 +949,19 @@ class Random_XorShift64_Pool {
   Random_XorShift64<DeviceType> get_state() const {
     const int i =
         Impl::Random_UniqueIndex<execution_space>::get_state_idx(locks_);
-    return Random_XorShift64<DeviceType>(state_(i), i);
+    return Random_XorShift64<DeviceType>(state_(i, 0), i);
   }
 
   // NOTE: state_idx MUST be unique and less than num_states
   KOKKOS_INLINE_FUNCTION
   Random_XorShift64<DeviceType> get_state(const int state_idx) const {
-    return Random_XorShift64<DeviceType>(state_(state_idx), state_idx);
+    return Random_XorShift64<DeviceType>(state_(state_idx, 0), state_idx);
   }
 
   KOKKOS_INLINE_FUNCTION
   void free_state(const Random_XorShift64<DeviceType>& state) const {
-    state_(state.state_idx_) = state.state_;
-    locks_(state.state_idx_) = 0;
+    state_(state.state_idx_, 0) = state.state_;
+    locks_(state.state_idx_, 0) = 0;
   }
 };
 
@@ -1074,14 +1121,15 @@ template <class DeviceType = Kokkos::DefaultExecutionSpace>
 class Random_XorShift1024_Pool {
  private:
   using execution_space = typename DeviceType::execution_space;
-  using locks_type      = View<int*, execution_space>;
-  using int_view_type   = View<int*, DeviceType>;
+  using locks_type      = View<int**, execution_space>;
+  using int_view_type   = View<int**, DeviceType>;
   using state_data_type = View<uint64_t * [16], DeviceType>;
 
   locks_type locks_;
   state_data_type state_;
   int_view_type p_;
   int num_states_;
+  int padding_;
   friend class Random_XorShift1024<DeviceType>;
 
  public:
@@ -1111,15 +1159,21 @@ class Random_XorShift1024_Pool {
     state_      = src.state_;
     p_          = src.p_;
     num_states_ = src.num_states_;
+    padding_    = src.padding_;
     return *this;
   }
 
   inline void init(uint64_t seed, int num_states) {
     if (seed == 0) seed = uint64_t(1318319);
+    // I only want to pad on CPU like archs (less than 1000 threads). 64 is a
+    // magic number, or random number I just wanted something not too large and
+    // not too small. 64 sounded fine.
+    padding_    = num_states < 1000 ? 64 : 1;
     num_states_ = num_states;
-    locks_      = locks_type("Kokkos::Random_XorShift1024::locks", num_states_);
+    locks_ =
+        locks_type("Kokkos::Random_XorShift1024::locks", num_states_, padding_);
     state_ = state_data_type("Kokkos::Random_XorShift1024::state", num_states_);
-    p_     = int_view_type("Kokkos::Random_XorShift1024::p", num_states_);
+    p_ = int_view_type("Kokkos::Random_XorShift1024::p", num_states_, padding_);
 
     typename state_data_type::HostMirror h_state = create_mirror_view(state_);
     typename locks_type::HostMirror h_lock       = create_mirror_view(locks_);
@@ -1140,8 +1194,8 @@ class Random_XorShift1024_Pool {
                         (((static_cast<uint64_t>(n3)) & 0xffff) << 32) |
                         (((static_cast<uint64_t>(n4)) & 0xffff) << 48);
       }
-      h_p(i)    = 0;
-      h_lock(i) = 0;
+      h_p(i, 0)    = 0;
+      h_lock(i, 0) = 0;
     }
     deep_copy(state_, h_state);
     deep_copy(locks_, h_lock);
@@ -1151,20 +1205,20 @@ class Random_XorShift1024_Pool {
   Random_XorShift1024<DeviceType> get_state() const {
     const int i =
         Impl::Random_UniqueIndex<execution_space>::get_state_idx(locks_);
-    return Random_XorShift1024<DeviceType>(state_, p_(i), i);
+    return Random_XorShift1024<DeviceType>(state_, p_(i, 0), i);
   };
 
   // NOTE: state_idx MUST be unique and less than num_states
   KOKKOS_INLINE_FUNCTION
   Random_XorShift1024<DeviceType> get_state(const int state_idx) const {
-    return Random_XorShift1024<DeviceType>(state_, p_(state_idx), state_idx);
+    return Random_XorShift1024<DeviceType>(state_, p_(state_idx, 0), state_idx);
   }
 
   KOKKOS_INLINE_FUNCTION
   void free_state(const Random_XorShift1024<DeviceType>& state) const {
     for (int i = 0; i < 16; i++) state_(state.state_idx_, i) = state.state_[i];
-    p_(state.state_idx_)     = state.p_;
-    locks_(state.state_idx_) = 0;
+    p_(state.state_idx_, 0)     = state.p_;
+    locks_(state.state_idx_, 0) = 0;
   }
 };
 
diff --git a/packages/kokkos/algorithms/src/Kokkos_Sort.hpp b/packages/kokkos/algorithms/src/Kokkos_Sort.hpp
index d17c02776ff5653045d9259d7a4fae2207546e21..7c1ce4c4cd8e757f3018da3989660d3b4c5e4cff 100644
--- a/packages/kokkos/algorithms/src/Kokkos_Sort.hpp
+++ b/packages/kokkos/algorithms/src/Kokkos_Sort.hpp
@@ -319,7 +319,7 @@ class BinSort {
                    Kokkos::RangePolicy<execution_space>(0, len), functor);
     }
 
-    execution_space().fence();
+    execution_space().fence("Kokkos::Sort: fence after sorting");
   }
 
   template <class ValuesViewType>
@@ -492,7 +492,8 @@ bool try_std_sort(ViewType view) {
                       view.stride_3(), view.stride_4(), view.stride_5(),
                       view.stride_6(), view.stride_7()};
   possible         = possible &&
-             std::is_same<typename ViewType::memory_space, HostSpace>::value;
+             SpaceAccessibility<HostSpace,
+                                typename ViewType::memory_space>::accessible;
   possible = possible && (ViewType::Rank == 1);
   possible = possible && (stride[0] == 1);
   if (possible) {
diff --git a/packages/kokkos/algorithms/unit_tests/CMakeLists.txt b/packages/kokkos/algorithms/unit_tests/CMakeLists.txt
index 9109837985a91ad14245133682af15aca59be503..50f8f0a332a6e528bcb12c26a4c60d5599f02c1d 100644
--- a/packages/kokkos/algorithms/unit_tests/CMakeLists.txt
+++ b/packages/kokkos/algorithms/unit_tests/CMakeLists.txt
@@ -44,7 +44,7 @@ IF(Kokkos_ENABLE_OPENMP)
   )
 ENDIF()
 
-foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;HIP;SYCL)
+foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;HIP;SYCL;OpenMPTarget)
   # Because there is always an exception to the rule
   if(Tag STREQUAL "Threads")
     set(DEVICE "PTHREAD")
diff --git a/packages/kokkos/algorithms/unit_tests/TestRandom.hpp b/packages/kokkos/algorithms/unit_tests/TestRandom.hpp
index 1f14875096dd2fbd0bebf4feea796d4c6ccd79f0..3dffce7df4f8dd1663a10a871075c1841005138d 100644
--- a/packages/kokkos/algorithms/unit_tests/TestRandom.hpp
+++ b/packages/kokkos/algorithms/unit_tests/TestRandom.hpp
@@ -47,7 +47,7 @@
 #include <iostream>
 #include <cstdlib>
 #include <cstdio>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <Kokkos_Core.hpp>
 #include <Kokkos_Random.hpp>
 #include <cmath>
@@ -109,6 +109,16 @@ struct RandomProperties {
   }
 };
 
+// FIXME_OPENMPTARGET: Need this for OpenMPTarget because contra to the standard
+// llvm requires the binary operator defined not just the +=
+KOKKOS_INLINE_FUNCTION
+RandomProperties operator+(const RandomProperties& org,
+                           const RandomProperties& add) {
+  RandomProperties val = org;
+  val += add;
+  return val;
+}
+
 template <class GeneratorPool, class Scalar>
 struct test_random_functor {
   using rnd_type = typename GeneratorPool::generator_type;
@@ -188,11 +198,50 @@ struct test_random_functor {
           static_cast<uint64_t>(1.0 * HIST_DIM3D * tmp2 / theMax);
       const uint64_t ind3_3d =
           static_cast<uint64_t>(1.0 * HIST_DIM3D * tmp3 / theMax);
-
+// Workaround Intel 17 compiler bug which sometimes add random
+// instruction alignment which makes the lock instruction
+// illegal. Seems to be mostly just for unsigned int atomics.
+// Looking at the assembly the compiler
+// appears to insert cache line alignment for the instruction.
+// Isn't restricted to specific archs. Seen it on SNB and SKX, but for
+// different code. Another occurrence was with Desul atomics in
+// a different unit test. This one here happens without desul atomics.
+// Inserting an assembly nop instruction changes the alignment and
+// works round this.
+//
+// 17.0.4 for 64bit Random works with 1/1/1/2/1
+// 17.0.4 for 1024bit Random works with 1/1/1/1/1
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
       atomic_fetch_add(&density_1d(ind1_1d), 1);
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
       atomic_fetch_add(&density_1d(ind2_1d), 1);
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
       atomic_fetch_add(&density_1d(ind3_1d), 1);
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      if (std::is_same<rnd_type, Kokkos::Random_XorShift64<device_type>>::value)
+        asm volatile("nop\n");
+      asm volatile("nop\n");
+#endif
+#endif
       atomic_fetch_add(&density_3d(ind1_3d, ind2_3d, ind3_3d), 1);
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
     }
     rand_pool.free_state(rand_gen);
   }
@@ -328,9 +377,11 @@ struct test_random_scalar {
       using functor_type =
           test_histogram1d_functor<typename RandomGenerator::device_type>;
       parallel_reduce(HIST_DIM1D, functor_type(density_1d, num_draws), result);
-
-      double tolerance   = 6 * std::sqrt(1.0 / HIST_DIM1D);
-      double mean_expect = 1.0 * num_draws * 3 / HIST_DIM1D;
+      double mean_eps_expect       = 0.0001;
+      double variance_eps_expect   = 0.07;
+      double covariance_eps_expect = 0.06;
+      double tolerance             = 6 * std::sqrt(1.0 / HIST_DIM1D);
+      double mean_expect           = 1.0 * num_draws * 3 / HIST_DIM1D;
       double variance_expect =
           1.0 * num_draws * 3 / HIST_DIM1D * (1.0 - 1.0 / HIST_DIM1D);
       double covariance_expect = -1.0 * num_draws * 3 / HIST_DIM1D / HIST_DIM1D;
@@ -339,11 +390,26 @@ struct test_random_scalar {
           variance_expect / (result.variance / HIST_DIM1D) - 1.0;
       double covariance_eps =
           (result.covariance / HIST_DIM1D - covariance_expect) / mean_expect;
-      pass_hist1d_mean = ((-0.0001 < mean_eps) && (0.0001 > mean_eps)) ? 1 : 0;
-      pass_hist1d_var =
-          ((-0.07 < variance_eps) && (0.07 > variance_eps)) ? 1 : 0;
-      pass_hist1d_covar =
-          ((-0.06 < covariance_eps) && (0.06 > covariance_eps)) ? 1 : 0;
+
+#if defined(KOKKOS_HALF_T_IS_FLOAT) && !KOKKOS_HALF_T_IS_FLOAT
+      if (std::is_same<Scalar, Kokkos::Experimental::half_t>::value) {
+        mean_eps_expect       = 0.0003;
+        variance_eps_expect   = 1.0;
+        covariance_eps_expect = 5.0e4;
+      }
+#endif
+
+      pass_hist1d_mean =
+          ((-mean_eps_expect < mean_eps) && (mean_eps_expect > mean_eps)) ? 1
+                                                                          : 0;
+      pass_hist1d_var = ((-variance_eps_expect < variance_eps) &&
+                         (variance_eps_expect > variance_eps))
+                            ? 1
+                            : 0;
+      pass_hist1d_covar = ((-covariance_eps_expect < covariance_eps) &&
+                           (covariance_eps_expect > covariance_eps))
+                              ? 1
+                              : 0;
 
       cout << "Density 1D: " << mean_eps << " " << variance_eps << " "
            << (result.covariance / HIST_DIM1D / HIST_DIM1D) << " || "
@@ -361,8 +427,9 @@ struct test_random_scalar {
           test_histogram3d_functor<typename RandomGenerator::device_type>;
       parallel_reduce(HIST_DIM1D, functor_type(density_3d, num_draws), result);
 
-      double tolerance   = 6 * std::sqrt(1.0 / HIST_DIM1D);
-      double mean_expect = 1.0 * num_draws / HIST_DIM1D;
+      double variance_factor = 1.2;
+      double tolerance       = 6 * std::sqrt(1.0 / HIST_DIM1D);
+      double mean_expect     = 1.0 * num_draws / HIST_DIM1D;
       double variance_expect =
           1.0 * num_draws / HIST_DIM1D * (1.0 - 1.0 / HIST_DIM1D);
       double covariance_expect = -1.0 * num_draws / HIST_DIM1D / HIST_DIM1D;
@@ -371,15 +438,23 @@ struct test_random_scalar {
           variance_expect / (result.variance / HIST_DIM1D) - 1.0;
       double covariance_eps =
           (result.covariance / HIST_DIM1D - covariance_expect) / mean_expect;
+
+#if defined(KOKKOS_HALF_T_IS_FLOAT) && !KOKKOS_HALF_T_IS_FLOAT
+      if (std::is_same<Scalar, Kokkos::Experimental::half_t>::value) {
+        variance_factor = 7;
+      }
+#endif
+
       pass_hist3d_mean =
           ((-tolerance < mean_eps) && (tolerance > mean_eps)) ? 1 : 0;
-      pass_hist3d_var = ((-1.2 * tolerance < variance_eps) &&
-                         (1.2 * tolerance > variance_eps))
+      pass_hist3d_var = ((-variance_factor * tolerance < variance_eps) &&
+                         (variance_factor * tolerance > variance_eps))
                             ? 1
                             : 0;
-      pass_hist3d_covar =
-          ((-tolerance < covariance_eps) && (tolerance > covariance_eps)) ? 1
-                                                                          : 0;
+      pass_hist3d_covar = ((-variance_factor * tolerance < covariance_eps) &&
+                           (variance_factor * tolerance > covariance_eps))
+                              ? 1
+                              : 0;
 
       cout << "Density 3D: " << mean_eps << " " << variance_eps << " "
            << result.covariance / HIST_DIM1D / HIST_DIM1D << " || " << tolerance
@@ -461,6 +536,21 @@ void test_random(unsigned int num_draws) {
   deep_copy(density_1d, 0);
   deep_copy(density_3d, 0);
 
+  cout << "Test Scalar=half" << endl;
+  test_random_scalar<RandomGenerator, Kokkos::Experimental::half_t> test_half(
+      density_1d, density_3d, pool, num_draws);
+  ASSERT_EQ(test_half.pass_mean, 1);
+  ASSERT_EQ(test_half.pass_var, 1);
+  ASSERT_EQ(test_half.pass_covar, 1);
+  ASSERT_EQ(test_half.pass_hist1d_mean, 1);
+  ASSERT_EQ(test_half.pass_hist1d_var, 1);
+  ASSERT_EQ(test_half.pass_hist1d_covar, 1);
+  ASSERT_EQ(test_half.pass_hist3d_mean, 1);
+  ASSERT_EQ(test_half.pass_hist3d_var, 1);
+  ASSERT_EQ(test_half.pass_hist3d_covar, 1);
+  deep_copy(density_1d, 0);
+  deep_copy(density_3d, 0);
+
   cout << "Test Scalar=float" << endl;
   test_random_scalar<RandomGenerator, float> test_float(density_1d, density_3d,
                                                         pool, num_draws);
diff --git a/packages/kokkos/algorithms/unit_tests/TestSort.hpp b/packages/kokkos/algorithms/unit_tests/TestSort.hpp
index a3c362ec201bae07df05867d07136e26e73204d0..9c6308c84347e2229ad083805db3d05918baa4f8 100644
--- a/packages/kokkos/algorithms/unit_tests/TestSort.hpp
+++ b/packages/kokkos/algorithms/unit_tests/TestSort.hpp
@@ -370,7 +370,10 @@ template <class ExecutionSpace, typename KeyType>
 void test_sort(unsigned int N) {
   test_1D_sort<ExecutionSpace, KeyType>(N);
   test_3D_sort<ExecutionSpace, KeyType>(N);
+// FIXME_OPENMPTARGET: OpenMPTarget doesn't support DynamicView yet.
+#ifndef KOKKOS_ENABLE_OPENMPTARGET
   test_dynamic_view_sort<ExecutionSpace, KeyType>(N);
+#endif
   test_issue_1160_sort<ExecutionSpace>();
 }
 }  // namespace Impl
diff --git a/packages/kokkos/appveyor.yml b/packages/kokkos/appveyor.yml
index e8763c0b665c4a992f74b70eab0caa915beb33dd..73a0d3187596be4d4c99ef9f211b93bd0659079e 100644
--- a/packages/kokkos/appveyor.yml
+++ b/packages/kokkos/appveyor.yml
@@ -3,4 +3,8 @@ image:
 clone_folder: c:\projects\source
 build_script:
 - cmd: >-
-    cmake c:\projects\source -DKokkos_ENABLE_TESTS=ON -DCMAKE_CXX_FLAGS="/W0 /EHsc /d1reportClassLayoutChanges" -DCTEST_ARGS="-C Debug -V --output-on-failure" -DBUILD_NAME=MSVC-2019 -DBUILD_TYPE=Debug -DSITE=AppVeyor -DTARGET=install -P cmake/KokkosCI.cmake
+    mkdir build &&
+    cd build &&
+    cmake c:\projects\source -DKokkos_ENABLE_TESTS=ON -DCMAKE_CXX_FLAGS="/W0 /EHsc" -DKokkos_ENABLE_DEPRECATED_CODE_3=ON -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF &&
+    cmake --build . --target install &&
+    ctest -C Debug --output-on-failure
diff --git a/packages/kokkos/benchmarks/atomic/main.cpp b/packages/kokkos/benchmarks/atomic/main.cpp
index 7b5caa1aee1658104a8916bec314759e3e5ba30a..cc0d3e41e85aaa7483d11dbf639cc5a9d5809a47 100644
--- a/packages/kokkos/benchmarks/atomic/main.cpp
+++ b/packages/kokkos/benchmarks/atomic/main.cpp
@@ -1,12 +1,12 @@
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <Kokkos_Random.hpp>
 
 template <class Scalar>
 double test_atomic(int L, int N, int M, int K, int R,
                    Kokkos::View<const int*> offsets) {
   Kokkos::View<Scalar*> output("Output", N);
-  Kokkos::Impl::Timer timer;
+  Kokkos::Timer timer;
 
   for (int r = 0; r < R; r++)
     Kokkos::parallel_for(
@@ -28,7 +28,7 @@ template <class Scalar>
 double test_no_atomic(int L, int N, int M, int K, int R,
                       Kokkos::View<const int*> offsets) {
   Kokkos::View<Scalar*> output("Output", N);
-  Kokkos::Impl::Timer timer;
+  Kokkos::Timer timer;
   for (int r = 0; r < R; r++)
     Kokkos::parallel_for(
         L, KOKKOS_LAMBDA(const int& i) {
diff --git a/packages/kokkos/benchmarks/bytes_and_flops/bench.hpp b/packages/kokkos/benchmarks/bytes_and_flops/bench.hpp
index 62d7ef4a4cf387191c6d0276c4ea360c289d4de5..4fc6ca2c68b3a77e37360b90b678ae5c461204f6 100644
--- a/packages/kokkos/benchmarks/bytes_and_flops/bench.hpp
+++ b/packages/kokkos/benchmarks/bytes_and_flops/bench.hpp
@@ -43,7 +43,7 @@
 */
 
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 template <class Scalar, int Unroll, int Stride>
 struct Run {
diff --git a/packages/kokkos/benchmarks/bytes_and_flops/main.cpp b/packages/kokkos/benchmarks/bytes_and_flops/main.cpp
index 6da2407a08b7afb981ffb8c3a970b2df7d55f951..75f30a340938378fe85f72b4dd294235594cf21d 100644
--- a/packages/kokkos/benchmarks/bytes_and_flops/main.cpp
+++ b/packages/kokkos/benchmarks/bytes_and_flops/main.cpp
@@ -43,7 +43,7 @@
 */
 
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <bench.hpp>
 #include <cstdlib>
 
diff --git a/packages/kokkos/benchmarks/gather/main.cpp b/packages/kokkos/benchmarks/gather/main.cpp
index 5f10e4dcc1aa509c191d3c7a6486114b3c0b7de9..dd502faaa480c1c7ab9936e4f032095094e714bb 100644
--- a/packages/kokkos/benchmarks/gather/main.cpp
+++ b/packages/kokkos/benchmarks/gather/main.cpp
@@ -43,7 +43,7 @@
 */
 
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <gather.hpp>
 #include <cstdlib>
 
diff --git a/packages/kokkos/benchmarks/stream/stream-kokkos.cpp b/packages/kokkos/benchmarks/stream/stream-kokkos.cpp
index e7ef67e0805c9c4424fbf1781423cf907dca3eec..311947c197cba64e8354dc63743baa99b0bfa782 100644
--- a/packages/kokkos/benchmarks/stream/stream-kokkos.cpp
+++ b/packages/kokkos/benchmarks/stream/stream-kokkos.cpp
@@ -52,35 +52,33 @@
 
 #define HLINE "-------------------------------------------------------------\n"
 
-#if defined(KOKKOS_ENABLE_CUDA)
-using StreamHostArray   = Kokkos::View<double*, Kokkos::CudaSpace>::HostMirror;
-using StreamDeviceArray = Kokkos::View<double*, Kokkos::CudaSpace>;
-#else
-using StreamHostArray   = Kokkos::View<double*, Kokkos::HostSpace>::HostMirror;
-using StreamDeviceArray = Kokkos::View<double*, Kokkos::HostSpace>;
-#endif
+using StreamDeviceArray =
+    Kokkos::View<double*, Kokkos::MemoryTraits<Kokkos::Restrict>>;
+using StreamHostArray = typename StreamDeviceArray::HostMirror;
 
 using StreamIndex = int;
+using Policy      = Kokkos::RangePolicy<Kokkos::IndexType<StreamIndex>>;
 
-double now() {
-  struct timeval now;
-  gettimeofday(&now, nullptr);
+void perform_set(StreamDeviceArray& a, const double scalar) {
+  Kokkos::parallel_for(
+      "set", Policy(0, a.extent(0)),
+      KOKKOS_LAMBDA(const StreamIndex i) { a[i] = scalar; });
 
-  return (double)now.tv_sec + ((double)now.tv_usec * 1.0e-6);
+  Kokkos::fence();
 }
 
-void perform_copy(StreamDeviceArray& a, StreamDeviceArray& b,
-                  StreamDeviceArray& c) {
+void perform_copy(StreamDeviceArray& a, StreamDeviceArray& b) {
   Kokkos::parallel_for(
-      "copy", a.extent(0), KOKKOS_LAMBDA(const StreamIndex i) { c[i] = a[i]; });
+      "copy", Policy(0, a.extent(0)),
+      KOKKOS_LAMBDA(const StreamIndex i) { b[i] = a[i]; });
 
   Kokkos::fence();
 }
 
-void perform_scale(StreamDeviceArray& a, StreamDeviceArray& b,
-                   StreamDeviceArray& c, const double scalar) {
+void perform_scale(StreamDeviceArray& b, StreamDeviceArray& c,
+                   const double scalar) {
   Kokkos::parallel_for(
-      "copy", a.extent(0),
+      "scale", Policy(0, b.extent(0)),
       KOKKOS_LAMBDA(const StreamIndex i) { b[i] = scalar * c[i]; });
 
   Kokkos::fence();
@@ -89,7 +87,7 @@ void perform_scale(StreamDeviceArray& a, StreamDeviceArray& b,
 void perform_add(StreamDeviceArray& a, StreamDeviceArray& b,
                  StreamDeviceArray& c) {
   Kokkos::parallel_for(
-      "add", a.extent(0),
+      "add", Policy(0, a.extent(0)),
       KOKKOS_LAMBDA(const StreamIndex i) { c[i] = a[i] + b[i]; });
 
   Kokkos::fence();
@@ -98,7 +96,7 @@ void perform_add(StreamDeviceArray& a, StreamDeviceArray& b,
 void perform_triad(StreamDeviceArray& a, StreamDeviceArray& b,
                    StreamDeviceArray& c, const double scalar) {
   Kokkos::parallel_for(
-      "triad", a.extent(0),
+      "triad", Policy(0, a.extent(0)),
       KOKKOS_LAMBDA(const StreamIndex i) { a[i] = b[i] + scalar * c[i]; });
 
   Kokkos::fence();
@@ -184,6 +182,7 @@ int run_benchmark() {
 
   const double scalar = 3.0;
 
+  double setTime   = std::numeric_limits<double>::max();
   double copyTime  = std::numeric_limits<double>::max();
   double scaleTime = std::numeric_limits<double>::max();
   double addTime   = std::numeric_limits<double>::max();
@@ -191,13 +190,10 @@ int run_benchmark() {
 
   printf("Initializing Views...\n");
 
-#if defined(KOKKOS_HAVE_OPENMP)
-  Kokkos::parallel_for(
-      "init", Kokkos::RangePolicy<Kokkos::OpenMP>(0, STREAM_ARRAY_SIZE),
-#else
   Kokkos::parallel_for(
-      "init", Kokkos::RangePolicy<Kokkos::Serial>(0, STREAM_ARRAY_SIZE),
-#endif
+      "init",
+      Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(0,
+                                                             STREAM_ARRAY_SIZE),
       KOKKOS_LAMBDA(const int i) {
         a[i] = 1.0;
         b[i] = 2.0;
@@ -209,26 +205,30 @@ int run_benchmark() {
   Kokkos::deep_copy(dev_b, b);
   Kokkos::deep_copy(dev_c, c);
 
-  double start;
-
   printf("Starting benchmarking...\n");
 
+  Kokkos::Timer timer;
+
   for (StreamIndex k = 0; k < STREAM_NTIMES; ++k) {
-    start = now();
-    perform_copy(dev_a, dev_b, dev_c);
-    copyTime = std::min(copyTime, (now() - start));
+    timer.reset();
+    perform_set(dev_c, 1.5);
+    setTime = std::min(setTime, timer.seconds());
+
+    timer.reset();
+    perform_copy(dev_a, dev_c);
+    copyTime = std::min(copyTime, timer.seconds());
 
-    start = now();
-    perform_scale(dev_a, dev_b, dev_c, scalar);
-    scaleTime = std::min(scaleTime, (now() - start));
+    timer.reset();
+    perform_scale(dev_b, dev_c, scalar);
+    scaleTime = std::min(scaleTime, timer.seconds());
 
-    start = now();
+    timer.reset();
     perform_add(dev_a, dev_b, dev_c);
-    addTime = std::min(addTime, (now() - start));
+    addTime = std::min(addTime, timer.seconds());
 
-    start = now();
+    timer.reset();
     perform_triad(dev_a, dev_b, dev_c, scalar);
-    triadTime = std::min(triadTime, (now() - start));
+    triadTime = std::min(triadTime, timer.seconds());
   }
 
   Kokkos::deep_copy(a, dev_a);
@@ -240,6 +240,9 @@ int run_benchmark() {
 
   printf(HLINE);
 
+  printf("Set             %11.2f MB/s\n",
+         (1.0e-06 * 1.0 * (double)sizeof(double) * (double)STREAM_ARRAY_SIZE) /
+             setTime);
   printf("Copy            %11.2f MB/s\n",
          (1.0e-06 * 2.0 * (double)sizeof(double) * (double)STREAM_ARRAY_SIZE) /
              copyTime);
diff --git a/packages/kokkos/bin/hpcbind b/packages/kokkos/bin/hpcbind
index 6af091a7d8b60766cddae67c6076b5df1f8ad12f..43f8a745da27c080ce54ac4cfd9b9358f618554f 100755
--- a/packages/kokkos/bin/hpcbind
+++ b/packages/kokkos/bin/hpcbind
@@ -634,15 +634,15 @@ elif [[ ${HPCBIND_HAS_COMMAND} -eq 1 ]]; then
   > ${HPCBIND_OUT}
   if [[ ${HPCBIND_TEE} -eq 0 ]]; then
     if [[ ${HPCBIND_ENABLE_HWLOC_BIND} -eq 1 ]]; then
-      hwloc-bind "${HPCBIND_HWLOC_CPUSET}" -- $@ > ${HPCBIND_OUT} 2> ${HPCBIND_ERR}
+      hwloc-bind "${HPCBIND_HWLOC_CPUSET}" -- "$@" > ${HPCBIND_OUT} 2> ${HPCBIND_ERR}
     else
-      eval $@ > ${HPCBIND_OUT} 2> ${HPCBIND_ERR}
+      eval "$@" > ${HPCBIND_OUT} 2> ${HPCBIND_ERR}
     fi
   else
     if [[ ${HPCBIND_ENABLE_HWLOC_BIND} -eq 1 ]]; then
-      hwloc-bind "${HPCBIND_HWLOC_CPUSET}" -- $@ > >(tee ${HPCBIND_OUT}) 2> >(tee ${HPCBIND_ERR} >&2)
+      hwloc-bind "${HPCBIND_HWLOC_CPUSET}" -- "$@" > >(tee ${HPCBIND_OUT}) 2> >(tee ${HPCBIND_ERR} >&2)
     else
-      eval $@ > >(tee ${HPCBIND_OUT}) 2> >(tee ${HPCBIND_ERR} >&2)
+      eval "$@" > >(tee ${HPCBIND_OUT}) 2> >(tee ${HPCBIND_ERR} >&2)
     fi
   fi
 fi
diff --git a/packages/kokkos/bin/nvcc_wrapper b/packages/kokkos/bin/nvcc_wrapper
index 5556e888e34b2f7c2dd18bdb6f47071abde0574b..ba2c55508aca0c139853ce7107c53f8a405ec9c3 100755
--- a/packages/kokkos/bin/nvcc_wrapper
+++ b/packages/kokkos/bin/nvcc_wrapper
@@ -67,6 +67,11 @@ shared_versioned_libraries=""
 
 # Does the User set the architecture
 arch_set=0
+arch_flag=""
+
+# Does the user set RDC?
+rdc_set=0
+rdc_flag=""
 
 # Does the user overwrite the host compiler
 ccbin_set=0
@@ -91,7 +96,7 @@ replace_pragma_ident=0
 first_xcompiler_arg=1
 
 # Allow for setting temp dir without setting TMPDIR in parent (see https://docs.olcf.ornl.gov/systems/summit_user_guide.html#setting-tmpdir-causes-jsm-jsrun-errors-job-state-flip-flop)
-if [[ ! -z ${NVCC_WRAPPER_TMPDIR+x} ]]; then
+if [[ -z ${NVCC_WRAPPER_TMPDIR+x} ]]; then
   temp_dir=${TMPDIR:-/tmp}
 else
   temp_dir=${NVCC_WRAPPER_TMPDIR+x}
@@ -190,22 +195,48 @@ do
     host_only_args="$host_only_args $1 $2"
     shift
     ;;
+  # Handle nvcc args controlling whether to generated relocatable device code
+  --relocatable-device-code=*|-rdc=*)
+    if [ "$rdc_set" -eq 0 ]; then
+        rdc_set=1
+        rdc_flag="$1"
+        cuda_args="$cuda_args $rdc_flag"
+    elif [  "$rdc_flag" != "$1" ]; then
+        echo "RDC is being set twice with different flags, which is not handled"
+        echo "$rdc_flag"
+        echo "$1"
+        exit 1
+    fi
+    ;;
+  -rdc)
+    if [ "$rdc_set" -eq 0 ]; then
+        rdc_set=1
+        rdc_flag="$1 $2"
+        cuda_args="$cuda_args $rdc_flag"
+        shift
+    elif [ "$rdc_flag" != "$1 $2" ]; then
+        echo "RDC is being set twice with different flags, which is not handled"
+        echo "$rdc_flag"
+        echo "$1 $2"
+        exit 1
+    fi
+    ;;
   #Handle known nvcc args
-  --dryrun|--verbose|--keep|--keep-dir*|-G|--relocatable-device-code*|-lineinfo|-expt-extended-lambda|-expt-relaxed-constexpr|--resource-usage|-Xptxas*|--fmad*|--use_fast_math|--Wext-lambda-captures-this|-Wext-lambda-captures-this)
+  --dryrun|--verbose|--keep|--keep-dir*|-G|-lineinfo|-expt-extended-lambda|-expt-relaxed-constexpr|--resource-usage|-Xptxas*|--fmad=*|--use_fast_math|--Wext-lambda-captures-this|-Wext-lambda-captures-this)
     cuda_args="$cuda_args $1"
     ;;
   #Handle more known nvcc args
-  --expt-extended-lambda|--expt-relaxed-constexpr|--Wno-deprecated-gpu-targets|-Wno-deprecated-gpu-targets)
+  --expt-extended-lambda|--expt-relaxed-constexpr|--Wno-deprecated-gpu-targets|-Wno-deprecated-gpu-targets|-allow-unsupported-compiler|--allow-unsupported-compiler)
     cuda_args="$cuda_args $1"
     ;;
   #Handle known nvcc args that have an argument
-  -rdc|-maxrregcount|--default-stream|-Xnvlink|--fmad|-cudart|--cudart|-include)
+  -maxrregcount=*|--maxrregcount=*|-time=*)
+    cuda_args="$cuda_args $1"
+    ;;
+  -maxrregcount|--default-stream|-Xnvlink|--fmad|-cudart|--cudart|-include|-time)
     cuda_args="$cuda_args $1 $2"
     shift
     ;;
-  -rdc=*|-maxrregcount*|--maxrregcount*)
-    cuda_args="$cuda_args $1"
-    ;;
   #Handle unsupported standard flags
   --std=c++1y|-std=c++1y|--std=gnu++1y|-std=gnu++1y|--std=c++1z|-std=c++1z|--std=gnu++1z|-std=gnu++1z|--std=c++2a|-std=c++2a)
     fallback_std_flag="-std=c++14"
@@ -323,20 +354,36 @@ do
     ;;
 
   #Handle -arch argument (if its not set use a default) this is the version with = sign
-  -arch*|-gencode*)
-    cuda_args="$cuda_args $1"
-    arch_set=1
+  -arch=*|-gencode=*)
+    if [ "$arch_set" -eq 0 ]; then
+        arch_set=1
+        arch_flag="$1"
+        cuda_args="$cuda_args $arch_flag"
+    elif [  "$arch_flag" != "$1" ]; then
+        echo "ARCH is being set twice with different flags, which is not handled"
+        echo "$arch_flag"
+        echo "$1"
+        exit 1
+    fi
+    ;;
+  #Handle -arch argument (if its not set use a default) this is the version without = sign
+  -arch|-gencode)
+    if [ "$arch_set" -eq 0 ]; then
+        arch_set=1
+        arch_flag="$1 $2"
+        cuda_args="$cuda_args $arch_flag"
+        shift
+    elif [ "$arch_flag" != "$1 $2" ]; then
+        echo "ARCH is being set twice with different flags, which is not handled"
+        echo "$arch_flag"
+        echo "$1 $2"
+        exit 1
+    fi
     ;;
   #Handle -code argument (if its not set use a default) this is the version with = sign
   -code*)
     cuda_args="$cuda_args $1"
     ;;
-  #Handle -arch argument (if its not set use a default) this is the version without = sign
-  -arch|-gencode)
-    cuda_args="$cuda_args $1 $2"
-    arch_set=1
-    shift
-    ;;
   #Handle -code argument (if its not set use a default) this is the version without = sign
   -code)
     cuda_args="$cuda_args $1 $2"
@@ -505,14 +552,14 @@ if [ $host_only -eq 1 ]; then
   $host_command
 elif [ -n "$nvcc_depfile_command" ]; then
   if [ "$NVCC_WRAPPER_SHOW_COMMANDS_BEING_RUN" == "1" ] ; then
-    echo "$nvcc_command && $nvcc_depfile_command"
+    echo "TMPDIR=${temp_dir} $nvcc_command && TMPDIR=${temp_dir} $nvcc_depfile_command"
   fi
-  $nvcc_command && $nvcc_depfile_command
+  TMPDIR=${temp_dir} $nvcc_command && TMPDIR=${temp_dir} $nvcc_depfile_command
 else
   if [ "$NVCC_WRAPPER_SHOW_COMMANDS_BEING_RUN" == "1" ] ; then
-    echo "$nvcc_command"
+    echo "TMPDIR=${temp_dir} $nvcc_command"
   fi
-  $nvcc_command
+  TMPDIR=${temp_dir} $nvcc_command
 fi
 error_code=$?
 
diff --git a/packages/kokkos/cmake/CTestConfig.cmake.in b/packages/kokkos/cmake/CTestConfig.cmake.in
deleted file mode 100644
index 1f82c0d64d15e0a4fb346cfb7227be9cd41e5f17..0000000000000000000000000000000000000000
--- a/packages/kokkos/cmake/CTestConfig.cmake.in
+++ /dev/null
@@ -1,91 +0,0 @@
-#----------------------------------------------------------------------------------------#
-#
-#   CTestConfig.cmake template for Kokkos
-#
-#----------------------------------------------------------------------------------------#
-
-#
-#   dash-board related
-#
-set(CTEST_PROJECT_NAME "Kokkos")
-set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC")
-set(CTEST_DROP_METHOD "https")
-set(CTEST_DROP_SITE "cdash.nersc.gov")
-set(CTEST_DROP_LOCATION "/submit.php?project=${CTEST_PROJECT_NAME}")
-set(CTEST_CDASH_VERSION "1.6")
-set(CTEST_CDASH_QUERY_VERSION TRUE)
-set(CTEST_SUBMIT_RETRY_COUNT "1")
-set(CTEST_SUBMIT_RETRY_DELAY "30")
-
-#
-#   configure/build related
-#
-set(CTEST_BUILD_NAME "@BUILD_NAME@")
-set(CTEST_MODEL "@MODEL@")
-set(CTEST_SITE "@SITE@")
-set(CTEST_CONFIGURATION_TYPE "@BUILD_TYPE@")
-set(CTEST_SOURCE_DIRECTORY "@SOURCE_REALDIR@")
-set(CTEST_BINARY_DIRECTORY "@BINARY_REALDIR@")
-
-#
-#   configure/build related
-#
-set(CTEST_UPDATE_TYPE "git")
-set(CTEST_UPDATE_VERSION_ONLY ON)
-# set(CTEST_GENERATOR "")
-# set(CTEST_GENERATOR_PLATFORM "")
-
-#
-#   testing related
-#
-set(CTEST_TIMEOUT "7200")
-set(CTEST_TEST_TIMEOUT "7200")
-set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_ERRORS "100")
-set(CTEST_CUSTOM_MAXIMUM_NUMBER_OF_WARNINGS "100")
-set(CTEST_CUSTOM_MAXIMUM_PASSED_TEST_OUTPUT_SIZE "1048576")
-
-#
-#   coverage related
-#
-set(CTEST_CUSTOM_COVERAGE_EXCLUDE ".*tpls/.*;/usr/.*;.*unit_test/.*;.*unit_tests/.*;.*perf_test/.*")
-
-#
-#   commands
-#
-if(NOT "@CHECKOUT_COMMAND@" STREQUAL "")
-    set(CTEST_CHECKOUT_COMMAND "@CHECKOUT_COMMAND@")
-endif()
-set(CTEST_UPDATE_COMMAND "@GIT_EXECUTABLE@")
-set(CTEST_CONFIGURE_COMMAND "@CMAKE_COMMAND@ -DCMAKE_BUILD_TYPE=@BUILD_TYPE@ -DKokkos_ENABLE_TESTS=ON @CONFIG_ARGS@ @SOURCE_REALDIR@")
-set(CTEST_BUILD_COMMAND "@CMAKE_COMMAND@ --build @BINARY_REALDIR@ --target @TARGET@")
-if(NOT WIN32)
-    set(CTEST_BUILD_COMMAND "${CTEST_BUILD_COMMAND} -- -j@BUILD_JOBS@")
-endif()
-set(CTEST_COVERAGE_COMMAND "gcov")
-set(CTEST_MEMORYCHECK_COMMAND "valgrind")
-set(CTEST_GIT_COMMAND "@GIT_EXECUTABLE@")
-
-#
-#   various configs
-#
-set(APPEND_VALUE @APPEND@)
-if(APPEND_VALUE)
-    set(APPEND_CTEST APPEND)
-endif()
-
-macro(SET_TEST_PROP VAR)
-    if(NOT "${ARGS}" STREQUAL "")
-        set(${VAR}_CTEST ${VAR} ${ARGN})
-    endif()
-endmacro()
-
-set_test_prop(START           @START@)
-set_test_prop(END             @END@)
-set_test_prop(STRIDE          @STRIDE@)
-set_test_prop(INCLUDE         @INCLUDE@)
-set_test_prop(EXCLUDE         @EXCLUDE@)
-set_test_prop(INCLUDE_LABEL   @INCLUDE_LABEL@)
-set_test_prop(EXCLUDE_LABEL   @EXCLUDE_LABEL@)
-set_test_prop(PARALLEL_LEVEL  @PARALLEL_LEVEL@)
-set_test_prop(STOP_TIME       @STOP_TIME@)
-set_test_prop(COVERAGE_LABELS @LABELS@)
diff --git a/packages/kokkos/cmake/KokkosCI.cmake b/packages/kokkos/cmake/KokkosCI.cmake
deleted file mode 100644
index e8c9af37ad544a93a62f498e9a903696321a1c75..0000000000000000000000000000000000000000
--- a/packages/kokkos/cmake/KokkosCI.cmake
+++ /dev/null
@@ -1,350 +0,0 @@
-cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
-
-message(STATUS "")
-
-get_cmake_property(_cached_vars CACHE_VARIABLES)
-set(KOKKOS_CMAKE_ARGS)
-set(EXCLUDED_VARIABLES "CMAKE_COMMAND" "CMAKE_CPACK_COMMAND" "CMAKE_CTEST_COMMAND" "CMAKE_ROOT"
-                       "CTEST_ARGS" "BUILD_NAME" "CMAKE_CXX_FLAGS" "CMAKE_BUILD_TYPE")
-list(SORT _cached_vars)
-foreach(_var ${_cached_vars})
-    if(NOT "${_var}" IN_LIST EXCLUDED_VARIABLES)
-        list(APPEND KOKKOS_CMAKE_ARGS ${_var})
-        if("${_var}" STREQUAL "CMAKE_BUILD_TYPE")
-            set(BUILD_TYPE "${CMAKE_BUILD_TYPE}")
-        endif()
-    endif()
-endforeach()
-
-
-#----------------------------------------------------------------------------------------#
-#
-#   Macros and variables
-#
-#----------------------------------------------------------------------------------------#
-
-macro(CHECK_REQUIRED VAR)
-    if(NOT DEFINED ${VAR})
-        message(FATAL_ERROR "Error! Variable '${VAR}' must be defined")
-    endif()
-endmacro()
-
-# require the build name variable
-CHECK_REQUIRED(BUILD_NAME)
-
-# uses all args
-macro(SET_DEFAULT VAR)
-    if(NOT DEFINED ${VAR})
-        set(${VAR} ${ARGN})
-    endif()
-    # remove these ctest configuration variables from the defines
-    # passed to the Kokkos configuration
-    if("${VAR}" IN_LIST KOKKOS_CMAKE_ARGS)
-        list(REMOVE_ITEM KOKKOS_CMAKE_ARGS "${VAR}")
-    endif()
-endmacro()
-
-# uses first arg -- useful for selecting via priority from multiple
-# potentially defined variables, e.g.:
-#
-#   set_default_arg1(BUILD_NAME ${TRAVIS_BUILD_NAME} ${BUILD_NAME})
-#
-macro(SET_DEFAULT_ARG1 VAR)
-    if(NOT DEFINED ${VAR})
-        foreach(_ARG ${ARGN})
-            if(NOT "${_ARG}" STREQUAL "")
-                set(${VAR} ${_ARG})
-                break()
-            endif()
-        endforeach()
-    endif()
-    # remove these ctest configuration variables from the defines
-    # passed to the Kokkos configuration
-    if("${VAR}" IN_LIST KOKKOS_CMAKE_ARGS)
-        list(REMOVE_ITEM KOKKOS_CMAKE_ARGS "${VAR}")
-    endif()
-endmacro()
-
-# determine the default working directory
-if(NOT "$ENV{WORKSPACE}" STREQUAL "")
-    set(WORKING_DIR "$ENV{WORKSPACE}")
-else()
-    get_filename_component(WORKING_DIR ${CMAKE_CURRENT_LIST_DIR} DIRECTORY)
-endif()
-
-# determine the hostname
-execute_process(COMMAND hostname
-    OUTPUT_VARIABLE HOSTNAME
-    OUTPUT_STRIP_TRAILING_WHITESPACE)
-
-SET_DEFAULT(HOSTNAME "$ENV{HOSTNAME}")
-
-# get the number of processors
-include(ProcessorCount)
-ProcessorCount(NUM_PROCESSORS)
-
-# find git
-find_package(Git QUIET)
-if(NOT GIT_EXECUTABLE)
-    unset(GIT_EXECUTABLE CACHE)
-    unset(GIT_EXECUTABLE)
-endif()
-
-function(EXECUTE_GIT_COMMAND VAR)
-    set(${VAR} "" PARENT_SCOPE)
-    execute_process(COMMAND ${GIT_EXECUTABLE} ${ARGN}
-        OUTPUT_VARIABLE VAL
-        RESULT_VARIABLE RET
-        OUTPUT_STRIP_TRAILING_WHITESPACE
-        WORKING_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}
-        ERROR_QUIET)
-    string(REPLACE ";" " " _CMD "${GIT_EXECUTABLE} ${ARGN}")
-    set(LAST_GIT_COMMAND "${_CMD}" PARENT_SCOPE)
-    if(RET EQUAL 0)
-        set(${VAR} "${VAL}" PARENT_SCOPE)
-    endif()
-endfunction()
-
-# just gets the git branch name if available
-function(GET_GIT_BRANCH_NAME VAR)
-    execute_git_command(GIT_BRANCH branch --show-current)
-    set(_INVALID "%D" "HEAD")
-    if(NOT GIT_BRANCH OR "${GIT_BRANCH}" IN_LIST _INVALID)
-        execute_git_command(GIT_BRANCH show -s --format=%D)
-        if(NOT GIT_BRANCH OR "${GIT_BRANCH}" IN_LIST _INVALID)
-            execute_git_command(GIT_BRANCH --describe all)
-        endif()
-    endif()
-    #
-    if(GIT_BRANCH)
-        string(REPLACE " " ";" _DESC "${GIT_BRANCH}")
-        # just set it to last one via loop instead of wonky cmake index manip
-        foreach(_ITR ${_DESC})
-            set(GIT_BRANCH "${_ITR}")
-        endforeach()
-        set(${VAR} "${GIT_BRANCH}" PARENT_SCOPE)
-        message(STATUS "GIT BRANCH via '${LAST_GIT_COMMAND}': ${GIT_BRANCH}")
-    endif()
-endfunction()
-
-# just gets the git branch name if available
-function(GET_GIT_AUTHOR_NAME VAR)
-    execute_git_command(GIT_AUTHOR show -s --format=%an)
-    if(GIT_AUTHOR)
-        string(LENGTH "${GIT_AUTHOR}" STRLEN)
-        # if the build name gets too long, this can cause submission errors
-        if(STRLEN GREATER 24)
-            # remove middle initial
-            string(REGEX REPLACE " [A-Z]\. " " " GIT_AUTHOR "${GIT_AUTHOR}")
-            # get first and sur name
-            string(REGEX REPLACE "([A-Za-z]+) ([A-Za-z]+)" "\\1" F_NAME "${GIT_AUTHOR}")
-            string(REGEX REPLACE "([A-Za-z]+) ([A-Za-z]+)" "\\2" S_NAME "${GIT_AUTHOR}")
-            if(S_NAME)
-                set(GIT_AUTHOR "${S_NAME}")
-            elseif(F_NAME)
-                set(GIT_AUTHOR "${F_NAME}")
-            endif()
-        endif()
-        # remove any spaces, quotes, periods, etc.
-        string(REGEX REPLACE "[ ',;_\.\"]+" "" GIT_AUTHOR "${GIT_AUTHOR}")
-        set(${VAR} "${GIT_AUTHOR}" PARENT_SCOPE)
-        message(STATUS "GIT AUTHOR via '${LAST_GIT_COMMAND}': ${GIT_AUTHOR}")
-    endif()
-endfunction()
-
-# get the name of the branch
-GET_GIT_BRANCH_NAME(GIT_BRANCH)
-# get the name of the author
-GET_GIT_AUTHOR_NAME(GIT_AUTHOR)
-# author, prefer git method for consistency
-SET_DEFAULT_ARG1(AUTHOR ${GIT_AUTHOR} $ENV{GIT_AUTHOR} $ENV{AUTHOR})
-# SLUG == owner_name/repo_name
-SET_DEFAULT_ARG1(SLUG $ENV{TRAVIS_PULL_REQUEST_SLUG} $ENV{TRAVIS_REPO_SLUG} $ENV{APPVEYOR_REPO_NAME} $ENV{PULL_REQUEST_SLUG} $ENV{REPO_SLUG})
-# branch name
-SET_DEFAULT_ARG1(BRANCH $ENV{TRAVIS_PULL_REQUEST_BRANCH} $ENV{TRAVIS_BRANCH} $ENV{APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH} $ENV{APPVEYOR_REPO_BRANCH} $ENV{GIT_BRANCH} $ENV{BRANCH_NAME} $ENV{BRANCH} ${GIT_BRANCH})
-# pull request number
-SET_DEFAULT_ARG1(PULL_REQUEST_NUM $ENV{TRAVIS_PULL_REQUEST} $ENV{CHANGE_ID} $ENV{APPVEYOR_PULL_REQUEST_NUMBER} $ENV{PULL_REQUEST_NUM})
-# get the event type, e.g. push, pull_request, api, cron, etc.
-SET_DEFAULT_ARG1(EVENT_TYPE $ENV{TRAVIS_EVENT_TYPE} ${EVENT_TYPE})
-
-if("${BRANCH}" STREQUAL "")
-    message(STATUS "Checked: environment variables for Travis, Appveyor, Jenkins (git plugin), BRANCH_NAME, BRANCH and 'git branch --show-current'")
-    message(FATAL_ERROR "Error! Git branch could not be determined. Please provide -DBRANCH=<name>")
-endif()
-
-#----------------------------------------------------------------------------------------#
-#
-#   Set default values if not provided on command-line
-#
-#----------------------------------------------------------------------------------------#
-
-SET_DEFAULT(SOURCE_DIR      "${WORKING_DIR}")           # source directory
-SET_DEFAULT(BINARY_DIR      "${WORKING_DIR}/build")     # build directory
-SET_DEFAULT(BUILD_TYPE      "${CMAKE_BUILD_TYPE}")      # Release, Debug, etc.
-SET_DEFAULT(MODEL           "Continuous")               # Continuous, Nightly, or Experimental
-SET_DEFAULT(JOBS            1)                          # number of parallel ctests
-SET_DEFAULT(CTEST_COMMAND   "${CMAKE_CTEST_COMMAND}")   # just in case
-SET_DEFAULT(CTEST_ARGS      "-V --output-on-failure")   # extra arguments when ctest is called
-SET_DEFAULT(GIT_EXECUTABLE  "git")                      # ctest_update
-SET_DEFAULT(TARGET          "all")                      # build target
-SET_DEFAULT_ARG1(SITE       "$ENV{SITE}"
-                            "${HOSTNAME}")              # update site
-SET_DEFAULT_ARG1(BUILD_JOBS "$ENV{BUILD_JOBS}"
-                            "${NUM_PROCESSORS}")        # number of parallel compile jobs
-#
-#   The variable below correspond to ctest arguments, i.e. START,END,STRIDE are
-#   '-I START,END,STRIDE'
-#
-SET_DEFAULT(START           "")
-SET_DEFAULT(END             "")
-SET_DEFAULT(STRIDE          "")
-SET_DEFAULT(INCLUDE         "")
-SET_DEFAULT(EXCLUDE         "")
-SET_DEFAULT(INCLUDE_LABEL   "")
-SET_DEFAULT(EXCLUDE_LABEL   "")
-SET_DEFAULT(PARALLEL_LEVEL  "")
-SET_DEFAULT(STOP_TIME       "")
-SET_DEFAULT(LABELS          "")
-SET_DEFAULT(NOTES           "")
-
-# default static build tag for Nightly
-set(BUILD_TAG "${BRANCH}")
-
-if(NOT BUILD_TYPE)
-    # default for kokkos if not specified
-    set(BUILD_TYPE "RelWithDebInfo")
-endif()
-
-# generate dynamic name if continuous or experimental model
-if(NOT "${MODEL}" STREQUAL "Nightly")
-    if(EVENT_TYPE AND PULL_REQUEST_NUM)
-        # e.g. pull_request/123
-        if(AUTHOR)
-            set(BUILD_TAG "${AUTHOR}/${EVENT_TYPE}/${PULL_REQUEST_NUM}")
-        else()
-            set(BUILD_TAG "${EVENT_TYPE}/${PULL_REQUEST_NUM}")
-        endif()
-    elseif(SLUG)
-        # e.g. owner_name/repo_name
-        set(BUILD_TAG "${SLUG}")
-    elseif(AUTHOR)
-        set(BUILD_TAG "${AUTHOR}/${BRANCH}")
-    endif()
-    if(EVENT_TYPE AND NOT PULL_REQUEST_NUM)
-        set(BUILD_TAG "${BUILD_TAG}-${EVENT_TYPE}")
-    endif()
-endif()
-
-# unnecessary
-string(REPLACE "/remotes/" "/" BUILD_TAG "${BUILD_TAG}")
-string(REPLACE "/origin/" "/" BUILD_TAG "${BUILD_TAG}")
-
-message(STATUS "BUILD_TAG: ${BUILD_TAG}")
-
-set(BUILD_NAME "[${BUILD_TAG}] [${BUILD_NAME}-${BUILD_TYPE}]")
-
-# colons in build name create extra (empty) entries in CDash
-string(REPLACE ":" "-" BUILD_NAME "${BUILD_NAME}")
-# unnecessary info
-string(REPLACE "/merge]" "]" BUILD_NAME "${BUILD_NAME}")
-# consistency
-string(REPLACE "/pr/" "/pull/" BUILD_NAME "${BUILD_NAME}")
-string(REPLACE "pull_request/" "pull/" BUILD_NAME "${BUILD_NAME}")
-# miscellaneous from missing fields
-string(REPLACE "--" "-" BUILD_NAME "${BUILD_NAME}")
-string(REPLACE "-]" "]" BUILD_NAME "${BUILD_NAME}")
-
-# check binary directory
-if(EXISTS ${BINARY_DIR})
-    if(NOT IS_DIRECTORY "${BINARY_DIR}")
-        message(FATAL_ERROR "Error! '${BINARY_DIR}' already exists and is not a directory!")
-    endif()
-    file(GLOB BINARY_DIR_FILES "${BINARY_DIR}/*")
-    if(NOT "${BINARY_DIR_FILES}" STREQUAL "")
-        message(FATAL_ERROR "Error! '${BINARY_DIR}' already exists and is not empty!")
-    endif()
-endif()
-
-get_filename_component(SOURCE_REALDIR ${SOURCE_DIR} REALPATH)
-get_filename_component(BINARY_REALDIR ${BINARY_DIR} REALPATH)
-
-#----------------------------------------------------------------------------------------#
-#
-#   Generate the CTestConfig.cmake
-#
-#----------------------------------------------------------------------------------------#
-
-set(CONFIG_ARGS)
-foreach(_ARG ${KOKKOS_CMAKE_ARGS})
-    if(NOT "${${_ARG}}" STREQUAL "")
-        get_property(_ARG_TYPE CACHE ${_ARG} PROPERTY TYPE)
-        if("${_ARG_TYPE}" STREQUAL "UNINITIALIZED")
-            if("${${_ARG}}" STREQUAL "ON" OR "${${_ARG}}" STREQUAL "OFF")
-                set(_ARG_TYPE "BOOL")
-            elseif(EXISTS "${${_ARG}}" AND NOT IS_DIRECTORY "${${_ARG}}")
-                set(_ARG_TYPE "FILEPATH")
-            elseif(EXISTS "${${_ARG}}" AND IS_DIRECTORY "${${_ARG}}")
-                set(_ARG_TYPE "PATH")
-            elseif(NOT "${${_ARG}}" STREQUAL "")
-                set(_ARG_TYPE "STRING")
-            endif()
-        endif()
-        set(CONFIG_ARGS "${CONFIG_ARGS}set(${_ARG} \"${${_ARG}}\" CACHE ${_ARG_TYPE} \"\")\n")
-    endif()
-endforeach()
-
-file(WRITE ${BINARY_REALDIR}/initial-cache.cmake
-"
-set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS}\" CACHE STRING \"\")
-${CONFIG_ARGS}
-")
-
-file(READ ${BINARY_REALDIR}/initial-cache.cmake _CACHE_INFO)
-message(STATUS "Initial cache:\n${_CACHE_INFO}")
-
-# initialize the cache
-set(CONFIG_ARGS "-C ${BINARY_REALDIR}/initial-cache.cmake")
-
-
-# generate the CTestConfig.cmake
-configure_file(
-    ${CMAKE_CURRENT_LIST_DIR}/CTestConfig.cmake.in
-    ${BINARY_REALDIR}/CTestConfig.cmake
-    @ONLY)
-
-# copy/generate the dashboard script
-configure_file(
-    ${CMAKE_CURRENT_LIST_DIR}/KokkosCTest.cmake.in
-    ${BINARY_REALDIR}/KokkosCTest.cmake
-    @ONLY)
-
-# custom CTest settings go in ${BINARY_DIR}/CTestCustom.cmake
-execute_process(
-    COMMAND             ${CMAKE_COMMAND} -E touch CTestCustom.cmake
-    WORKING_DIRECTORY   ${BINARY_REALDIR}
-    )
-
-#----------------------------------------------------------------------------------------#
-#
-#   Execute CTest
-#
-#----------------------------------------------------------------------------------------#
-
-message(STATUS "")
-message(STATUS "BUILD_NAME: ${BUILD_NAME}")
-message(STATUS "Executing '${CTEST_COMMAND} -S KokkosCTest.cmake ${CTEST_ARGS}'...")
-message(STATUS "")
-
-# e.g. -DCTEST_ARGS="--output-on-failure -VV" should really be -DCTEST_ARGS="--output-on-failure;-VV"
-string(REPLACE " " ";" CTEST_ARGS "${CTEST_ARGS}")
-
-execute_process(
-    COMMAND             ${CTEST_COMMAND} -S KokkosCTest.cmake ${CTEST_ARGS}
-    RESULT_VARIABLE     RET
-    WORKING_DIRECTORY   ${BINARY_REALDIR}
-    )
-
-# ensure that any non-zero result variable gets propagated
-if(NOT RET EQUAL 0)
-    message(FATAL_ERROR "CTest return non-zero exit code: ${RET}")
-endif()
diff --git a/packages/kokkos/cmake/KokkosCTest.cmake.in b/packages/kokkos/cmake/KokkosCTest.cmake.in
deleted file mode 100644
index b6917f3cc1897aa6b1f0876560bb08c0c87b4c3a..0000000000000000000000000000000000000000
--- a/packages/kokkos/cmake/KokkosCTest.cmake.in
+++ /dev/null
@@ -1,261 +0,0 @@
-cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
-
-if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/CTestConfig.cmake")
-    include("${CMAKE_CURRENT_LIST_DIR}/CTestConfig.cmake")
-endif()
-
-include(ProcessorCount)
-ProcessorCount(CTEST_PROCESSOR_COUNT)
-
-cmake_policy(SET CMP0009 NEW)
-cmake_policy(SET CMP0011 NEW)
-
-# ---------------------------------------------------------------------------- #
-# -- Commands
-# ---------------------------------------------------------------------------- #
-find_program(CTEST_CMAKE_COMMAND    NAMES cmake)
-find_program(CTEST_UNAME_COMMAND    NAMES uname)
-
-find_program(CTEST_BZR_COMMAND      NAMES bzr)
-find_program(CTEST_CVS_COMMAND      NAMES cvs)
-find_program(CTEST_GIT_COMMAND      NAMES git)
-find_program(CTEST_HG_COMMAND       NAMES hg)
-find_program(CTEST_P4_COMMAND       NAMES p4)
-find_program(CTEST_SVN_COMMAND      NAMES svn)
-
-find_program(VALGRIND_COMMAND       NAMES valgrind)
-find_program(GCOV_COMMAND           NAMES gcov)
-find_program(LCOV_COMMAND           NAMES llvm-cov)
-find_program(MEMORYCHECK_COMMAND    NAMES valgrind )
-
-set(MEMORYCHECK_TYPE Valgrind)
-# set(MEMORYCHECK_TYPE Purify)
-# set(MEMORYCHECK_TYPE BoundsChecker)
-# set(MEMORYCHECK_TYPE ThreadSanitizer)
-# set(MEMORYCHECK_TYPE AddressSanitizer)
-# set(MEMORYCHECK_TYPE LeakSanitizer)
-# set(MEMORYCHECK_TYPE MemorySanitizer)
-# set(MEMORYCHECK_TYPE UndefinedBehaviorSanitizer)
-set(MEMORYCHECK_COMMAND_OPTIONS "--trace-children=yes --leak-check=full")
-
-# ---------------------------------------------------------------------------- #
-# -- Settings
-# ---------------------------------------------------------------------------- #
-## -- Process timeout in seconds
-set(CTEST_TIMEOUT           "7200")
-## -- Set output to English
-set(ENV{LC_MESSAGES}        "en_EN" )
-
-
-# ---------------------------------------------------------------------------- #
-# -- Copy ctest configuration file
-# ---------------------------------------------------------------------------- #
-macro(COPY_CTEST_CONFIG_FILES)
-
-    foreach(_FILE CTestConfig.cmake CTestCustom.cmake)
-
-        # if current directory is not binary or source directory
-        if(NOT "${CMAKE_CURRENT_LIST_DIR}" STREQUAL "${CTEST_BINARY_DIRECTORY}" AND
-           NOT "${CTEST_SOURCE_DIRECTORY}" STREQUAL "${CTEST_BINARY_DIRECTORY}")
-
-            # if file exists in current directory
-            if(EXISTS ${CMAKE_CURRENT_LIST_DIR}/${_FILE})
-                configure_file(${CMAKE_CURRENT_LIST_DIR}/${_FILE}
-                    ${CTEST_BINARY_DIRECTORY}/${_FILE} COPYONLY)
-            endif()
-
-        # if source and binary differ
-        elseif(NOT "${CTEST_SOURCE_DIRECTORY}" STREQUAL "${CTEST_BINARY_DIRECTORY}")
-
-            # if file exists in source directory but not in binary directory
-            if(EXISTS ${CTEST_SOURCE_DIRECTORY}/${_FILE} AND
-               NOT EXISTS ${CTEST_BINARY_DIRECTORY}/${_FILE})
-                configure_file(${CTEST_SOURCE_DIRECTORY}/${_FILE}
-                    ${CTEST_BINARY_DIRECTORY}/${_FILE} COPYONLY)
-            endif()
-
-        endif()
-    endforeach()
-
-endmacro()
-
-ctest_read_custom_files("${CMAKE_CURRENT_LIST_DIR}")
-
-message(STATUS "CTEST_MODEL: ${CTEST_MODEL}")
-
-#-------------------------------------------------------------------------#
-# Start
-#
-message(STATUS "")
-message(STATUS "[${CTEST_BUILD_NAME}] Running START_CTEST stage...")
-message(STATUS "")
-
-ctest_start(${CTEST_MODEL} TRACK ${CTEST_MODEL} ${APPEND_CTEST}
-    ${CTEST_SOURCE_DIRECTORY} ${CTEST_BINARY_DIRECTORY})
-
-
-#-------------------------------------------------------------------------#
-# Config
-#
-copy_ctest_config_files()
-ctest_read_custom_files("${CTEST_BINARY_DIRECTORY}")
-
-
-#-------------------------------------------------------------------------#
-# Update
-#
-message(STATUS "")
-message(STATUS "[${CTEST_BUILD_NAME}] Running CTEST_UPDATE stage...")
-message(STATUS "")
-
-ctest_update(SOURCE "${CTEST_SOURCE_DIRECTORY}"
-    RETURN_VALUE up_ret)
-
-
-#-------------------------------------------------------------------------#
-# Configure
-#
-message(STATUS "")
-message(STATUS "[${CTEST_BUILD_NAME}] Running CTEST_CONFIGURE stage...")
-message(STATUS "")
-
-ctest_configure(BUILD "${CTEST_BINARY_DIRECTORY}"
-    SOURCE ${CTEST_SOURCE_DIRECTORY}
-    ${APPEND_CTEST}
-    OPTIONS "${CTEST_CONFIGURE_OPTIONS}"
-    RETURN_VALUE config_ret)
-
-
-#-------------------------------------------------------------------------#
-# Echo configure log bc Damien wants to delay merging this PR for eternity
-#
-file(GLOB _configure_log "${CTEST_BINARY_DIRECTORY}/Testing/Temporary/LastConfigure*.log")
-# should only have one but loop just for safety
-foreach(_LOG ${_configure_log})
-    file(READ ${_LOG} _LOG_MESSAGE)
-    message(STATUS "Configure Log: ${_LOG}")
-    message(STATUS "\n${_LOG_MESSAGE}\n")
-endforeach()
-
-
-#-------------------------------------------------------------------------#
-# Build
-#
-message(STATUS "")
-message(STATUS "[${CTEST_BUILD_NAME}] Running CTEST_BUILD stage...")
-message(STATUS "")
-
-ctest_build(BUILD "${CTEST_BINARY_DIRECTORY}"
-    ${APPEND_CTEST}
-    RETURN_VALUE build_ret)
-
-
-#-------------------------------------------------------------------------#
-# Echo build log bc Damien wants to delay merging this PR for eternity
-#
-file(GLOB _build_log "${CTEST_BINARY_DIRECTORY}/Testing/Temporary/LastBuild*.log")
-# should only have one but loop just for safety
-foreach(_LOG ${_build_log})
-    file(READ ${_LOG} _LOG_MESSAGE)
-    message(STATUS "Build Log: ${_LOG}")
-    message(STATUS "\n${_LOG_MESSAGE}\n")
-endforeach()
-
-
-#-------------------------------------------------------------------------#
-# Test
-#
-message(STATUS "")
-message(STATUS "[${CTEST_BUILD_NAME}] Running CTEST_TEST stage...")
-message(STATUS "")
-
-ctest_test(RETURN_VALUE test_ret
-    ${APPEND_CTEST}
-    ${START_CTEST}
-    ${END_CTEST}
-    ${STRIDE_CTEST}
-    ${INCLUDE_CTEST}
-    ${EXCLUDE_CTEST}
-    ${INCLUDE_LABEL_CTEST}
-    ${EXCLUDE_LABEL_CTEST}
-    ${PARALLEL_LEVEL_CTEST}
-    ${STOP_TIME_CTEST}
-    SCHEDULE_RANDOM OFF)
-
-
-#-------------------------------------------------------------------------#
-# Coverage
-#
-message(STATUS "")
-message(STATUS "[${CTEST_BUILD_NAME}] Running CTEST_COVERAGE stage...")
-message(STATUS "")
-
-execute_process(COMMAND ${CTEST_COVERAGE_COMMAND} ${CTEST_COVERAGE_EXTRA_FLAGS}
-    WORKING_DIRECTORY ${CTEST_BINARY_DIRECTORY}
-    ERROR_QUIET)
-
-ctest_coverage(${APPEND_CTEST}
-    ${CTEST_COVERAGE_LABELS}
-    RETURN_VALUE cov_ret)
-
-
-#-------------------------------------------------------------------------#
-# MemCheck
-#
-message(STATUS "")
-message(STATUS "[${CTEST_BUILD_NAME}] Running CTEST_MEMCHECK stage...")
-message(STATUS "")
-
-ctest_memcheck(RETURN_VALUE mem_ret
-    ${APPEND_CTEST}
-    ${START_CTEST}
-    ${END_CTEST}
-    ${STRIDE_CTEST}
-    ${INCLUDE_CTEST}
-    ${EXCLUDE_CTEST}
-    ${INCLUDE_LABEL_CTEST}
-    ${EXCLUDE_LABEL_CTEST}
-    ${PARALLEL_LEVEL_CTEST})
-
-
-#-------------------------------------------------------------------------#
-# Submit
-#
-message(STATUS "")
-message(STATUS "[${CTEST_BUILD_NAME}] Running CTEST_SUBMIT stage...")
-message(STATUS "")
-
-file(GLOB_RECURSE NOTE_FILES "${CTEST_BINARY_DIRECTORY}/*CTestNotes.cmake")
-foreach(_FILE ${NOTE_FILES})
-    message(STATUS "Including CTest notes files: \"${_FILE}\"...")
-    include("${_FILE}")
-endforeach()
-
-# capture submit error so it doesn't fail because of a submission error
-ctest_submit(RETURN_VALUE submit_ret
-    RETRY_COUNT 2
-    RETRY_DELAY 10
-    CAPTURE_CMAKE_ERROR submit_err)
-
-#-------------------------------------------------------------------------#
-# Submit
-#
-message(STATUS "")
-message(STATUS "[${CTEST_BUILD_NAME}] Finished ${CTEST_MODEL} Stages (${STAGES})")
-message(STATUS "")
-
-
-#-------------------------------------------------------------------------#
-# Non-zero exit codes for important errors
-#
-if(NOT config_ret EQUAL 0)
-    message(FATAL_ERROR "Error during configuration! Exit code: ${config_ret}")
-endif()
-
-if(NOT build_ret EQUAL 0)
-    message(FATAL_ERROR "Error during build! Exit code: ${build_ret}")
-endif()
-
-if(NOT test_ret EQUAL 0)
-    message(FATAL_ERROR "Error during testing! Exit code: ${test_ret}")
-endif()
diff --git a/packages/kokkos/cmake/KokkosCore_config.h.in b/packages/kokkos/cmake/KokkosCore_config.h.in
index fbfae3711ec14573b4c3067aea4a8625d6b2ad8c..07baa0a5f09a708d344a9ef72510baa6f4b8e15b 100644
--- a/packages/kokkos/cmake/KokkosCore_config.h.in
+++ b/packages/kokkos/cmake/KokkosCore_config.h.in
@@ -41,6 +41,7 @@
 #cmakedefine KOKKOS_ENABLE_CUDA_LAMBDA
 #cmakedefine KOKKOS_ENABLE_CUDA_CONSTEXPR
 #cmakedefine KOKKOS_ENABLE_CUDA_LDG_INTRINSIC
+#cmakedefine KOKKOS_ENABLE_IMPL_CUDA_MALLOC_ASYNC
 #cmakedefine KOKKOS_ENABLE_HIP_RELOCATABLE_DEVICE_CODE
 #cmakedefine KOKKOS_ENABLE_HPX_ASYNC_DISPATCH
 #cmakedefine KOKKOS_ENABLE_DEBUG
@@ -49,17 +50,21 @@
 #cmakedefine KOKKOS_ENABLE_COMPILER_WARNINGS
 #cmakedefine KOKKOS_ENABLE_PROFILING_LOAD_PRINT
 #cmakedefine KOKKOS_ENABLE_TUNING
-#cmakedefine KOKKOS_ENABLE_DEPRECATED_CODE
+#cmakedefine KOKKOS_ENABLE_DEPRECATED_CODE_3
+#cmakedefine KOKKOS_ENABLE_DEPRECATION_WARNINGS
 #cmakedefine KOKKOS_ENABLE_LARGE_MEM_TESTS
 #cmakedefine KOKKOS_ENABLE_DUALVIEW_MODIFY_CHECK
 #cmakedefine KOKKOS_ENABLE_COMPLEX_ALIGN
-#cmakedefine KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION
+#cmakedefine KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+#cmakedefine KOKKOS_OPT_RANGE_AGGRESSIVE_VECTORIZATION  // deprecated
+#cmakedefine KOKKOS_ENABLE_AGGRESSIVE_VECTORIZATION
 
 /* TPL Settings */
 #cmakedefine KOKKOS_ENABLE_HWLOC
 #cmakedefine KOKKOS_USE_LIBRT
 #cmakedefine KOKKOS_ENABLE_HBWSPACE
 #cmakedefine KOKKOS_ENABLE_LIBDL
+#cmakedefine KOKKOS_ENABLE_LIBQUADMATH
 #cmakedefine KOKKOS_IMPL_CUDA_CLANG_WORKAROUND
 
 #cmakedefine KOKKOS_COMPILER_CUDA_VERSION @KOKKOS_COMPILER_CUDA_VERSION@
@@ -79,6 +84,12 @@
 #cmakedefine KOKKOS_ARCH_POWER8
 #cmakedefine KOKKOS_ARCH_POWER9
 #cmakedefine KOKKOS_ARCH_INTEL_GEN
+#cmakedefine KOKKOS_ARCH_INTEL_DG1
+#cmakedefine KOKKOS_ARCH_INTEL_GEN9
+#cmakedefine KOKKOS_ARCH_INTEL_GEN11
+#cmakedefine KOKKOS_ARCH_INTEL_GEN12LP
+#cmakedefine KOKKOS_ARCH_INTEL_XEHP
+#cmakedefine KOKKOS_ARCH_INTEL_GPU
 #cmakedefine KOKKOS_ARCH_KEPLER
 #cmakedefine KOKKOS_ARCH_KEPLER30
 #cmakedefine KOKKOS_ARCH_KEPLER32
@@ -95,9 +106,11 @@
 #cmakedefine KOKKOS_ARCH_VOLTA70
 #cmakedefine KOKKOS_ARCH_VOLTA72
 #cmakedefine KOKKOS_ARCH_TURING75
+#cmakedefine KOKKOS_ARCH_AMPERE
 #cmakedefine KOKKOS_ARCH_AMPERE80
 #cmakedefine KOKKOS_ARCH_AMPERE86
 #cmakedefine KOKKOS_ARCH_AMD_ZEN
 #cmakedefine KOKKOS_ARCH_AMD_ZEN2
+#cmakedefine KOKKOS_ARCH_AMD_ZEN3
 
 #cmakedefine KOKKOS_IMPL_DISABLE_SYCL_DEVICE_PRINTF
diff --git a/packages/kokkos/cmake/Modules/FindTPLCUDA.cmake b/packages/kokkos/cmake/Modules/FindTPLCUDA.cmake
index 8d58d96415808499dc39d44ad3600f5f5a64368e..0c825c59e04248f2cd76d5faf9c6aa16a663bbb1 100644
--- a/packages/kokkos/cmake/Modules/FindTPLCUDA.cmake
+++ b/packages/kokkos/cmake/Modules/FindTPLCUDA.cmake
@@ -29,7 +29,12 @@ ELSE()
 ENDIF()
 
 include(FindPackageHandleStandardArgs)
-FIND_PACKAGE_HANDLE_STANDARD_ARGS(TPLCUDA DEFAULT_MSG FOUND_CUDART FOUND_CUDA_DRIVER)
+IF(KOKKOS_CXX_HOST_COMPILER_ID STREQUAL PGI)
+  SET(KOKKOS_CUDA_ERROR "Using NVHPC as host compiler requires at least CMake 3.20.1")
+ELSE()
+  SET(KOKKOS_CUDA_ERROR DEFAULT_MSG)
+ENDIF()
+FIND_PACKAGE_HANDLE_STANDARD_ARGS(TPLCUDA ${KOKKOS_CUDA_ERROR} FOUND_CUDART FOUND_CUDA_DRIVER)
 IF (FOUND_CUDA_DRIVER AND FOUND_CUDART)
   KOKKOS_CREATE_IMPORTED_TPL(CUDA INTERFACE
     LINK_LIBRARIES CUDA::cuda_driver CUDA::cudart
diff --git a/packages/kokkos/cmake/Modules/FindTPLLIBQUADMATH.cmake b/packages/kokkos/cmake/Modules/FindTPLLIBQUADMATH.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..be70b711e0f92ac9d99e3c3fdd2770430f6c2b68
--- /dev/null
+++ b/packages/kokkos/cmake/Modules/FindTPLLIBQUADMATH.cmake
@@ -0,0 +1 @@
+KOKKOS_FIND_IMPORTED(LIBQUADMATH HEADER quadmath.h LIBRARY quadmath)
diff --git a/packages/kokkos/cmake/deps/quadmath.cmake b/packages/kokkos/cmake/deps/quadmath.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..826f5021d359e99c2aed8b695de7b601dabaf453
--- /dev/null
+++ b/packages/kokkos/cmake/deps/quadmath.cmake
@@ -0,0 +1,46 @@
+# @HEADER
+# ************************************************************************
+#
+#                        Kokkos v. 3.0
+#       Copyright (2020) National Technology & Engineering
+#               Solutions of Sandia, LLC (NTESS).
+#
+# Under the terms of Contract DE-NA0003525 with NTESS,
+# the U.S. Government retains certain rights in this software.
+#
+# 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 Corporation nor the names of the
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+# 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.
+#
+# Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+#
+# ************************************************************************
+# @HEADER
+
+KOKKOS_TPL_FIND_INCLUDE_DIRS_AND_LIBRARIES( quadmath
+  REQUIRED_HEADERS quadmath.h
+  REQUIRED_LIBS_NAMES quadmath
+)
diff --git a/packages/kokkos/cmake/kokkos_arch.cmake b/packages/kokkos/cmake/kokkos_arch.cmake
index ec18e70a36a34dbecc305f978e0d7b84c482da37..c4637339f31fa47e80d9b53a1435b3ddc7641573 100644
--- a/packages/kokkos/cmake/kokkos_arch.cmake
+++ b/packages/kokkos/cmake/kokkos_arch.cmake
@@ -63,11 +63,17 @@ KOKKOS_ARCH_OPTION(AMPERE80        GPU  "NVIDIA Ampere generation CC 8.0")
 KOKKOS_ARCH_OPTION(AMPERE86        GPU  "NVIDIA Ampere generation CC 8.6")
 KOKKOS_ARCH_OPTION(ZEN             HOST "AMD Zen architecture")
 KOKKOS_ARCH_OPTION(ZEN2            HOST "AMD Zen2 architecture")
+KOKKOS_ARCH_OPTION(ZEN3            HOST "AMD Zen3 architecture")
 KOKKOS_ARCH_OPTION(VEGA900         GPU  "AMD GPU MI25 GFX900")
 KOKKOS_ARCH_OPTION(VEGA906         GPU  "AMD GPU MI50/MI60 GFX906")
 KOKKOS_ARCH_OPTION(VEGA908         GPU  "AMD GPU MI100 GFX908")
+KOKKOS_ARCH_OPTION(VEGA90A         GPU  "" )
 KOKKOS_ARCH_OPTION(INTEL_GEN       GPU  "Intel GPUs Gen9+")
-
+KOKKOS_ARCH_OPTION(INTEL_DG1       GPU  "Intel Iris XeMAX GPU")
+KOKKOS_ARCH_OPTION(INTEL_GEN9      GPU  "Intel GPU Gen9")
+KOKKOS_ARCH_OPTION(INTEL_GEN11     GPU  "Intel GPU Gen11")
+KOKKOS_ARCH_OPTION(INTEL_GEN12LP   GPU  "Intel GPU Gen12LP")
+KOKKOS_ARCH_OPTION(INTEL_XEHP      GPU  "Intel GPU Xe-HP")
 
 
 IF(KOKKOS_ENABLE_COMPILER_WARNINGS)
@@ -75,6 +81,12 @@ IF(KOKKOS_ENABLE_COMPILER_WARNINGS)
     "-Wall" "-Wunused-parameter" "-Wshadow" "-pedantic"
     "-Wsign-compare" "-Wtype-limits" "-Wuninitialized")
 
+  # NOTE KOKKOS_ prefixed variable (all uppercase) is not set yet because TPLs are processed after ARCH
+  IF(Kokkos_ENABLE_LIBQUADMATH)
+    # warning: non-standard suffix on floating constant [-Wpedantic]
+    LIST(REMOVE_ITEM COMMON_WARNINGS "-pedantic")
+  ENDIF()
+
   # OpenMPTarget compilers give erroneous warnings about sign comparison in loops
   IF(KOKKOS_ENABLE_OPENMPTARGET)
     LIST(REMOVE_ITEM COMMON_WARNINGS "-Wsign-compare")
@@ -85,7 +97,7 @@ IF(KOKKOS_ENABLE_COMPILER_WARNINGS)
 
   COMPILER_SPECIFIC_FLAGS(
     COMPILER_ID CMAKE_CXX_COMPILER_ID
-    PGI         NO-VALUE-SPECIFIED
+    NVHPC       NO-VALUE-SPECIFIED
     GNU         ${GNU_WARNINGS}
     DEFAULT     ${COMMON_WARNINGS}
   )
@@ -157,16 +169,18 @@ ENDIF()
 
 IF (KOKKOS_ARCH_ARMV80)
   COMPILER_SPECIFIC_FLAGS(
-    Cray NO-VALUE-SPECIFIED
-    PGI  NO-VALUE-SPECIFIED
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
+    Cray    NO-VALUE-SPECIFIED
+    NVHPC   NO-VALUE-SPECIFIED
     DEFAULT -march=armv8-a
   )
 ENDIF()
 
 IF (KOKKOS_ARCH_ARMV81)
   COMPILER_SPECIFIC_FLAGS(
-    Cray NO-VALUE-SPECIFIED
-    PGI  NO-VALUE-SPECIFIED
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
+    Cray    NO-VALUE-SPECIFIED
+    NVHPC   NO-VALUE-SPECIFIED
     DEFAULT -march=armv8.1-a
   )
 ENDIF()
@@ -174,8 +188,9 @@ ENDIF()
 IF (KOKKOS_ARCH_ARMV8_THUNDERX)
   SET(KOKKOS_ARCH_ARMV80 ON) #Not a cache variable
   COMPILER_SPECIFIC_FLAGS(
-    Cray NO-VALUE-SPECIFIED
-    PGI  NO-VALUE-SPECIFIED
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
+    Cray    NO-VALUE-SPECIFIED
+    NVHPC   NO-VALUE-SPECIFIED
     DEFAULT -march=armv8-a -mtune=thunderx
   )
 ENDIF()
@@ -183,23 +198,28 @@ ENDIF()
 IF (KOKKOS_ARCH_ARMV8_THUNDERX2)
   SET(KOKKOS_ARCH_ARMV81 ON) #Not a cache variable
   COMPILER_SPECIFIC_FLAGS(
-    Cray NO-VALUE-SPECIFIED
-    PGI  NO-VALUE-SPECIFIED
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
+    Cray    NO-VALUE-SPECIFIED
+    NVHPC   NO-VALUE-SPECIFIED
     DEFAULT -mcpu=thunderx2t99 -mtune=thunderx2t99
   )
 ENDIF()
 
 IF (KOKKOS_ARCH_A64FX)
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
+    NVHPC   NO-VALUE-SPECIFIED
     DEFAULT -march=armv8.2-a+sve
-    Clang -march=armv8.2-a+sve -msve-vector-bits=512
-    GCC -march=armv8.2-a+sve -msve-vector-bits=512
+    Clang   -march=armv8.2-a+sve -msve-vector-bits=512
+    GCC     -march=armv8.2-a+sve -msve-vector-bits=512
   )
 ENDIF()
 
 IF (KOKKOS_ARCH_ZEN)
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
     Intel   -mavx2
+    NVHPC   -tp=zen
     DEFAULT -march=znver1 -mtune=znver1
   )
   SET(KOKKOS_ARCH_AMD_ZEN  ON)
@@ -208,17 +228,31 @@ ENDIF()
 
 IF (KOKKOS_ARCH_ZEN2)
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
     Intel   -mavx2
+    NVHPC   -tp=zen2
     DEFAULT -march=znver2 -mtune=znver2
   )
   SET(KOKKOS_ARCH_AMD_ZEN2 ON)
   SET(KOKKOS_ARCH_AMD_AVX2 ON)
 ENDIF()
 
+IF (KOKKOS_ARCH_ZEN3)
+  COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
+    Intel   -mavx2
+    NVHPC   -tp=zen2
+    DEFAULT -march=znver3 -mtune=znver3
+  )
+  SET(KOKKOS_ARCH_AMD_ZEN3 ON)
+  SET(KOKKOS_ARCH_AMD_AVX2 ON)
+ENDIF()
+
 IF (KOKKOS_ARCH_WSM)
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
     Intel   -xSSE4.2
-    PGI     -tp=nehalem
+    NVHPC   -tp=px
     Cray    NO-VALUE-SPECIFIED
     DEFAULT -msse4.2
   )
@@ -228,8 +262,9 @@ ENDIF()
 IF (KOKKOS_ARCH_SNB OR KOKKOS_ARCH_AMDAVX)
   SET(KOKKOS_ARCH_AVX ON)
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
     Intel   -mavx
-    PGI     -tp=sandybridge
+    NVHPC   -tp=sandybridge
     Cray    NO-VALUE-SPECIFIED
     DEFAULT -mavx
   )
@@ -238,8 +273,9 @@ ENDIF()
 IF (KOKKOS_ARCH_HSW)
   SET(KOKKOS_ARCH_AVX2 ON)
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
     Intel   -xCORE-AVX2
-    PGI     -tp=haswell
+    NVHPC   -tp=haswell
     Cray    NO-VALUE-SPECIFIED
     DEFAULT -march=core-avx2 -mtune=core-avx2
   )
@@ -248,8 +284,9 @@ ENDIF()
 IF (KOKKOS_ARCH_BDW)
   SET(KOKKOS_ARCH_AVX2 ON)
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
     Intel   -xCORE-AVX2
-    PGI     -tp=haswell
+    NVHPC   -tp=haswell
     Cray    NO-VALUE-SPECIFIED
     DEFAULT -march=core-avx2 -mtune=core-avx2 -mrtm
   )
@@ -259,8 +296,9 @@ IF (KOKKOS_ARCH_KNL)
   #avx512-mic
   SET(KOKKOS_ARCH_AVX512MIC ON) #not a cache variable
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
     Intel   -xMIC-AVX512
-    PGI     NO-VALUE-SPECIFIED
+    NVHPC   -tp=knl
     Cray    NO-VALUE-SPECIFIED
     DEFAULT -march=knl -mtune=knl
   )
@@ -269,6 +307,7 @@ ENDIF()
 IF (KOKKOS_ARCH_KNC)
   SET(KOKKOS_USE_ISA_KNC ON)
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
     DEFAULT -mmic
   )
 ENDIF()
@@ -277,14 +316,15 @@ IF (KOKKOS_ARCH_SKX)
   #avx512-xeon
   SET(KOKKOS_ARCH_AVX512XEON ON)
   COMPILER_SPECIFIC_FLAGS(
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
     Intel   -xCORE-AVX512
-    PGI     NO-VALUE-SPECIFIED
+    NVHPC   -tp=skylake
     Cray    NO-VALUE-SPECIFIED
     DEFAULT -march=skylake-avx512 -mtune=skylake-avx512 -mrtm
   )
 ENDIF()
 
-IF (KOKKOS_ARCH_WSM OR KOKKOS_ARCH_SNB OR KOKKOS_ARCH_HSW OR KOKKOS_ARCH_BDW OR KOKKOS_ARCH_KNL OR KOKKOS_ARCH_SKX OR KOKKOS_ARCH_ZEN OR KOKKOS_ARCH_ZEN2)
+IF (KOKKOS_ARCH_WSM OR KOKKOS_ARCH_SNB OR KOKKOS_ARCH_HSW OR KOKKOS_ARCH_BDW OR KOKKOS_ARCH_KNL OR KOKKOS_ARCH_SKX OR KOKKOS_ARCH_ZEN OR KOKKOS_ARCH_ZEN2 OR KOKKOS_ARCH_ZEN3)
   SET(KOKKOS_USE_ISA_X86_64 ON)
 ENDIF()
 
@@ -294,7 +334,8 @@ ENDIF()
 
 IF (KOKKOS_ARCH_POWER7)
   COMPILER_SPECIFIC_FLAGS(
-    PGI     NO-VALUE-SPECIFIED
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
+    NVHPC   NO-VALUE-SPECIFIED
     DEFAULT -mcpu=power7 -mtune=power7
   )
   SET(KOKKOS_USE_ISA_POWERPCBE ON)
@@ -302,16 +343,16 @@ ENDIF()
 
 IF (KOKKOS_ARCH_POWER8)
   COMPILER_SPECIFIC_FLAGS(
-    PGI     NO-VALUE-SPECIFIED
-    NVIDIA  NO-VALUE-SPECIFIED
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
+    NVHPC   -tp=pwr8
     DEFAULT -mcpu=power8 -mtune=power8
   )
 ENDIF()
 
 IF (KOKKOS_ARCH_POWER9)
   COMPILER_SPECIFIC_FLAGS(
-    PGI     NO-VALUE-SPECIFIED
-    NVIDIA  NO-VALUE-SPECIFIED
+    COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
+    NVHPC   -tp=pwr9
     DEFAULT -mcpu=power9 -mtune=power9
   )
 ENDIF()
@@ -358,7 +399,7 @@ ENDIF()
 
 IF (KOKKOS_ENABLE_SYCL)
   COMPILER_SPECIFIC_FLAGS(
-    DEFAULT -fsycl
+    DEFAULT -fsycl -fno-sycl-id-queries-fit-in-int
   )
   COMPILER_SPECIFIC_OPTIONS(
     DEFAULT -fsycl-unnamed-lambda
@@ -433,20 +474,58 @@ ENDFUNCTION()
 CHECK_AMDGPU_ARCH(VEGA900 gfx900) # Radeon Instinct MI25
 CHECK_AMDGPU_ARCH(VEGA906 gfx906) # Radeon Instinct MI50 and MI60
 CHECK_AMDGPU_ARCH(VEGA908 gfx908)
+CHECK_AMDGPU_ARCH(VEGA90A gfx90a)
 
 IF(KOKKOS_ENABLE_HIP AND NOT AMDGPU_ARCH_ALREADY_SPECIFIED)
-  MESSAGE(SEND_ERROR "HIP enabled but no AMD GPU architecture currently enabled. "
-                     "Please enable one AMD GPU architecture via -DKokkos_ARCH_{..}=ON'.")
+  IF(KOKKOS_CXX_COMPILER_ID STREQUAL HIPCC)
+    FIND_PROGRAM(ROCM_ENUMERATOR rocm_agent_enumerator)
+    EXECUTE_PROCESS(COMMAND ${ROCM_ENUMERATOR} OUTPUT_VARIABLE GPU_ARCHS)
+    STRING(LENGTH "${GPU_ARCHS}" len_str)
+    # enumerator always output gfx000 as the first line
+    IF(${len_str} LESS 8)
+      MESSAGE(SEND_ERROR "HIP enabled but no AMD GPU architecture currently enabled. "
+                         "Please enable one AMD GPU architecture via -DKokkos_ARCH_{..}=ON'.")
+    ENDIF()
+  ELSE()
+    MESSAGE(SEND_ERROR "HIP enabled but no AMD GPU architecture currently enabled. "
+                       "Please enable one AMD GPU architecture via -DKokkos_ARCH_{..}=ON'.")
+  ENDIF()
+ENDIF()
+
+MACRO(CHECK_MULTIPLE_INTEL_ARCH)
+  IF(KOKKOS_ARCH_INTEL_GPU)
+    MESSAGE(FATAL_ERROR "Specifying multiple Intel GPU architectures is not allowed!")
+  ENDIF()
+  SET(KOKKOS_ARCH_INTEL_GPU ON)
+ENDMACRO()
+
+IF(KOKKOS_ARCH_INTEL_GEN)
+  CHECK_MULTIPLE_INTEL_ARCH()
+ENDIF()
+IF(KOKKOS_ARCH_INTEL_DG1)
+  CHECK_MULTIPLE_INTEL_ARCH()
+ENDIF()
+IF(KOKKOS_ARCH_INTEL_GEN9)
+  CHECK_MULTIPLE_INTEL_ARCH()
+ENDIF()
+IF(KOKKOS_ARCH_INTEL_GEN11)
+  CHECK_MULTIPLE_INTEL_ARCH()
+ENDIF()
+IF(KOKKOS_ARCH_INTEL_GEN12LP)
+  CHECK_MULTIPLE_INTEL_ARCH()
+ENDIF()
+IF(KOKKOS_ARCH_INTEL_XEHP)
+  CHECK_MULTIPLE_INTEL_ARCH()
 ENDIF()
 
 IF (KOKKOS_ENABLE_OPENMPTARGET)
   SET(CLANG_CUDA_ARCH ${KOKKOS_CUDA_ARCH_FLAG})
   IF (CLANG_CUDA_ARCH)
-    STRING(REPLACE "sm_" "cc" PGI_CUDA_ARCH ${CLANG_CUDA_ARCH})
+    STRING(REPLACE "sm_" "cc" NVHPC_CUDA_ARCH ${CLANG_CUDA_ARCH})
     COMPILER_SPECIFIC_FLAGS(
       Clang -Xopenmp-target -march=${CLANG_CUDA_ARCH} -fopenmp-targets=nvptx64-nvidia-cuda
-      XL -qtgtarch=${KOKKOS_CUDA_ARCH_FLAG}
-      PGI -gpu=${PGI_CUDA_ARCH}
+      XL    -qtgtarch=${KOKKOS_CUDA_ARCH_FLAG}
+      NVHPC -gpu=${NVHPC_CUDA_ARCH}
     )
   ENDIF()
   SET(CLANG_AMDGPU_ARCH ${KOKKOS_AMDGPU_ARCH_FLAG})
@@ -455,9 +534,9 @@ IF (KOKKOS_ENABLE_OPENMPTARGET)
       Clang -Xopenmp-target=amdgcn-amd-amdhsa -march=${CLANG_AMDGPU_ARCH} -fopenmp-targets=amdgcn-amd-amdhsa
     )
   ENDIF()
-  IF (KOKKOS_ARCH_INTEL_GEN)
+  IF (KOKKOS_ARCH_INTEL_GPU)
     COMPILER_SPECIFIC_FLAGS(
-      IntelClang -fopenmp-targets=spir64 -D__STRICT_ANSI__
+      IntelLLVM -fopenmp-targets=spir64 -D__STRICT_ANSI__
     )
   ENDIF()
 ENDIF()
@@ -475,7 +554,27 @@ IF (KOKKOS_ENABLE_SYCL)
     ENDIF()
   ELSEIF(KOKKOS_ARCH_INTEL_GEN)
     COMPILER_SPECIFIC_FLAGS(
-      DEFAULT -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device skl"
+      DEFAULT -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device gen9-"
+    )
+  ELSEIF(KOKKOS_ARCH_INTEL_GEN9)
+    COMPILER_SPECIFIC_FLAGS(
+      DEFAULT -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device gen9"
+    )
+  ELSEIF(KOKKOS_ARCH_INTEL_GEN11)
+    COMPILER_SPECIFIC_FLAGS(
+      DEFAULT -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device gen11"
+    )
+  ELSEIF(KOKKOS_ARCH_INTEL_GEN12LP)
+    COMPILER_SPECIFIC_FLAGS(
+      DEFAULT -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device gen12lp"
+    )
+  ELSEIF(KOKKOS_ARCH_INTEL_DG1)
+    COMPILER_SPECIFIC_FLAGS(
+      DEFAULT -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device dg1"
+    )
+  ELSEIF(KOKKOS_ARCH_INTEL_XEHP)
+    COMPILER_SPECIFIC_FLAGS(
+      DEFAULT -fsycl-targets=spir64_gen-unknown-unknown-sycldevice -Xsycl-target-backend "-device xehp"
     )
   ENDIF()
 ENDIF()
diff --git a/packages/kokkos/cmake/kokkos_compiler_id.cmake b/packages/kokkos/cmake/kokkos_compiler_id.cmake
index 4434d6928f46429ad7525c944a0c1c6c351c4cdd..5afed4fb0e7ba0cd2bca8250b6f58e4434f483ec 100644
--- a/packages/kokkos/cmake/kokkos_compiler_id.cmake
+++ b/packages/kokkos/cmake/kokkos_compiler_id.cmake
@@ -101,7 +101,7 @@ IF(KOKKOS_CXX_COMPILER_ID STREQUAL Clang)
                   OUTPUT_STRIP_TRAILING_WHITESPACE)
   IF (INTERNAL_HAVE_INTEL_COMPILER) #not actually Clang
     SET(KOKKOS_CLANG_IS_INTEL TRUE)
-    SET(KOKKOS_CXX_COMPILER_ID IntelClang CACHE STRING INTERNAL FORCE)
+    SET(KOKKOS_CXX_COMPILER_ID IntelLLVM CACHE STRING INTERNAL FORCE)
   ENDIF()
 ENDIF()
 
@@ -137,7 +137,7 @@ SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n    Clang      4.0.0 or higher"
 SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n    GCC        5.3.0 or higher")
 SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n    Intel     17.0.0 or higher")
 SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n    NVCC      9.2.88 or higher")
-SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n    HIPCC      3.8.0 or higher")
+SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n    HIPCC      4.2.0 or higher")
 SET(KOKKOS_MESSAGE_TEXT "${KOKKOS_MESSAGE_TEXT}\n    PGI         17.4 or higher\n")
 
 IF(KOKKOS_CXX_COMPILER_ID STREQUAL Clang)
@@ -158,13 +158,23 @@ ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL NVIDIA)
   ENDIF()
   SET(CMAKE_CXX_EXTENSIONS OFF CACHE BOOL "Kokkos turns off CXX extensions" FORCE)
 ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL HIPCC)
-  IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 3.8.0)
+  IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 4.2.0)
     MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}")
   ENDIF()
 ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL PGI)
   IF(KOKKOS_CXX_COMPILER_VERSION VERSION_LESS 17.4)
     MESSAGE(FATAL_ERROR "${KOKKOS_MESSAGE_TEXT}")
   ENDIF()
+  # Treat PGI internally as NVHPC to simplify handling both compilers.
+  # Before CMake 3.20 NVHPC was identified as PGI, nvc++ is
+  # backward-compatible to pgc++.
+  SET(KOKKOS_CXX_COMPILER_ID NVHPC CACHE STRING INTERNAL FORCE)
+ENDIF()
+
+IF(NOT DEFINED KOKKOS_CXX_HOST_COMPILER_ID)
+  SET(KOKKOS_CXX_HOST_COMPILER_ID ${KOKKOS_CXX_COMPILER_ID})
+ELSEIF(KOKKOS_CXX_HOST_COMPILER_ID STREQUAL PGI)
+  SET(KOKKOS_CXX_HOST_COMPILER_ID NVHPC CACHE STRING INTERNAL FORCE)
 ENDIF()
 
 STRING(REPLACE "." ";" VERSION_LIST ${KOKKOS_CXX_COMPILER_VERSION})
diff --git a/packages/kokkos/cmake/kokkos_enable_devices.cmake b/packages/kokkos/cmake/kokkos_enable_devices.cmake
index 445dad47ce561979037bf5b1622413ddda05f3b3..7fd0794036454da9d8a246fd4a3a19fe2e5cf0ef 100644
--- a/packages/kokkos/cmake/kokkos_enable_devices.cmake
+++ b/packages/kokkos/cmake/kokkos_enable_devices.cmake
@@ -61,8 +61,8 @@ IF(KOKKOS_ENABLE_OPENMP)
     COMPILER_SPECIFIC_FLAGS(
       COMPILER_ID KOKKOS_CXX_HOST_COMPILER_ID
       Clang      -Xcompiler ${ClangOpenMPFlag}
-      IntelClang -Xcompiler -fiopenmp
-      PGI        -Xcompiler -mp
+      IntelLLVM  -Xcompiler -fiopenmp
+      NVHPC      -Xcompiler -mp
       Cray       NO-VALUE-SPECIFIED
       XL         -Xcompiler -qsmp=omp
       DEFAULT    -Xcompiler -fopenmp
@@ -70,9 +70,9 @@ IF(KOKKOS_ENABLE_OPENMP)
   ELSE()
     COMPILER_SPECIFIC_FLAGS(
       Clang      ${ClangOpenMPFlag}
-      IntelClang -fiopenmp
+      IntelLLVM  -fiopenmp
       AppleClang -Xpreprocessor -fopenmp
-      PGI        -mp
+      NVHPC      -mp
       Cray       NO-VALUE-SPECIFIED
       XL         -qsmp=omp
       DEFAULT    -fopenmp
@@ -92,9 +92,9 @@ IF (KOKKOS_ENABLE_OPENMPTARGET)
 
   COMPILER_SPECIFIC_FLAGS(
     Clang      ${ClangOpenMPFlag} -Wno-openmp-mapping
-    IntelClang -fiopenmp -Wno-openmp-mapping
+    IntelLLVM  -fiopenmp -Wno-openmp-mapping
     XL         -qsmp=omp -qoffload -qnoeh
-    PGI        -mp=gpu
+    NVHPC      -mp=gpu
     DEFAULT    -fopenmp
   )
   COMPILER_SPECIFIC_DEFS(
diff --git a/packages/kokkos/cmake/kokkos_enable_options.cmake b/packages/kokkos/cmake/kokkos_enable_options.cmake
index 95bce66c7bee32f8800cbd6e0324f9d4c599c97c..4cb8bd20f5ecb3e519ef64d9e1c31c0a5cb7e431 100644
--- a/packages/kokkos/cmake/kokkos_enable_options.cmake
+++ b/packages/kokkos/cmake/kokkos_enable_options.cmake
@@ -26,9 +26,16 @@ KOKKOS_CFG_DEPENDS(OPTIONS COMPILER_ID)
 # Put a check in just in case people are using this option
 KOKKOS_DEPRECATED_LIST(OPTIONS ENABLE)
 
+# Set the Default for Desul Atomics usage.
+set(_DESUL_ATOMICS_DEFAULT ON)
+
 KOKKOS_ENABLE_OPTION(CUDA_RELOCATABLE_DEVICE_CODE  OFF "Whether to enable relocatable device code (RDC) for CUDA")
 KOKKOS_ENABLE_OPTION(CUDA_UVM             OFF "Whether to use unified memory (UM) for CUDA by default")
 KOKKOS_ENABLE_OPTION(CUDA_LDG_INTRINSIC   OFF "Whether to use CUDA LDG intrinsics")
+# As of 08/12/2021 CudaMallocAsync causes issues if UCX is used as MPI communication layer.
+KOKKOS_ENABLE_OPTION(IMPL_CUDA_MALLOC_ASYNC      OFF  "Whether to enable CudaMallocAsync (requires CUDA Toolkit 11.2)")
+KOKKOS_ENABLE_OPTION(DEPRECATED_CODE_3    ON "Whether code deprecated in major release 3 is available" )
+KOKKOS_ENABLE_OPTION(DEPRECATION_WARNINGS ON "Whether to emit deprecation warnings" )
 KOKKOS_ENABLE_OPTION(HIP_RELOCATABLE_DEVICE_CODE  OFF "Whether to enable relocatable device code (RDC) for HIP")
 KOKKOS_ENABLE_OPTION(HPX_ASYNC_DISPATCH   OFF "Whether HPX supports asynchronous dispatch")
 KOKKOS_ENABLE_OPTION(TESTS         OFF  "Whether to build the unit tests")
@@ -50,6 +57,9 @@ KOKKOS_ENABLE_OPTION(TUNING               OFF "Whether to create bindings for tu
 KOKKOS_ENABLE_OPTION(AGGRESSIVE_VECTORIZATION OFF "Whether to aggressively vectorize loops")
 KOKKOS_ENABLE_OPTION(LAUNCH_COMPILER      ON  "Whether to potentially use the launch compiler")
 
+# This option will go away eventually, but allows fallback to old implementation when needed.
+KOKKOS_ENABLE_OPTION(IMPL_DESUL_ATOMICS   ON  "Whether to use desul based atomics - option only during beta")
+
 IF (KOKKOS_ENABLE_CUDA)
   SET(KOKKOS_COMPILER_CUDA_VERSION "${KOKKOS_COMPILER_VERSION_MAJOR}${KOKKOS_COMPILER_VERSION_MINOR}")
 ENDIF()
diff --git a/packages/kokkos/cmake/kokkos_functions.cmake b/packages/kokkos/cmake/kokkos_functions.cmake
index 858322394d7aefcb9fe23f55a60863f3a8f63484..02c9a911b1b827994b7d4a1e0c004cfb55afd749 100644
--- a/packages/kokkos/cmake/kokkos_functions.cmake
+++ b/packages/kokkos/cmake/kokkos_functions.cmake
@@ -773,7 +773,7 @@ FUNCTION(kokkos_link_tpl TARGET)
 ENDFUNCTION()
 
 FUNCTION(COMPILER_SPECIFIC_OPTIONS_HELPER)
-  SET(COMPILERS NVIDIA PGI XL DEFAULT Cray Intel Clang AppleClang IntelClang GNU HIPCC Fujitsu)
+  SET(COMPILERS NVIDIA NVHPC XL XLClang DEFAULT Cray Intel Clang AppleClang IntelLLVM GNU HIPCC Fujitsu)
   CMAKE_PARSE_ARGUMENTS(
     PARSE
     "LINK_OPTIONS;COMPILE_OPTIONS;COMPILE_DEFINITIONS;LINK_LIBRARIES"
diff --git a/packages/kokkos/cmake/kokkos_test_cxx_std.cmake b/packages/kokkos/cmake/kokkos_test_cxx_std.cmake
index 707fb000af528694780d6668f160a3fee3472a69..1eb0592c7f054185e566f053faa931029f92fbc1 100644
--- a/packages/kokkos/cmake/kokkos_test_cxx_std.cmake
+++ b/packages/kokkos/cmake/kokkos_test_cxx_std.cmake
@@ -140,7 +140,7 @@ IF (NOT KOKKOS_CXX_STANDARD_FEATURE)
   IF(KOKKOS_CXX_COMPILER_ID STREQUAL Cray)
     INCLUDE(${KOKKOS_SRC_PATH}/cmake/cray.cmake)
     kokkos_set_cray_flags(${KOKKOS_CXX_STANDARD} ${KOKKOS_CXX_INTERMEDIATE_STANDARD})
-  ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL PGI)
+  ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC)
     INCLUDE(${KOKKOS_SRC_PATH}/cmake/pgi.cmake)
     kokkos_set_pgi_flags(${KOKKOS_CXX_STANDARD} ${KOKKOS_CXX_INTERMEDIATE_STANDARD})
   ELSEIF(KOKKOS_CXX_COMPILER_ID STREQUAL Intel)
diff --git a/packages/kokkos/cmake/kokkos_tpls.cmake b/packages/kokkos/cmake/kokkos_tpls.cmake
index d8d044c9d75384a1d8d312a94708623c735d121f..51bad521c4878c00b6b8c7587d7233c26a1d4ba9 100644
--- a/packages/kokkos/cmake/kokkos_tpls.cmake
+++ b/packages/kokkos/cmake/kokkos_tpls.cmake
@@ -67,6 +67,12 @@ SET(PTHREAD_DEFAULT OFF)
 ENDIF()
 KOKKOS_TPL_OPTION(PTHREAD ${PTHREAD_DEFAULT} TRIBITS Pthread)
 
+IF(Trilinos_ENABLE_Kokkos AND TPL_ENABLE_quadmath)
+  SET(LIBQUADMATH_DEFAULT ON)
+ELSE()
+  SET(LIBQUADMATH_DEFAULT OFF)
+ENDIF()
+KOKKOS_TPL_OPTION(LIBQUADMATH ${LIBQUADMATH_DEFAULT} TRIBITS quadmath)
 
 #Make sure we use our local FindKokkosCuda.cmake
 KOKKOS_IMPORT_TPL(HPX INTERFACE)
@@ -78,6 +84,7 @@ KOKKOS_IMPORT_TPL(LIBDL)
 KOKKOS_IMPORT_TPL(MEMKIND)
 KOKKOS_IMPORT_TPL(PTHREAD INTERFACE)
 KOKKOS_IMPORT_TPL(ROCM INTERFACE)
+KOKKOS_IMPORT_TPL(LIBQUADMATH)
 
 #Convert list to newlines (which CMake doesn't always like in cache variables)
 STRING(REPLACE ";" "\n" KOKKOS_TPL_EXPORT_TEMP "${KOKKOS_TPL_EXPORTS}")
diff --git a/packages/kokkos/cmake/tpls/FindTPLquadmath.cmake b/packages/kokkos/cmake/tpls/FindTPLquadmath.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..1f7587da808fd587f6380079f9a672f124b3a25b
--- /dev/null
+++ b/packages/kokkos/cmake/tpls/FindTPLquadmath.cmake
@@ -0,0 +1,46 @@
+# @HEADER
+# ************************************************************************
+#
+#                        Kokkos v. 3.0
+#       Copyright (2020) National Technology & Engineering
+#               Solutions of Sandia, LLC (NTESS).
+#
+# Under the terms of Contract DE-NA0003525 with NTESS,
+# the U.S. Government retains certain rights in this software.
+#
+# 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 Corporation nor the names of the
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+# 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.
+#
+# Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+#
+# ************************************************************************
+# @HEADER
+
+TRIBITS_TPL_FIND_INCLUDE_DIRS_AND_LIBRARIES( quadmath
+  REQUIRED_HEADERS quadmath.h
+  REQUIRED_LIBS_NAMES quadmath
+)
diff --git a/packages/kokkos/containers/performance_tests/TestDynRankView.hpp b/packages/kokkos/containers/performance_tests/TestDynRankView.hpp
index 8c507c76621d09b134ad94f12da589e8c31a014c..7ed9a0271a51db453ff29a982e57cd17d70a642d 100644
--- a/packages/kokkos/containers/performance_tests/TestDynRankView.hpp
+++ b/packages/kokkos/containers/performance_tests/TestDynRankView.hpp
@@ -48,7 +48,7 @@
 #include <Kokkos_DynRankView.hpp>
 #include <vector>
 
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 // Compare performance of DynRankView to View, specific focus on the parenthesis
 // operators
diff --git a/packages/kokkos/containers/performance_tests/TestGlobal2LocalIds.hpp b/packages/kokkos/containers/performance_tests/TestGlobal2LocalIds.hpp
index 65de551b2715f1eb31f4385fa0cb2a455bca6a4f..16b74a4997e5f1e643e095e167253829d47a050a 100644
--- a/packages/kokkos/containers/performance_tests/TestGlobal2LocalIds.hpp
+++ b/packages/kokkos/containers/performance_tests/TestGlobal2LocalIds.hpp
@@ -48,7 +48,7 @@
 #include <vector>
 #include <algorithm>
 
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 // This test will simulate global ids
 
diff --git a/packages/kokkos/containers/performance_tests/TestScatterView.hpp b/packages/kokkos/containers/performance_tests/TestScatterView.hpp
index 0f3ba103efc5d09d012e3cc35cbfa41fa8be9170..8a23f59d32cdd4f6290465ad41fa70d521e39bfb 100644
--- a/packages/kokkos/containers/performance_tests/TestScatterView.hpp
+++ b/packages/kokkos/containers/performance_tests/TestScatterView.hpp
@@ -46,7 +46,7 @@
 #define KOKKOS_TEST_SCATTER_VIEW_HPP
 
 #include <Kokkos_ScatterView.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 namespace Perf {
 
diff --git a/packages/kokkos/containers/performance_tests/TestUnorderedMapPerformance.hpp b/packages/kokkos/containers/performance_tests/TestUnorderedMapPerformance.hpp
index c31412552ad696ada0dad4fd1058f76290282256..4547d5c35758e2eadc0e5029779f0d2e23fc4081 100644
--- a/packages/kokkos/containers/performance_tests/TestUnorderedMapPerformance.hpp
+++ b/packages/kokkos/containers/performance_tests/TestUnorderedMapPerformance.hpp
@@ -43,7 +43,7 @@
 #ifndef KOKKOS_TEST_UNORDERED_MAP_PERFORMANCE_HPP
 #define KOKKOS_TEST_UNORDERED_MAP_PERFORMANCE_HPP
 
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 #include <iostream>
 #include <iomanip>
diff --git a/packages/kokkos/containers/src/Kokkos_Bitset.hpp b/packages/kokkos/containers/src/Kokkos_Bitset.hpp
index ea1d6dde5d26e9baf719281a0d8f13bb80ec59f8..c5b66f05a3ce0b7778fdcbc8e7a3e766301273d0 100644
--- a/packages/kokkos/containers/src/Kokkos_Bitset.hpp
+++ b/packages/kokkos/containers/src/Kokkos_Bitset.hpp
@@ -76,20 +76,25 @@ class Bitset {
   using execution_space = Device;
   using size_type       = unsigned int;
 
-  enum { BIT_SCAN_REVERSE = 1u };
-  enum { MOVE_HINT_BACKWARD = 2u };
-
-  enum {
-    BIT_SCAN_FORWARD_MOVE_HINT_FORWARD  = 0u,
-    BIT_SCAN_REVERSE_MOVE_HINT_FORWARD  = BIT_SCAN_REVERSE,
-    BIT_SCAN_FORWARD_MOVE_HINT_BACKWARD = MOVE_HINT_BACKWARD,
-    BIT_SCAN_REVERSE_MOVE_HINT_BACKWARD = BIT_SCAN_REVERSE | MOVE_HINT_BACKWARD
-  };
+  static constexpr unsigned BIT_SCAN_REVERSE   = 1u;
+  static constexpr unsigned MOVE_HINT_BACKWARD = 2u;
+
+  static constexpr unsigned BIT_SCAN_FORWARD_MOVE_HINT_FORWARD = 0u;
+  static constexpr unsigned BIT_SCAN_REVERSE_MOVE_HINT_FORWARD =
+      BIT_SCAN_REVERSE;
+  static constexpr unsigned BIT_SCAN_FORWARD_MOVE_HINT_BACKWARD =
+      MOVE_HINT_BACKWARD;
+  static constexpr unsigned BIT_SCAN_REVERSE_MOVE_HINT_BACKWARD =
+      BIT_SCAN_REVERSE | MOVE_HINT_BACKWARD;
 
  private:
-  enum { block_size = static_cast<unsigned>(sizeof(unsigned) * CHAR_BIT) };
-  enum { block_mask = block_size - 1u };
-  enum { block_shift = Kokkos::Impl::integral_power_of_two(block_size) };
+  enum : unsigned {
+    block_size = static_cast<unsigned>(sizeof(unsigned) * CHAR_BIT)
+  };
+  enum : unsigned { block_mask = block_size - 1u };
+  enum : unsigned {
+    block_shift = Kokkos::Impl::integral_power_of_two(block_size)
+  };
 
  public:
   /// constructor
@@ -317,14 +322,18 @@ class ConstBitset {
   enum { block_shift = Kokkos::Impl::integral_power_of_two(block_size) };
 
  public:
+  KOKKOS_FUNCTION
   ConstBitset() : m_size(0) {}
 
+  KOKKOS_FUNCTION
   ConstBitset(Bitset<Device> const& rhs)
       : m_size(rhs.m_size), m_blocks(rhs.m_blocks) {}
 
+  KOKKOS_FUNCTION
   ConstBitset(ConstBitset<Device> const& rhs)
       : m_size(rhs.m_size), m_blocks(rhs.m_blocks) {}
 
+  KOKKOS_FUNCTION
   ConstBitset<Device>& operator=(Bitset<Device> const& rhs) {
     this->m_size   = rhs.m_size;
     this->m_blocks = rhs.m_blocks;
@@ -332,6 +341,7 @@ class ConstBitset {
     return *this;
   }
 
+  KOKKOS_FUNCTION
   ConstBitset<Device>& operator=(ConstBitset<Device> const& rhs) {
     this->m_size   = rhs.m_size;
     this->m_blocks = rhs.m_blocks;
diff --git a/packages/kokkos/containers/src/Kokkos_DualView.hpp b/packages/kokkos/containers/src/Kokkos_DualView.hpp
index 45710d1f737ca14348dd79d698bbc4a581225bbb..f55d0f2b7f3f10b43ea4ee076dc4dea191010449 100644
--- a/packages/kokkos/containers/src/Kokkos_DualView.hpp
+++ b/packages/kokkos/containers/src/Kokkos_DualView.hpp
@@ -597,8 +597,10 @@ class DualView : public ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type> {
     }
     if (std::is_same<typename t_host::memory_space,
                      typename t_dev::memory_space>::value) {
-      typename t_dev::execution_space().fence();
-      typename t_host::execution_space().fence();
+      typename t_dev::execution_space().fence(
+          "Kokkos::DualView<>::sync: fence after syncing DualView");
+      typename t_host::execution_space().fence(
+          "Kokkos::DualView<>::sync: fence after syncing DualView");
     }
   }
 
@@ -776,10 +778,11 @@ class DualView : public ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type> {
   /// If \c Device is the same as this DualView's device type, then
   /// mark the device's data as modified.  Otherwise, mark the host's
   /// data as modified.
-  template <class Device>
+  template <class Device, class Dummy = DualView,
+            std::enable_if_t<!Dummy::impl_dualview_is_single_device::value>* =
+                nullptr>
   void modify() {
     if (modified_flags.data() == nullptr) return;
-    if (impl_dualview_is_single_device::value) return;
     int dev = get_device_side<Device>();
 
     if (dev == 1) {  // if Device is the same as DualView's device type
@@ -811,8 +814,17 @@ class DualView : public ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type> {
 #endif
   }
 
+  template <
+      class Device, class Dummy = DualView,
+      std::enable_if_t<Dummy::impl_dualview_is_single_device::value>* = nullptr>
+  void modify() {
+    return;
+  }
+
+  template <class Dummy = DualView,
+            std::enable_if_t<!Dummy::impl_dualview_is_single_device::value>* =
+                nullptr>
   inline void modify_host() {
-    if (impl_dualview_is_single_device::value) return;
     if (modified_flags.data() != nullptr) {
       modified_flags(0) =
           (modified_flags(1) > modified_flags(0) ? modified_flags(1)
@@ -832,8 +844,17 @@ class DualView : public ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type> {
     }
   }
 
+  template <
+      class Dummy = DualView,
+      std::enable_if_t<Dummy::impl_dualview_is_single_device::value>* = nullptr>
+  inline void modify_host() {
+    return;
+  }
+
+  template <class Dummy = DualView,
+            std::enable_if_t<!Dummy::impl_dualview_is_single_device::value>* =
+                nullptr>
   inline void modify_device() {
-    if (impl_dualview_is_single_device::value) return;
     if (modified_flags.data() != nullptr) {
       modified_flags(1) =
           (modified_flags(1) > modified_flags(0) ? modified_flags(1)
@@ -853,6 +874,13 @@ class DualView : public ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type> {
     }
   }
 
+  template <
+      class Dummy = DualView,
+      std::enable_if_t<Dummy::impl_dualview_is_single_device::value>* = nullptr>
+  inline void modify_device() {
+    return;
+  }
+
   inline void clear_sync_state() {
     if (modified_flags.data() != nullptr)
       modified_flags(1) = modified_flags(0) = 0;
@@ -875,8 +903,15 @@ class DualView : public ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type> {
                const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
                const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
                const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
-    ::Kokkos::realloc(d_view, n0, n1, n2, n3, n4, n5, n6, n7);
-    h_view = create_mirror_view(d_view);
+    const size_t new_extents[8] = {n0, n1, n2, n3, n4, n5, n6, n7};
+    const bool sizeMismatch =
+        Impl::size_mismatch(h_view, h_view.rank_dynamic, new_extents);
+
+    if (sizeMismatch) {
+      ::Kokkos::realloc(d_view, n0, n1, n2, n3, n4, n5, n6, n7);
+      h_view = create_mirror_view(d_view);
+    } else
+      ::Kokkos::deep_copy(d_view, typename t_dev::value_type{});
 
     /* Reset dirty flags */
     if (modified_flags.data() == nullptr) {
@@ -897,41 +932,31 @@ class DualView : public ViewTraits<DataType, Arg1Type, Arg2Type, Arg3Type> {
               const size_t n5 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
               const size_t n6 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
               const size_t n7 = KOKKOS_IMPL_CTOR_DEFAULT_ARG) {
+    const size_t new_extents[8] = {n0, n1, n2, n3, n4, n5, n6, n7};
+    const bool sizeMismatch =
+        Impl::size_mismatch(h_view, h_view.rank_dynamic, new_extents);
+
     if (modified_flags.data() == nullptr) {
       modified_flags = t_modified_flags("DualView::modified_flags");
     }
     if (modified_flags(1) >= modified_flags(0)) {
       /* Resize on Device */
-      ::Kokkos::resize(d_view, n0, n1, n2, n3, n4, n5, n6, n7);
-      h_view = create_mirror_view(d_view);
-
-      /* Mark Device copy as modified */
-      modified_flags(1) = modified_flags(1) + 1;
+      if (sizeMismatch) {
+        ::Kokkos::resize(d_view, n0, n1, n2, n3, n4, n5, n6, n7);
+        h_view = create_mirror_view(d_view);
 
+        /* Mark Device copy as modified */
+        modified_flags(1) = modified_flags(1) + 1;
+      }
     } else {
       /* Realloc on Device */
-
-      ::Kokkos::realloc(d_view, n0, n1, n2, n3, n4, n5, n6, n7);
-
-      const bool sizeMismatch =
-          (h_view.extent(0) != n0) || (h_view.extent(1) != n1) ||
-          (h_view.extent(2) != n2) || (h_view.extent(3) != n3) ||
-          (h_view.extent(4) != n4) || (h_view.extent(5) != n5) ||
-          (h_view.extent(6) != n6) || (h_view.extent(7) != n7);
-      if (sizeMismatch)
+      if (sizeMismatch) {
         ::Kokkos::resize(h_view, n0, n1, n2, n3, n4, n5, n6, n7);
+        d_view = create_mirror_view(typename t_dev::execution_space(), h_view);
 
-      t_host temp_view = create_mirror_view(d_view);
-
-      /* Remap on Host */
-      Kokkos::deep_copy(temp_view, h_view);
-
-      h_view = temp_view;
-
-      d_view = create_mirror_view(typename t_dev::execution_space(), h_view);
-
-      /* Mark Host copy as modified */
-      modified_flags(0) = modified_flags(0) + 1;
+        /* Mark Host copy as modified */
+        modified_flags(0) = modified_flags(0) + 1;
+      }
     }
   }
 
diff --git a/packages/kokkos/containers/src/Kokkos_DynRankView.hpp b/packages/kokkos/containers/src/Kokkos_DynRankView.hpp
index c6323fef93694de1ee39d5784141bf6991f78bd7..b673c53a4ef8e8a760c613332418ae5d600a6812 100644
--- a/packages/kokkos/containers/src/Kokkos_DynRankView.hpp
+++ b/packages/kokkos/containers/src/Kokkos_DynRankView.hpp
@@ -1140,7 +1140,8 @@ class DynRankView : public ViewTraits<DataType, Properties...> {
     // to avoid incomplete type errors from usng Kokkos::Cuda directly.
     if (std::is_same<Kokkos::CudaUVMSpace,
                      typename traits::device_type::memory_space>::value) {
-      typename traits::device_type::memory_space::execution_space().fence();
+      typename traits::device_type::memory_space::execution_space().fence(
+          "Kokkos::DynRankView<>::DynRankView: fence before UVM allocation");
     }
 #endif
     //------------------------------------------------------------
@@ -1154,7 +1155,8 @@ class DynRankView : public ViewTraits<DataType, Properties...> {
 #if defined(KOKKOS_ENABLE_CUDA)
     if (std::is_same<Kokkos::CudaUVMSpace,
                      typename traits::device_type::memory_space>::value) {
-      typename traits::device_type::memory_space::execution_space().fence();
+      typename traits::device_type::memory_space::execution_space().fence(
+          "Kokkos::DynRankView<>::DynRankView: fence after UVM allocation");
     }
 #endif
     //------------------------------------------------------------
@@ -1404,7 +1406,7 @@ class ViewMapping<
 
   template <class MemoryTraits>
   struct apply {
-    static_assert(Kokkos::Impl::is_memory_traits<MemoryTraits>::value, "");
+    static_assert(Kokkos::is_memory_traits<MemoryTraits>::value, "");
 
     using traits_type =
         Kokkos::ViewTraits<data_type, array_layout,
@@ -1574,7 +1576,7 @@ KOKKOS_INLINE_FUNCTION bool operator!=(const DynRankView<LT, LP...>& lhs,
 namespace Kokkos {
 namespace Impl {
 
-template <class OutputView, typename Enable = void>
+template <class OutputView, class Enable = void>
 struct DynRankViewFill {
   using const_value_type = typename OutputView::traits::const_value_type;
 
@@ -1693,9 +1695,11 @@ inline void deep_copy(
                    typename ViewTraits<DT, DP...>::value_type>::value,
       "deep_copy requires non-const type");
 
-  Kokkos::fence();
+  Kokkos::fence(
+      "Kokkos::deep_copy(DynRankView, value_type): fence before filling view");
   Kokkos::Impl::DynRankViewFill<DynRankView<DT, DP...> >(dst, value);
-  Kokkos::fence();
+  Kokkos::fence(
+      "Kokkos::deep_copy(DynRankView, value_type): fence after filling view");
 }
 
 /** \brief  Deep copy into a value in Host memory from a view.  */
@@ -1711,10 +1715,13 @@ inline void deep_copy(
 
   using src_traits       = ViewTraits<ST, SP...>;
   using src_memory_space = typename src_traits::memory_space;
-  Kokkos::fence();
+  Kokkos::fence(
+      "Kokkos::deep_copy(value_type, DynRankView): fence before copying "
+      "value");
   Kokkos::Impl::DeepCopy<HostSpace, src_memory_space>(&dst, src.data(),
                                                       sizeof(ST));
-  Kokkos::fence();
+  Kokkos::fence(
+      "Kokkos::deep_copy(value_type, DynRankView): fence after copying value");
 }
 
 //----------------------------------------------------------------------------
@@ -1744,14 +1751,14 @@ inline void deep_copy(
 
   enum {
     DstExecCanAccessSrc =
-        Kokkos::Impl::SpaceAccessibility<dst_execution_space,
-                                         src_memory_space>::accessible
+        Kokkos::SpaceAccessibility<dst_execution_space,
+                                   src_memory_space>::accessible
   };
 
   enum {
     SrcExecCanAccessDst =
-        Kokkos::Impl::SpaceAccessibility<src_execution_space,
-                                         dst_memory_space>::accessible
+        Kokkos::SpaceAccessibility<src_execution_space,
+                                   dst_memory_space>::accessible
   };
 
   if ((void*)dst.data() != (void*)src.data()) {
@@ -1762,10 +1769,14 @@ inline void deep_copy(
     // memory then can byte-wise copy
     if (rank(src) == 0 && rank(dst) == 0) {
       using value_type = typename dst_type::value_type;
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence before "
+          "copying rank-0 views");
       Kokkos::Impl::DeepCopy<dst_memory_space, src_memory_space>(
           dst.data(), src.data(), sizeof(value_type));
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence after "
+          "copying rank-0 views");
     } else if (std::is_same<
                    typename DstType::traits::value_type,
                    typename SrcType::traits::non_const_value_type>::value &&
@@ -1787,10 +1798,14 @@ inline void deep_copy(
                dst.extent(6) == src.extent(6) &&
                dst.extent(7) == src.extent(7)) {
       const size_t nbytes = sizeof(typename dst_type::value_type) * dst.span();
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence before "
+          "copying rank-1 views");
       Kokkos::Impl::DeepCopy<dst_memory_space, src_memory_space>(
           dst.data(), src.data(), nbytes);
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence after "
+          "copying rank-1 views");
     } else if (std::is_same<
                    typename DstType::traits::value_type,
                    typename SrcType::traits::non_const_value_type>::value &&
@@ -1817,29 +1832,43 @@ inline void deep_copy(
                dst.stride_6() == src.stride_6() &&
                dst.stride_7() == src.stride_7()) {
       const size_t nbytes = sizeof(typename dst_type::value_type) * dst.span();
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence before "
+          "copying rank-1 views");
       Kokkos::Impl::DeepCopy<dst_memory_space, src_memory_space>(
           dst.data(), src.data(), nbytes);
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence after "
+          "copying rank-1 views");
     } else if (DstExecCanAccessSrc) {
       // Copying data between views in accessible memory spaces and either
       // non-contiguous or incompatible shape.
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence before "
+          "remapping views of incompatible shape");
       Kokkos::Impl::DynRankViewRemap<dst_type, src_type>(dst, src);
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence after "
+          "remapping views of incompatible shape");
     } else if (SrcExecCanAccessDst) {
       // Copying data between views in accessible memory spaces and either
       // non-contiguous or incompatible shape.
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence before "
+          "remapping views of incompatible shape");
       Kokkos::Impl::DynRankViewRemap<dst_type, src_type, src_execution_space>(
           dst, src);
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence after "
+          "remapping views of incompatible shape");
     } else {
       Kokkos::Impl::throw_runtime_exception(
           "deep_copy given views that would require a temporary allocation");
     }
   } else {
-    Kokkos::fence();
+    Kokkos::fence(
+        "Kokkos::Impl::DeepCopy(DynRankView, DynRankView): fence due to same "
+        "src and dst");
   }
 }
 
diff --git a/packages/kokkos/containers/src/Kokkos_DynamicView.hpp b/packages/kokkos/containers/src/Kokkos_DynamicView.hpp
index cc949d4c556ab4abd982ea5334fee870c42ef305..2c764f535c585a4f545300d619b83917f327f414 100644
--- a/packages/kokkos/containers/src/Kokkos_DynamicView.hpp
+++ b/packages/kokkos/containers/src/Kokkos_DynamicView.hpp
@@ -53,36 +53,201 @@
 namespace Kokkos {
 namespace Experimental {
 
-// Simple metafunction for choosing memory space
-// In the current implementation, if memory_space == CudaSpace,
-// use CudaUVMSpace for the chunk 'array' allocation, which
-// contains will contain pointers to chunks of memory allocated
-// in CudaSpace
 namespace Impl {
-template <class MemSpace>
-struct ChunkArraySpace {
-  using memory_space = MemSpace;
-};
 
-#ifdef KOKKOS_ENABLE_CUDA
-template <>
-struct ChunkArraySpace<Kokkos::CudaSpace> {
-  using memory_space = typename Kokkos::CudaUVMSpace;
-};
-#endif
-#ifdef KOKKOS_ENABLE_HIP
-template <>
-struct ChunkArraySpace<Kokkos::Experimental::HIPSpace> {
-  using memory_space = typename Kokkos::Experimental::HIPHostPinnedSpace;
-};
-#endif
-#ifdef KOKKOS_ENABLE_SYCL
-template <>
-struct ChunkArraySpace<Kokkos::Experimental::SYCLDeviceUSMSpace> {
-  using memory_space = typename Kokkos::Experimental::SYCLSharedUSMSpace;
+/// Utility class to manage memory for chunked arrays on the host and
+/// device. Allocates/deallocates memory on both the host and device along with
+/// providing utilities for creating mirrors and deep copying between them.
+template <typename MemorySpace, typename ValueType>
+struct ChunkedArrayManager {
+  using value_type   = ValueType;
+  using pointer_type = ValueType*;
+  using track_type   = Kokkos::Impl::SharedAllocationTracker;
+
+  ChunkedArrayManager()                           = default;
+  ChunkedArrayManager(ChunkedArrayManager const&) = default;
+  ChunkedArrayManager(ChunkedArrayManager&&)      = default;
+  ChunkedArrayManager& operator=(ChunkedArrayManager&&) = default;
+  ChunkedArrayManager& operator=(const ChunkedArrayManager&) = default;
+
+  template <typename Space, typename Value>
+  friend struct ChunkedArrayManager;
+
+  template <typename Space, typename Value>
+  inline ChunkedArrayManager(const ChunkedArrayManager<Space, Value>& rhs)
+      : m_valid(rhs.m_valid),
+        m_chunk_max(rhs.m_chunk_max),
+        m_chunks((ValueType**)(rhs.m_chunks)),
+        m_track(rhs.m_track),
+        m_chunk_size(rhs.m_chunk_size) {
+    static_assert(
+        Kokkos::Impl::MemorySpaceAccess<MemorySpace, Space>::assignable,
+        "Incompatible ChunkedArrayManager copy construction");
+  }
+
+  ChunkedArrayManager(const unsigned arg_chunk_max,
+                      const unsigned arg_chunk_size)
+      : m_chunk_max(arg_chunk_max), m_chunk_size(arg_chunk_size) {}
+
+ private:
+  struct ACCESSIBLE_TAG {};
+  struct INACCESSIBLE_TAG {};
+
+  ChunkedArrayManager(ACCESSIBLE_TAG, pointer_type* arg_chunks,
+                      const unsigned arg_chunk_max)
+      : m_valid(true), m_chunk_max(arg_chunk_max), m_chunks(arg_chunks) {}
+
+  ChunkedArrayManager(INACCESSIBLE_TAG, const unsigned arg_chunk_max,
+                      const unsigned arg_chunk_size)
+      : m_chunk_max(arg_chunk_max), m_chunk_size(arg_chunk_size) {}
+
+ public:
+  template <typename Space, typename Enable_ = void>
+  struct IsAccessibleFrom;
+
+  template <typename Space>
+  struct IsAccessibleFrom<
+      Space, typename std::enable_if_t<Kokkos::Impl::MemorySpaceAccess<
+                 MemorySpace, Space>::accessible>> : std::true_type {};
+
+  template <typename Space>
+  struct IsAccessibleFrom<
+      Space, typename std::enable_if_t<!Kokkos::Impl::MemorySpaceAccess<
+                 MemorySpace, Space>::accessible>> : std::false_type {};
+
+  template <typename Space>
+  static ChunkedArrayManager<Space, ValueType> create_mirror(
+      ChunkedArrayManager<MemorySpace, ValueType> const& other,
+      typename std::enable_if<IsAccessibleFrom<Space>::value>::type* =
+          nullptr) {
+    return ChunkedArrayManager<Space, ValueType>{
+        ACCESSIBLE_TAG{}, other.m_chunks, other.m_chunk_max};
+  }
+
+  template <typename Space>
+  static ChunkedArrayManager<Space, ValueType> create_mirror(
+      ChunkedArrayManager<MemorySpace, ValueType> const& other,
+      typename std::enable_if<!IsAccessibleFrom<Space>::value>::type* =
+          nullptr) {
+    using tag_type =
+        typename ChunkedArrayManager<Space, ValueType>::INACCESSIBLE_TAG;
+    return ChunkedArrayManager<Space, ValueType>{tag_type{}, other.m_chunk_max,
+                                                 other.m_chunk_size};
+  }
+
+ public:
+  void allocate_device(const std::string& label) {
+    if (m_chunks == nullptr) {
+      m_chunks = reinterpret_cast<pointer_type*>(MemorySpace().allocate(
+          label.c_str(), (sizeof(pointer_type) * (m_chunk_max + 2))));
+    }
+  }
+
+  void initialize() {
+    for (unsigned i = 0; i < m_chunk_max + 2; i++) {
+      m_chunks[i] = nullptr;
+    }
+    m_valid = true;
+  }
+
+ private:
+  /// Custom destroy functor for deallocating array chunks along with a linked
+  /// allocation
+  template <typename Space>
+  struct Destroy {
+    Destroy()               = default;
+    Destroy(Destroy&&)      = default;
+    Destroy(const Destroy&) = default;
+    Destroy& operator=(Destroy&&) = default;
+    Destroy& operator=(const Destroy&) = default;
+
+    Destroy(std::string label, value_type** arg_chunk,
+            const unsigned arg_chunk_max, const unsigned arg_chunk_size,
+            value_type** arg_linked)
+        : m_label(label),
+          m_chunks(arg_chunk),
+          m_linked(arg_linked),
+          m_chunk_max(arg_chunk_max),
+          m_chunk_size(arg_chunk_size) {}
+
+    void execute() {
+      // Destroy the array of chunk pointers.
+      // Two entries beyond the max chunks are allocation counters.
+      for (unsigned i = 0; i < m_chunk_max; i++) {
+        Space().deallocate(m_label.c_str(), m_chunks[i],
+                           sizeof(value_type) * m_chunk_size);
+      }
+      // Destroy the linked allocation if we have one.
+      if (m_linked != nullptr) {
+        Space().deallocate(m_label.c_str(), m_linked,
+                           (sizeof(value_type*) * (m_chunk_max + 2)));
+      }
+    }
+
+    void destroy_shared_allocation() { execute(); }
+
+    std::string m_label;
+    value_type** m_chunks = nullptr;
+    value_type** m_linked = nullptr;
+    unsigned m_chunk_max;
+    unsigned m_chunk_size;
+  };
+
+ public:
+  template <typename Space>
+  void allocate_with_destroy(const std::string& label,
+                             pointer_type* linked_allocation = nullptr) {
+    using destroy_type = Destroy<Space>;
+    using record_type =
+        Kokkos::Impl::SharedAllocationRecord<MemorySpace, destroy_type>;
+
+    // Allocate + 2 extra slots so that *m_chunk[m_chunk_max] ==
+    // num_chunks_alloc and *m_chunk[m_chunk_max+1] == extent This must match in
+    // Destroy's execute(...) method
+    record_type* const record = record_type::allocate(
+        MemorySpace(), label, (sizeof(pointer_type) * (m_chunk_max + 2)));
+    m_chunks = static_cast<pointer_type*>(record->data());
+    m_track.assign_allocated_record_to_uninitialized(record);
+
+    record->m_destroy = destroy_type(label, m_chunks, m_chunk_max, m_chunk_size,
+                                     linked_allocation);
+  }
+
+  pointer_type* get_ptr() const { return m_chunks; }
+
+  template <typename Space>
+  typename std::enable_if<!IsAccessibleFrom<Space>::value>::type deep_copy_to(
+      ChunkedArrayManager<Space, ValueType> const& other) {
+    Kokkos::Impl::DeepCopy<Space, MemorySpace>(
+        other.m_chunks, m_chunks, sizeof(pointer_type) * (m_chunk_max + 2));
+  }
+
+  template <typename Space>
+  typename std::enable_if<IsAccessibleFrom<Space>::value>::type deep_copy_to(
+      ChunkedArrayManager<Space, ValueType> const&) {
+    // no-op
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  pointer_type* operator+(int i) const { return m_chunks + i; }
+
+  KOKKOS_INLINE_FUNCTION
+  pointer_type& operator[](int i) const { return m_chunks[i]; }
+
+  track_type const& track() const { return m_track; }
+
+  KOKKOS_INLINE_FUNCTION
+  bool valid() const { return m_valid; }
+
+ private:
+  bool m_valid           = false;
+  unsigned m_chunk_max   = 0;
+  pointer_type* m_chunks = nullptr;
+  track_type m_track;
+  unsigned m_chunk_size = 0;
 };
-#endif
-}  // end namespace Impl
+
+} /* end namespace Impl */
 
 /** \brief Dynamic views are restricted to rank-one and no layout.
  *         Resize only occurs on host outside of parallel_regions.
@@ -93,6 +258,13 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
  public:
   using traits = Kokkos::ViewTraits<DataType, P...>;
 
+  using value_type   = typename traits::value_type;
+  using device_space = typename traits::memory_space;
+  using host_space =
+      typename Kokkos::Impl::HostMirror<device_space>::Space::memory_space;
+  using device_accessor = Impl::ChunkedArrayManager<device_space, value_type>;
+  using host_accessor   = Impl::ChunkedArrayManager<host_space, value_type>;
+
  private:
   template <class, class...>
   friend class DynamicView;
@@ -108,7 +280,7 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
                 "DynamicView only implemented for non-specialized View type");
 
   template <class Space, bool = Kokkos::Impl::MemorySpaceAccess<
-                             Space, typename traits::memory_space>::accessible>
+                             Space, device_space>::accessible>
   struct verify_space {
     KOKKOS_FORCEINLINE_FUNCTION static void check() {}
   };
@@ -123,9 +295,8 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
   };
 
  private:
-  track_type m_track;
-  typename traits::value_type** m_chunks =
-      nullptr;             // array of pointers to 'chunks' of memory
+  device_accessor m_chunks;
+  host_accessor m_chunks_host;
   unsigned m_chunk_shift;  // ceil(log2(m_chunk_size))
   unsigned m_chunk_mask;   // m_chunk_size - 1
   unsigned m_chunk_max;  // number of entries in the chunk array - each pointing
@@ -173,7 +344,8 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
 
   KOKKOS_INLINE_FUNCTION
   size_t allocation_extent() const noexcept {
-    uintptr_t n = *reinterpret_cast<const uintptr_t*>(m_chunks + m_chunk_max);
+    uintptr_t n =
+        *reinterpret_cast<const uintptr_t*>(m_chunks_host + m_chunk_max);
     return (n << m_chunk_shift);
   }
 
@@ -183,7 +355,7 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
   KOKKOS_INLINE_FUNCTION
   size_t size() const noexcept {
     size_t extent_0 =
-        *reinterpret_cast<const size_t*>(m_chunks + m_chunk_max + 1);
+        *reinterpret_cast<const size_t*>(m_chunks_host + m_chunk_max + 1);
     return extent_0;
   }
 
@@ -215,10 +387,10 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
   // Allocation tracking properties
 
   KOKKOS_INLINE_FUNCTION
-  int use_count() const { return m_track.use_count(); }
+  int use_count() const { return m_chunks_host.track().use_count(); }
 
   inline const std::string label() const {
-    return m_track.template get_label<typename traits::memory_space>();
+    return m_chunks_host.track().template get_label<host_space>();
   }
 
   //----------------------------------------------------------------------
@@ -285,13 +457,7 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
    *          up to the maximum number of chunks
    * */
   template <typename IntType>
-  inline typename std::enable_if<
-      std::is_integral<IntType>::value &&
-      Kokkos::Impl::MemorySpaceAccess<
-          Kokkos::HostSpace,
-          typename Impl::ChunkArraySpace<
-              typename traits::memory_space>::memory_space>::accessible>::type
-  resize_serial(IntType const& n) {
+  inline void resize_serial(IntType const& n) {
     using local_value_type   = typename traits::value_type;
     using value_pointer_type = local_value_type*;
 
@@ -304,37 +470,40 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
     }
 
     // *m_chunks[m_chunk_max] stores the current number of chunks being used
-    uintptr_t* const pc = reinterpret_cast<uintptr_t*>(m_chunks + m_chunk_max);
-    std::string _label =
-        m_track.template get_label<typename traits::memory_space>();
+    uintptr_t* const pc =
+        reinterpret_cast<uintptr_t*>(m_chunks_host + m_chunk_max);
+    std::string _label = m_chunks_host.track().template get_label<host_space>();
+
     if (*pc < NC) {
       while (*pc < NC) {
-        m_chunks[*pc] = reinterpret_cast<value_pointer_type>(
-            typename traits::memory_space().allocate(
+        m_chunks_host[*pc] =
+            reinterpret_cast<value_pointer_type>(device_space().allocate(
                 _label.c_str(), sizeof(local_value_type) << m_chunk_shift));
         ++*pc;
       }
     } else {
       while (NC + 1 <= *pc) {
         --*pc;
-        typename traits::memory_space().deallocate(
-            _label.c_str(), m_chunks[*pc],
-            sizeof(local_value_type) << m_chunk_shift);
-        m_chunks[*pc] = nullptr;
+        device_space().deallocate(_label.c_str(), m_chunks_host[*pc],
+                                  sizeof(local_value_type) << m_chunk_shift);
+        m_chunks_host[*pc] = nullptr;
       }
     }
-    // *m_chunks[m_chunk_max+1] stores the 'extent' requested by resize
+    // *m_chunks_host[m_chunk_max+1] stores the 'extent' requested by resize
     *(pc + 1) = n;
+
+    m_chunks_host.deep_copy_to(m_chunks);
   }
 
   KOKKOS_INLINE_FUNCTION bool is_allocated() const {
-    if (m_chunks == nullptr) {
-      return false;
-    } else {
-      // *m_chunks[m_chunk_max] stores the current number of chunks being used
+    if (m_chunks_host.valid()) {
+      // *m_chunks_host[m_chunk_max] stores the current number of chunks being
+      // used
       uintptr_t* const pc =
-          reinterpret_cast<uintptr_t*>(m_chunks + m_chunk_max);
+          reinterpret_cast<uintptr_t*>(m_chunks_host + m_chunk_max);
       return (*(pc + 1) > 0);
+    } else {
+      return false;
     }
   }
 
@@ -349,8 +518,8 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
 
   template <class RT, class... RP>
   DynamicView(const DynamicView<RT, RP...>& rhs)
-      : m_track(rhs.m_track),
-        m_chunks((typename traits::value_type**)rhs.m_chunks),
+      : m_chunks(rhs.m_chunks),
+        m_chunks_host(rhs.m_chunks_host),
         m_chunk_shift(rhs.m_chunk_shift),
         m_chunk_mask(rhs.m_chunk_mask),
         m_chunk_max(rhs.m_chunk_max),
@@ -361,63 +530,6 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
                   "Incompatible DynamicView copy construction");
   }
 
-  //----------------------------------------------------------------------
-
-  struct Destroy {
-    using local_value_type = typename traits::value_type;
-    std::string m_label;
-    local_value_type** m_chunks;
-    unsigned m_chunk_max;
-    bool m_destroy;
-    unsigned m_chunk_size;
-
-    // Initialize or destroy array of chunk pointers.
-    // Two entries beyond the max chunks are allocation counters.
-    inline void operator()(unsigned i) const {
-      if (m_destroy && i < m_chunk_max && nullptr != m_chunks[i]) {
-        typename traits::memory_space().deallocate(
-            m_label.c_str(), m_chunks[i],
-            sizeof(local_value_type) * m_chunk_size);
-      }
-      m_chunks[i] = nullptr;
-    }
-
-    void execute(bool arg_destroy) {
-      using Range = Kokkos::RangePolicy<typename HostSpace::execution_space>;
-
-      m_destroy = arg_destroy;
-
-      Kokkos::Impl::ParallelFor<Destroy, Range> closure(
-          *this,
-          Range(0, m_chunk_max + 2));  // Add 2 to 'destroy' extra slots storing
-                                       // num_chunks and extent; previously + 1
-
-      closure.execute();
-
-      typename traits::execution_space().fence();
-      // Impl::ChunkArraySpace< typename traits::memory_space
-      // >::memory_space::execution_space().fence();
-    }
-
-    void construct_shared_allocation() { execute(false); }
-
-    void destroy_shared_allocation() { execute(true); }
-
-    Destroy()               = default;
-    Destroy(Destroy&&)      = default;
-    Destroy(const Destroy&) = default;
-    Destroy& operator=(Destroy&&) = default;
-    Destroy& operator=(const Destroy&) = default;
-
-    Destroy(std::string label, typename traits::value_type** arg_chunk,
-            const unsigned arg_chunk_max, const unsigned arg_chunk_size)
-        : m_label(label),
-          m_chunks(arg_chunk),
-          m_chunk_max(arg_chunk_max),
-          m_destroy(false),
-          m_chunk_size(arg_chunk_size) {}
-  };
-
   /**\brief  Allocation constructor
    *
    *  Memory is allocated in chunks
@@ -427,10 +539,7 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
   explicit inline DynamicView(const std::string& arg_label,
                               const unsigned min_chunk_size,
                               const unsigned max_extent)
-      : m_track(),
-        m_chunks(nullptr)
-        // The chunk size is guaranteed to be a power of two
-        ,
+      :  // The chunk size is guaranteed to be a power of two
         m_chunk_shift(Kokkos::Impl::integral_power_of_two_that_contains(
             min_chunk_size))  // div ceil(log2(min_chunk_size))
         ,
@@ -440,28 +549,22 @@ class DynamicView : public Kokkos::ViewTraits<DataType, P...> {
                     m_chunk_shift)  // max num pointers-to-chunks in array
         ,
         m_chunk_size(2 << (m_chunk_shift - 1)) {
-    using chunk_array_memory_space = typename Impl::ChunkArraySpace<
-        typename traits::memory_space>::memory_space;
-    // A functor to deallocate all of the chunks upon final destruction
-    using record_type =
-        Kokkos::Impl::SharedAllocationRecord<chunk_array_memory_space, Destroy>;
-
-    // Allocate chunk pointers and allocation counter
-    record_type* const record =
-        record_type::allocate(chunk_array_memory_space(), arg_label,
-                              (sizeof(pointer_type) * (m_chunk_max + 2)));
-    // Allocate + 2 extra slots so that *m_chunk[m_chunk_max] ==
-    // num_chunks_alloc and *m_chunk[m_chunk_max+1] == extent This must match in
-    // Destroy's execute(...) method
-
-    m_chunks = reinterpret_cast<pointer_type*>(record->data());
-
-    record->m_destroy = Destroy(arg_label, m_chunks, m_chunk_max, m_chunk_size);
+    m_chunks = device_accessor(m_chunk_max, m_chunk_size);
 
-    // Initialize to zero
-    record->m_destroy.construct_shared_allocation();
-
-    m_track.assign_allocated_record_to_uninitialized(record);
+    if (device_accessor::template IsAccessibleFrom<host_space>::value) {
+      m_chunks.template allocate_with_destroy<device_space>(arg_label);
+      m_chunks.initialize();
+      m_chunks_host =
+          device_accessor::template create_mirror<host_space>(m_chunks);
+    } else {
+      m_chunks.allocate_device(arg_label);
+      m_chunks_host =
+          device_accessor::template create_mirror<host_space>(m_chunks);
+      m_chunks_host.template allocate_with_destroy<device_space>(
+          arg_label, m_chunks.get_ptr());
+      m_chunks_host.initialize();
+      m_chunks_host.deep_copy_to(m_chunks);
+    }
   }
 };
 
@@ -487,8 +590,8 @@ inline void deep_copy(const View<T, DP...>& dst,
 
   enum {
     DstExecCanAccessSrc =
-        Kokkos::Impl::SpaceAccessibility<dst_execution_space,
-                                         src_memory_space>::accessible
+        Kokkos::SpaceAccessibility<dst_execution_space,
+                                   src_memory_space>::accessible
   };
 
   if (DstExecCanAccessSrc) {
@@ -512,8 +615,8 @@ inline void deep_copy(const Kokkos::Experimental::DynamicView<T, DP...>& dst,
 
   enum {
     DstExecCanAccessSrc =
-        Kokkos::Impl::SpaceAccessibility<dst_execution_space,
-                                         src_memory_space>::accessible
+        Kokkos::SpaceAccessibility<dst_execution_space,
+                                   src_memory_space>::accessible
   };
 
   if (DstExecCanAccessSrc) {
diff --git a/packages/kokkos/containers/src/Kokkos_ErrorReporter.hpp b/packages/kokkos/containers/src/Kokkos_ErrorReporter.hpp
index fbfaed9b1bcda2d22077947532f3abe303ea5533..18f026dc6ffcffc6c0b1884358ebf5a85012d40e 100644
--- a/packages/kokkos/containers/src/Kokkos_ErrorReporter.hpp
+++ b/packages/kokkos/containers/src/Kokkos_ErrorReporter.hpp
@@ -187,7 +187,8 @@ template <typename ReportType, typename DeviceType>
 void ErrorReporter<ReportType, DeviceType>::resize(const size_t new_size) {
   m_reports.resize(new_size);
   m_reporters.resize(new_size);
-  typename DeviceType::execution_space().fence();
+  typename DeviceType::execution_space().fence(
+      "Kokkos::Experimental::ErrorReporter::resize: fence after resizing");
 }
 
 }  // namespace Experimental
diff --git a/packages/kokkos/containers/src/Kokkos_OffsetView.hpp b/packages/kokkos/containers/src/Kokkos_OffsetView.hpp
index 0f21a08ba3ba86ed176dc4c4535ef76c960e90bc..57bf745d4038de73b71654e518aa855e0faa1698 100644
--- a/packages/kokkos/containers/src/Kokkos_OffsetView.hpp
+++ b/packages/kokkos/containers/src/Kokkos_OffsetView.hpp
@@ -116,8 +116,7 @@ KOKKOS_INLINE_FUNCTION void offsetview_verify_operator_bounds(
       This check should cover the case of Views that don't
       have the Unmanaged trait but were initialized by pointer. */
     if (tracker.has_record()) {
-      Kokkos::Impl::operator_bounds_error_on_device<MapType>(
-          map, Kokkos::Impl::has_printable_label_typedef<MapType>());
+      Kokkos::Impl::operator_bounds_error_on_device(map);
     } else {
       Kokkos::abort("OffsetView bounds error");
     }
@@ -1244,7 +1243,8 @@ class OffsetView : public ViewTraits<DataType, Properties...> {
     // to avoid incomplete type errors from usng Kokkos::Cuda directly.
     if (std::is_same<Kokkos::CudaUVMSpace,
                      typename traits::device_type::memory_space>::value) {
-      typename traits::device_type::memory_space::execution_space().fence();
+      typename traits::device_type::memory_space::execution_space().fence(
+          "Kokkos::OffsetView::OffsetView(): fence before UVM allocation");
     }
 #endif
     //------------------------------------------------------------
@@ -1256,7 +1256,8 @@ class OffsetView : public ViewTraits<DataType, Properties...> {
 #if defined(KOKKOS_ENABLE_CUDA)
     if (std::is_same<Kokkos::CudaUVMSpace,
                      typename traits::device_type::memory_space>::value) {
-      typename traits::device_type::memory_space::execution_space().fence();
+      typename traits::device_type::memory_space::execution_space().fence(
+          "Kokkos::OffsetView::OffsetView(): fence after UVM allocation");
     }
 #endif
     //------------------------------------------------------------
diff --git a/packages/kokkos/containers/src/Kokkos_ScatterView.hpp b/packages/kokkos/containers/src/Kokkos_ScatterView.hpp
index dcd4cf73e5d710bc427772a8a8de6384e80c9dae..79bc43b7393d85a1214e0ca3a8dc15861281e44e 100644
--- a/packages/kokkos/containers/src/Kokkos_ScatterView.hpp
+++ b/packages/kokkos/containers/src/Kokkos_ScatterView.hpp
@@ -834,7 +834,7 @@ class ScatterView<DataType, Layout, DeviceType, Op, ScatterNonDuplicated,
     static_assert(std::is_same<typename dest_type::array_layout, Layout>::value,
                   "ScatterView contribute destination has different layout");
     static_assert(
-        Kokkos::Impl::SpaceAccessibility<
+        Kokkos::SpaceAccessibility<
             execution_space, typename dest_type::memory_space>::accessible,
         "ScatterView contribute destination memory space not accessible");
     if (dest.data() == internal_view.data()) return;
@@ -1061,7 +1061,7 @@ class ScatterView<DataType, Kokkos::LayoutRight, DeviceType, Op,
                                Kokkos::LayoutRight>::value,
                   "ScatterView deep_copy destination has different layout");
     static_assert(
-        Kokkos::Impl::SpaceAccessibility<
+        Kokkos::SpaceAccessibility<
             execution_space, typename dest_type::memory_space>::accessible,
         "ScatterView deep_copy destination memory space not accessible");
     bool is_equal = (dest.data() == internal_view.data());
@@ -1290,7 +1290,7 @@ class ScatterView<DataType, Kokkos::LayoutLeft, DeviceType, Op,
                                Kokkos::LayoutLeft>::value,
                   "ScatterView deep_copy destination has different layout");
     static_assert(
-        Kokkos::Impl::SpaceAccessibility<
+        Kokkos::SpaceAccessibility<
             execution_space, typename dest_type::memory_space>::accessible,
         "ScatterView deep_copy destination memory space not accessible");
     auto extent   = internal_view.extent(internal_view_type::rank - 1);
diff --git a/packages/kokkos/containers/src/Kokkos_StaticCrsGraph.hpp b/packages/kokkos/containers/src/Kokkos_StaticCrsGraph.hpp
index 81be3ee2d3e836436a23f8808a07f9386bc3ac05..cd633e40310177b116f04220c7030545ba37039d 100644
--- a/packages/kokkos/containers/src/Kokkos_StaticCrsGraph.hpp
+++ b/packages/kokkos/containers/src/Kokkos_StaticCrsGraph.hpp
@@ -405,7 +405,9 @@ class StaticCrsGraph {
     Kokkos::parallel_for("Kokkos::StaticCrsGraph::create_block_partitioning",
                          Kokkos::RangePolicy<execution_space>(0, numRows()),
                          partitioner);
-    typename device_type::execution_space().fence();
+    typename device_type::execution_space().fence(
+        "Kokkos::StaticCrsGraph::create_block_partitioning:: fence after "
+        "partition");
 
     row_block_offsets = block_offsets;
   }
diff --git a/packages/kokkos/containers/src/Kokkos_UnorderedMap.hpp b/packages/kokkos/containers/src/Kokkos_UnorderedMap.hpp
index edb0e7261da93bb629cad4e9cc7c7d3118868288..a1601eee35869f5c26249dbf2ed325c4e84d5420 100644
--- a/packages/kokkos/containers/src/Kokkos_UnorderedMap.hpp
+++ b/packages/kokkos/containers/src/Kokkos_UnorderedMap.hpp
@@ -345,7 +345,8 @@ class UnorderedMap {
       const impl_value_type tmp = impl_value_type();
       Kokkos::deep_copy(m_values, tmp);
     }
-    { Kokkos::deep_copy(m_scalars, 0); }
+    Kokkos::deep_copy(m_scalars, 0);
+    m_size = 0;
   }
 
   KOKKOS_INLINE_FUNCTION constexpr bool is_allocated() const {
@@ -393,9 +394,9 @@ class UnorderedMap {
   ///
   /// This method has undefined behavior when erasable() is true.
   ///
-  /// Note that this is not a device function; it cannot be called in
+  /// Note that this is <i>not</i> a device function; it cannot be called in
   /// a parallel kernel.  The value is not stored as a variable; it
-  /// must be computed.
+  /// must be computed. m_size is a mutable cache of that value.
   size_type size() const {
     if (capacity() == 0u) return 0u;
     if (modified()) {
@@ -419,9 +420,13 @@ class UnorderedMap {
   bool begin_erase() {
     bool result = !erasable();
     if (is_insertable_map && result) {
-      execution_space().fence();
+      execution_space().fence(
+          "Kokkos::UnorderedMap::begin_erase: fence before setting erasable "
+          "flag");
       set_flag(erasable_idx);
-      execution_space().fence();
+      execution_space().fence(
+          "Kokkos::UnorderedMap::begin_erase: fence after setting erasable "
+          "flag");
     }
     return result;
   }
@@ -429,10 +434,12 @@ class UnorderedMap {
   bool end_erase() {
     bool result = erasable();
     if (is_insertable_map && result) {
-      execution_space().fence();
+      execution_space().fence(
+          "Kokkos::UnorderedMap::end_erase: fence before erasing");
       Impl::UnorderedMapErase<declared_map_type> f(*this);
       f.apply();
-      execution_space().fence();
+      execution_space().fence(
+          "Kokkos::UnorderedMap::end_erase: fence after erasing");
       reset_flag(erasable_idx);
     }
     return result;
diff --git a/packages/kokkos/containers/src/Kokkos_Vector.hpp b/packages/kokkos/containers/src/Kokkos_Vector.hpp
index a1fbba6b21c76b4bb7b2a63a4e3a863241a7cd74..88721bd89eb2fd86543c480727876a58fd888a56 100644
--- a/packages/kokkos/containers/src/Kokkos_Vector.hpp
+++ b/packages/kokkos/containers/src/Kokkos_Vector.hpp
@@ -119,12 +119,14 @@ class vector : public DualView<Scalar*, LayoutLeft, Arg1Type> {
     if (DV::template need_sync<typename DV::t_dev::device_type>()) {
       set_functor_host f(DV::h_view, val);
       parallel_for("Kokkos::vector::assign", n, f);
-      typename DV::t_host::execution_space().fence();
+      typename DV::t_host::execution_space().fence(
+          "Kokkos::vector::assign: fence after assigning values");
       DV::template modify<typename DV::t_host::device_type>();
     } else {
       set_functor f(DV::d_view, val);
       parallel_for("Kokkos::vector::assign", n, f);
-      typename DV::t_dev::execution_space().fence();
+      typename DV::t_dev::execution_space().fence(
+          "Kokkos::vector::assign: fence after assigning values");
       DV::template modify<typename DV::t_dev::device_type>();
     }
   }
diff --git a/packages/kokkos/containers/src/impl/Kokkos_Bitset_impl.hpp b/packages/kokkos/containers/src/impl/Kokkos_Bitset_impl.hpp
index 6047e60f3dd080b8cfe456627ccc80266e7df66b..9512f2d4a20e509af321d315c8963693076a0d58 100644
--- a/packages/kokkos/containers/src/impl/Kokkos_Bitset_impl.hpp
+++ b/packages/kokkos/containers/src/impl/Kokkos_Bitset_impl.hpp
@@ -57,22 +57,10 @@
 namespace Kokkos {
 namespace Impl {
 
-KOKKOS_FORCEINLINE_FUNCTION
-unsigned rotate_left(unsigned i, int r) {
-  constexpr int size = static_cast<int>(sizeof(unsigned) * CHAR_BIT);
-  return r ? ((i << r) | (i >> (size - r))) : i;
-}
-
 KOKKOS_FORCEINLINE_FUNCTION
 unsigned rotate_right(unsigned i, int r) {
   constexpr int size = static_cast<int>(sizeof(unsigned) * CHAR_BIT);
-  // FIXME_SYCL llvm.fshr.i32 missing
-  // (https://github.com/intel/llvm/issues/3308)
-#ifdef __SYCL_DEVICE_ONLY__
-  return rotate_left(i, size - r);
-#else
   return r ? ((i >> r) | (i << (size - r))) : i;
-#endif
 }
 
 template <typename Bitset>
diff --git a/packages/kokkos/containers/src/impl/Kokkos_Functional_impl.hpp b/packages/kokkos/containers/src/impl/Kokkos_Functional_impl.hpp
index 367ab338572064f167c3c50f447e4d27efff6999..fdd78e4e5f99dc4748c093d274c1e62f9316261a 100644
--- a/packages/kokkos/containers/src/impl/Kokkos_Functional_impl.hpp
+++ b/packages/kokkos/containers/src/impl/Kokkos_Functional_impl.hpp
@@ -75,7 +75,7 @@ uint32_t fmix32(uint32_t h) {
 
 KOKKOS_INLINE_FUNCTION
 uint32_t MurmurHash3_x86_32(const void* key, int len, uint32_t seed) {
-  const uint8_t* data = (const uint8_t*)key;
+  const uint8_t* data = static_cast<const uint8_t*>(key);
   const int nblocks   = len / 4;
 
   uint32_t h1 = seed;
diff --git a/packages/kokkos/containers/src/impl/Kokkos_StaticCrsGraph_factory.hpp b/packages/kokkos/containers/src/impl/Kokkos_StaticCrsGraph_factory.hpp
index f22e5d1eca928bc968d3cf32900f9fa0335751d7..00d3eafd231eabd8af444c8508acb42668ff883d 100644
--- a/packages/kokkos/containers/src/impl/Kokkos_StaticCrsGraph_factory.hpp
+++ b/packages/kokkos/containers/src/impl/Kokkos_StaticCrsGraph_factory.hpp
@@ -114,15 +114,11 @@ namespace Kokkos {
 template <class StaticCrsGraphType, class InputSizeType>
 inline typename StaticCrsGraphType::staticcrsgraph_type create_staticcrsgraph(
     const std::string& label, const std::vector<InputSizeType>& input) {
-  using output_type = StaticCrsGraphType;
-  // using input_type = std::vector<InputSizeType>; // unused
-
+  using output_type  = StaticCrsGraphType;
   using entries_type = typename output_type::entries_type;
-
-  using work_type = View<typename output_type::size_type[],
-                         typename output_type::array_layout,
-                         typename output_type::execution_space,
-                         typename output_type::memory_traits>;
+  using work_type    = View<
+      typename output_type::size_type[], typename output_type::array_layout,
+      typename output_type::device_type, typename output_type::memory_traits>;
 
   output_type output;
 
@@ -161,10 +157,9 @@ inline typename StaticCrsGraphType::staticcrsgraph_type create_staticcrsgraph(
 
   static_assert(entries_type::rank == 1, "Graph entries view must be rank one");
 
-  using work_type = View<typename output_type::size_type[],
-                         typename output_type::array_layout,
-                         typename output_type::execution_space,
-                         typename output_type::memory_traits>;
+  using work_type = View<
+      typename output_type::size_type[], typename output_type::array_layout,
+      typename output_type::device_type, typename output_type::memory_traits>;
 
   output_type output;
 
diff --git a/packages/kokkos/containers/unit_tests/TestDualView.hpp b/packages/kokkos/containers/unit_tests/TestDualView.hpp
index 3eee85ed10bd81bc8b511afa9f0fbde7ba244b8f..e22564aa5c24e569ae98d972fe5526a35cc741a6 100644
--- a/packages/kokkos/containers/unit_tests/TestDualView.hpp
+++ b/packages/kokkos/containers/unit_tests/TestDualView.hpp
@@ -49,7 +49,7 @@
 #include <iostream>
 #include <cstdlib>
 #include <cstdio>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <Kokkos_DualView.hpp>
 
 namespace Test {
diff --git a/packages/kokkos/containers/unit_tests/TestDynViewAPI.hpp b/packages/kokkos/containers/unit_tests/TestDynViewAPI.hpp
index dd0199ed81c75dcee42b964ac0bb1c246175ed01..a8d62bd24cad46531f2b4814f4d832c08758fe10 100644
--- a/packages/kokkos/containers/unit_tests/TestDynViewAPI.hpp
+++ b/packages/kokkos/containers/unit_tests/TestDynViewAPI.hpp
@@ -702,6 +702,11 @@ class TestDynViewAPI {
 
   using View0 = Kokkos::View<T, device>;
   using View1 = Kokkos::View<T*, device>;
+  using View2 = Kokkos::View<T**, device>;
+  using View3 = Kokkos::View<T***, device>;
+  using View4 = Kokkos::View<T****, device>;
+  using View5 = Kokkos::View<T*****, device>;
+  using View6 = Kokkos::View<T******, device>;
   using View7 = Kokkos::View<T*******, device>;
 
   using host_view_space = typename View0::host_mirror_space;
@@ -1065,7 +1070,7 @@ class TestDynViewAPI {
 
     dView0 d_uninitialized(
         Kokkos::view_alloc(Kokkos::WithoutInitializing, "uninit"), 10, 20);
-    ASSERT_TRUE(d_uninitialized.data() != nullptr);
+    ASSERT_NE(d_uninitialized.data(), nullptr);
     ASSERT_EQ(d_uninitialized.rank(), 2);
     ASSERT_EQ(d_uninitialized.extent(0), 10);
     ASSERT_EQ(d_uninitialized.extent(1), 20);
@@ -1075,14 +1080,14 @@ class TestDynViewAPI {
     hView0 hx, hy, hz;
 
     ASSERT_TRUE(Kokkos::is_dyn_rank_view<dView0>::value);
-    ASSERT_FALSE(Kokkos::is_dyn_rank_view<Kokkos::View<double> >::value);
-
-    ASSERT_TRUE(dx.data() == nullptr);  // Okay with UVM
-    ASSERT_TRUE(dy.data() == nullptr);  // Okay with UVM
-    ASSERT_TRUE(dz.data() == nullptr);  // Okay with UVM
-    ASSERT_TRUE(hx.data() == nullptr);
-    ASSERT_TRUE(hy.data() == nullptr);
-    ASSERT_TRUE(hz.data() == nullptr);
+    ASSERT_FALSE(Kokkos::is_dyn_rank_view<Kokkos::View<double>>::value);
+
+    ASSERT_EQ(dx.data(), nullptr);  // Okay with UVM
+    ASSERT_EQ(dy.data(), nullptr);  // Okay with UVM
+    ASSERT_EQ(dz.data(), nullptr);  // Okay with UVM
+    ASSERT_EQ(hx.data(), nullptr);
+    ASSERT_EQ(hy.data(), nullptr);
+    ASSERT_EQ(hz.data(), nullptr);
     ASSERT_EQ(dx.extent(0), 0u);  // Okay with UVM
     ASSERT_EQ(dy.extent(0), 0u);  // Okay with UVM
     ASSERT_EQ(dz.extent(0), 0u);  // Okay with UVM
@@ -1153,11 +1158,11 @@ class TestDynViewAPI {
 
     ASSERT_EQ(dx.use_count(), size_t(2));
 
-    ASSERT_FALSE(dx.data() == nullptr);
-    ASSERT_FALSE(const_dx.data() == nullptr);
-    ASSERT_FALSE(unmanaged_dx.data() == nullptr);
-    ASSERT_FALSE(unmanaged_from_ptr_dx.data() == nullptr);
-    ASSERT_FALSE(dy.data() == nullptr);
+    ASSERT_NE(dx.data(), nullptr);
+    ASSERT_NE(const_dx.data(), nullptr);
+    ASSERT_NE(unmanaged_dx.data(), nullptr);
+    ASSERT_NE(unmanaged_from_ptr_dx.data(), nullptr);
+    ASSERT_NE(dy.data(), nullptr);
     ASSERT_NE(dx, dy);
 
     ASSERT_EQ(dx.extent(0), unsigned(N0));
@@ -1317,17 +1322,17 @@ class TestDynViewAPI {
     ASSERT_NE(dx, dz);
 
     dx = dView0();
-    ASSERT_TRUE(dx.data() == nullptr);
-    ASSERT_FALSE(dy.data() == nullptr);
-    ASSERT_FALSE(dz.data() == nullptr);
+    ASSERT_EQ(dx.data(), nullptr);
+    ASSERT_NE(dy.data(), nullptr);
+    ASSERT_NE(dz.data(), nullptr);
     dy = dView0();
-    ASSERT_TRUE(dx.data() == nullptr);
-    ASSERT_TRUE(dy.data() == nullptr);
-    ASSERT_FALSE(dz.data() == nullptr);
+    ASSERT_EQ(dx.data(), nullptr);
+    ASSERT_EQ(dy.data(), nullptr);
+    ASSERT_NE(dz.data(), nullptr);
     dz = dView0();
-    ASSERT_TRUE(dx.data() == nullptr);
-    ASSERT_TRUE(dy.data() == nullptr);
-    ASSERT_TRUE(dz.data() == nullptr);
+    ASSERT_EQ(dx.data(), nullptr);
+    ASSERT_EQ(dy.data(), nullptr);
+    ASSERT_EQ(dz.data(), nullptr);
 
     // View - DynRankView Interoperability tests
     // deep_copy from view to dynrankview
@@ -1367,7 +1372,7 @@ class TestDynViewAPI {
   static void check_auto_conversion_to_const(
       const Kokkos::DynRankView<const DataType, device>& arg_const,
       const Kokkos::DynRankView<DataType, device>& arg) {
-    ASSERT_TRUE(arg_const == arg);
+    ASSERT_EQ(arg_const, arg);
   }
 
   static void run_test_allocated() {
@@ -1396,8 +1401,8 @@ class TestDynViewAPI {
     const_typeX xc = x;
     const_typeR xr = x;
 
-    ASSERT_TRUE(xc == x);
-    ASSERT_TRUE(x == xc);
+    ASSERT_EQ(xc, x);
+    ASSERT_EQ(x, xc);
 
     // For CUDA the constant random access View does not return
     // an lvalue reference due to retrieving through texture cache
@@ -1406,7 +1411,7 @@ class TestDynViewAPI {
     if (!std::is_same<typename device::execution_space, Kokkos::Cuda>::value)
 #endif
     {
-      ASSERT_TRUE(x.data() == xr.data());
+      ASSERT_EQ(x.data(), xr.data());
     }
 
     // typeX xf = xc ; // setting non-const from const must not compile
@@ -1659,29 +1664,29 @@ class TestDynViewAPI {
     const_svector_right_type cvr3 =
         Kokkos::subdynrankview(mv, Kokkos::ALL(), 2);
 
-    ASSERT_TRUE(&v1[0] == &v1(0));
-    ASSERT_TRUE(&v1[0] == &mv(0, 0));
-    ASSERT_TRUE(&v2[0] == &mv(0, 1));
-    ASSERT_TRUE(&v3[0] == &mv(0, 2));
-
-    ASSERT_TRUE(&cv1[0] == &mv(0, 0));
-    ASSERT_TRUE(&cv2[0] == &mv(0, 1));
-    ASSERT_TRUE(&cv3[0] == &mv(0, 2));
-
-    ASSERT_TRUE(&vr1[0] == &mv(0, 0));
-    ASSERT_TRUE(&vr2[0] == &mv(0, 1));
-    ASSERT_TRUE(&vr3[0] == &mv(0, 2));
-
-    ASSERT_TRUE(&cvr1[0] == &mv(0, 0));
-    ASSERT_TRUE(&cvr2[0] == &mv(0, 1));
-    ASSERT_TRUE(&cvr3[0] == &mv(0, 2));
-
-    ASSERT_TRUE(&mv1(0, 0) == &mv(1, 2));
-    ASSERT_TRUE(&mv1(1, 1) == &mv(2, 3));
-    ASSERT_TRUE(&mv1(3, 2) == &mv(4, 4));
-    ASSERT_TRUE(&mvr1(0, 0) == &mv_right(1, 2));
-    ASSERT_TRUE(&mvr1(1, 1) == &mv_right(2, 3));
-    ASSERT_TRUE(&mvr1(3, 2) == &mv_right(4, 4));
+    ASSERT_EQ(&v1[0], &v1(0));
+    ASSERT_EQ(&v1[0], &mv(0, 0));
+    ASSERT_EQ(&v2[0], &mv(0, 1));
+    ASSERT_EQ(&v3[0], &mv(0, 2));
+
+    ASSERT_EQ(&cv1[0], &mv(0, 0));
+    ASSERT_EQ(&cv2[0], &mv(0, 1));
+    ASSERT_EQ(&cv3[0], &mv(0, 2));
+
+    ASSERT_EQ(&vr1[0], &mv(0, 0));
+    ASSERT_EQ(&vr2[0], &mv(0, 1));
+    ASSERT_EQ(&vr3[0], &mv(0, 2));
+
+    ASSERT_EQ(&cvr1[0], &mv(0, 0));
+    ASSERT_EQ(&cvr2[0], &mv(0, 1));
+    ASSERT_EQ(&cvr3[0], &mv(0, 2));
+
+    ASSERT_EQ(&mv1(0, 0), &mv(1, 2));
+    ASSERT_EQ(&mv1(1, 1), &mv(2, 3));
+    ASSERT_EQ(&mv1(3, 2), &mv(4, 4));
+    ASSERT_EQ(&mvr1(0, 0), &mv_right(1, 2));
+    ASSERT_EQ(&mvr1(1, 1), &mv_right(2, 3));
+    ASSERT_EQ(&mvr1(3, 2), &mv_right(4, 4));
 
     const_svector_type c_cv1(v1);
     typename svector_type::const_type c_cv2(v2);
diff --git a/packages/kokkos/containers/unit_tests/TestDynamicView.hpp b/packages/kokkos/containers/unit_tests/TestDynamicView.hpp
index f018793dd6f3b162acbf9db20174c47ac75fc1c0..023bf92f62b48bc46878209e6c5ef6eccedeb726 100644
--- a/packages/kokkos/containers/unit_tests/TestDynamicView.hpp
+++ b/packages/kokkos/containers/unit_tests/TestDynamicView.hpp
@@ -52,7 +52,7 @@
 #include <Kokkos_Core.hpp>
 
 #include <Kokkos_DynamicView.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 namespace Test {
 
diff --git a/packages/kokkos/containers/unit_tests/TestOffsetView.hpp b/packages/kokkos/containers/unit_tests/TestOffsetView.hpp
index 9ddc226e291f6e7dc7d6bc960fad470fafeb9974..24a43e1ebc72820dbd84dd6e2931837cabfaecba 100644
--- a/packages/kokkos/containers/unit_tests/TestOffsetView.hpp
+++ b/packages/kokkos/containers/unit_tests/TestOffsetView.hpp
@@ -50,7 +50,7 @@
 #include <iostream>
 #include <cstdlib>
 #include <cstdio>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <Kokkos_OffsetView.hpp>
 #include <KokkosExp_MDRangePolicy.hpp>
 
diff --git a/packages/kokkos/containers/unit_tests/TestScatterView.hpp b/packages/kokkos/containers/unit_tests/TestScatterView.hpp
index fdbce2d492009cf38d5491398d77423108edc6a5..342ce2af48afe2cba3737db653f67957d04a51d4 100644
--- a/packages/kokkos/containers/unit_tests/TestScatterView.hpp
+++ b/packages/kokkos/containers/unit_tests/TestScatterView.hpp
@@ -118,11 +118,51 @@ struct test_scatter_view_impl_cls<DeviceType, Layout, Duplication, Contribution,
       scatter_access(k, 3)++;
       scatter_access(k, 4)--;
       scatter_access(k, 5) -= 5;
+// Workaround Intel 17 compiler bug which sometimes add random
+// instruction alignment which makes the lock instruction
+// illegal. Seems to be mostly just for unsigned int atomics.
+// Looking at the assembly the compiler
+// appears to insert cache line alignment for the instruction.
+// Isn't restricted to specific archs. Seen it on SNB and SKX, but for
+// different code. Another occurrence was with Desul atomics in
+// a different unit test. This one here happens without desul atomics.
+// Inserting an assembly nop instruction changes the alignment and
+// works round this.
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
       scatter_access_atomic(k, 6) += 2;
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
       scatter_access_atomic(k, 7)++;
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
       scatter_access_atomic(k, 8)--;
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
       --scatter_access_atomic(k, 9);
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
       ++scatter_access_atomic(k, 10);
+#ifdef KOKKOS_COMPILER_INTEL
+#if (KOKKOS_COMPILER_INTEL < 1800)
+      asm volatile("nop\n");
+#endif
+#endif
       scatter_access(k, 11) -= 3;
     }
   }
diff --git a/packages/kokkos/containers/unit_tests/TestStaticCrsGraph.hpp b/packages/kokkos/containers/unit_tests/TestStaticCrsGraph.hpp
index a9a178f95e7b7fedabcb7b00b292d88603ff3f77..c9a3eed90c372fcd4211d0a46868fe8bcc061614 100644
--- a/packages/kokkos/containers/unit_tests/TestStaticCrsGraph.hpp
+++ b/packages/kokkos/containers/unit_tests/TestStaticCrsGraph.hpp
@@ -180,8 +180,6 @@ void run_test_graph3(size_t B, size_t N) {
 
   std::vector<size_t> sizes(LENGTH);
 
-  size_t total_length = 0;
-
   for (size_t i = 0; i < LENGTH; ++i) {
     sizes[i] = rand() % 1000;
   }
@@ -189,10 +187,6 @@ void run_test_graph3(size_t B, size_t N) {
   sizes[1]    = N;
   sizes[1998] = N;
 
-  for (size_t i = 0; i < LENGTH; ++i) {
-    total_length += sizes[i];
-  }
-
   int C    = 0;
   dView dx = Kokkos::create_staticcrsgraph<dView>("test", sizes);
   dx.create_block_partitioning(B, C);
diff --git a/packages/kokkos/containers/unit_tests/TestUnorderedMap.hpp b/packages/kokkos/containers/unit_tests/TestUnorderedMap.hpp
index 4413cfbc80e31271d1e2b830976796ade24aaa9a..8009b996566322147bcd5cfe257dd858b72819bb 100644
--- a/packages/kokkos/containers/unit_tests/TestUnorderedMap.hpp
+++ b/packages/kokkos/containers/unit_tests/TestUnorderedMap.hpp
@@ -295,10 +295,8 @@ void test_deep_copy(uint32_t num_nodes) {
 }
 
 // FIXME_SYCL wrong results on Nvidia GPUs but correct on Host and Intel GPUs
-// FIXME_HIP
 // WORKAROUND MSVC
-#if !(defined(KOKKOS_ENABLE_HIP) && (HIP_VERSION < 401)) && \
-    !defined(_WIN32) && !defined(KOKKOS_ENABLE_SYCL)
+#if !defined(_WIN32) && !defined(KOKKOS_ENABLE_SYCL)
 TEST(TEST_CATEGORY, UnorderedMap_insert) {
   for (int i = 0; i < 500; ++i) {
     test_insert<TEST_EXECSPACE>(100000, 90000, 100, true);
@@ -329,6 +327,23 @@ TEST(TEST_CATEGORY, UnorderedMap_valid_empty) {
   ASSERT_TRUE(n.is_allocated());
 }
 
+TEST(TEST_CATEGORY, UnorderedMap_clear_zero_size) {
+  using Map =
+      Kokkos::UnorderedMap<int, void, Kokkos::DefaultHostExecutionSpace>;
+
+  Map m(11);
+  ASSERT_EQ(0u, m.size());
+
+  m.insert(2);
+  m.insert(3);
+  m.insert(5);
+  m.insert(7);
+  ASSERT_EQ(4u, m.size());
+
+  m.clear();
+  ASSERT_EQ(0u, m.size());
+}
+
 }  // namespace Test
 
 #endif  // KOKKOS_TEST_UNORDERED_MAP_HPP
diff --git a/packages/kokkos/core/cmake/KokkosCore_config.h.in b/packages/kokkos/core/cmake/KokkosCore_config.h.in
deleted file mode 100644
index f0835772b864faf0126796c75f5f1e9d02f95e28..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/cmake/KokkosCore_config.h.in
+++ /dev/null
@@ -1,104 +0,0 @@
-/* The trivial 'src/build_common.sh' creates a config
- * that must stay in sync with this file.
- */
-#cmakedefine KOKKOS_FOR_SIERRA
-
-#if !defined(KOKKOS_FOR_SIERRA)
-
-#if !defined(KOKKOS_MACROS_HPP) || defined(KOKKOS_CORE_CONFIG_H)
-#error \
-    "Don't include KokkosCore_config.h directly; include Kokkos_Macros.hpp instead."
-#else
-#define KOKKOS_CORE_CONFIG_H
-#endif
-
-#cmakedefine KOKKOS_ENABLE_CUDA
-#cmakedefine KOKKOS_ENABLE_HIP
-#cmakedefine KOKKOS_ENABLE_OPENMP
-#cmakedefine KOKKOS_ENABLE_THREADS
-#cmakedefine KOKKOS_ENABLE_SERIAL
-#cmakedefine KOKKOS_ENABLE_Winthread
-
-#cmakedefine KOKKOS_ENABLE_HWLOC
-#cmakedefine KOKKOS_ENABLE_HBWSPACE
-#cmakedefine KOKKOS_ENABLE_LIBRT
-
-#cmakedefine KOKKOS_ENABLE_DEBUG
-#cmakedefine KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK
-#cmakedefine KOKKOS_ENABLE_DEBUG_DUALVIEW_MODIFY_CHECK
-#cmakedefine KOKKOS_ENABLE_PROFILING_LOAD_PRINT
-#cmakedefine KOKKOS_ENABLE_TUNING
-
-#cmakedefine KOKKOS_ENABLE_AGGRESSIVE_VECTORIZATION
-
-#ifdef KOKKOS_ENABLE_CUDA
-
-#cmakedefine KOKKOS_ENABLE_CUDA_LDG_INTRINSIC
-
-// mfh 16 Sep 2014: If passed in on the command line, that overrides
-// any value of KOKKOS_USE_CUDA_UVM here.  Doing this should prevent build
-// warnings like this one:
-//
-// packages/kokkos/core/src/KokkosCore_config.h:13:1: warning:
-// "KOKKOS_USE_CUDA_UVM" redefined
-//
-// At some point, we should edit the test-build scripts in
-// Trilinos/cmake/ctest/drivers/perseus/, and take
-// -DKOKKOS_USE_CUDA_UVM from the command-line arguments there.  I
-// hesitate to do that now, because I'm not sure if all the files are
-// including KokkosCore_config.h (or a header file that includes it) like
-// they should.
-#ifndef KOKKOS_USE_CUDA_UVM
-#cmakedefine KOKKOS_USE_CUDA_UVM
-#endif
-
-#cmakedefine KOKKOS_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE
-
-#cmakedefine KOKKOS_ENABLE_CUDA_LAMBDA
-
-#endif
-
-#cmakedefine KOKKOS_IMPL_CUDA_CLANG_WORKAROUND
-
-#ifndef __CUDA_ARCH__
-#cmakedefine KOKKOS_ENABLE_ISA_X86_64
-#cmakedefine KOKKOS_ENABLE_ISA_KNC
-#cmakedefine KOKKOS_ENABLE_ISA_POWERPCLE
-#endif
-
-#ifdef KOKKOS_ENABLE_HIP
-#cmakedefine KOKKOS_ENABLE_HIP_RELOCATABLE_DEVICE_CODE
-#endif
-
-#cmakedefine KOKKOS_ARCH_ARMV80 1
-#cmakedefine KOKKOS_ARCH_ARMV81 1
-#cmakedefine KOKKOS_ARCH_ARMV8_THUNDERX 1
-#cmakedefine KOKKOS_ARCH_AVX 1
-#cmakedefine KOKKOS_ARCH_AVX2 1
-#cmakedefine KOKKOS_ARCH_AVX512MIC 1
-#cmakedefine KOKKOS_ARCH_AVX512XEON 1
-#cmakedefine KOKKOS_ARCH_KNC 1
-#cmakedefine KOKKOS_ARCH_POWER8 1
-#cmakedefine KOKKOS_ARCH_POWER9 1
-#cmakedefine KOKKOS_ARCH_KEPLER 1
-#cmakedefine KOKKOS_ARCH_KEPLER30 1
-#cmakedefine KOKKOS_ARCH_KEPLER32 1
-#cmakedefine KOKKOS_ARCH_KEPLER35 1
-#cmakedefine KOKKOS_ARCH_KEPLER37 1
-#cmakedefine KOKKOS_ARCH_MAXWELL 1
-#cmakedefine KOKKOS_ARCH_MAXWELL50 1
-#cmakedefine KOKKOS_ARCH_MAXWELL52 1
-#cmakedefine KOKKOS_ARCH_MAXWELL53 1
-#cmakedefine KOKKOS_ARCH_PASCAL 1
-#cmakedefine KOKKOS_ARCH_PASCAL60 1
-#cmakedefine KOKKOS_ARCH_PASCAL61 1
-#cmakedefine KOKKOS_ARCH_VOLTA70 1
-
-// TODO: These are currently not used in Kokkos.  Should they be removed?
-#cmakedefine KOKKOS_ENABLE_MPI
-#cmakedefine KOKKOS_ENABLE_CUSPARSE
-
-// TODO: No longer options in Kokkos.  Need to be removed.
-#cmakedefine KOKKOS_USING_DEPRECATED_VIEW
-
-#endif  // !defined(KOKKOS_FOR_SIERRA)
diff --git a/packages/kokkos/core/perf_test/CMakeLists.txt b/packages/kokkos/core/perf_test/CMakeLists.txt
index 9ff4b6006da8cb0358f2a9e53810b79ce59e8b02..a7c57a94346d74db97c8c320e0f3669bb2cc68cc 100644
--- a/packages/kokkos/core/perf_test/CMakeLists.txt
+++ b/packages/kokkos/core/perf_test/CMakeLists.txt
@@ -10,9 +10,7 @@
 #INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/../../algorithms/src")
 
 # FIXME_OPENMPTARGET - the NVIDIA HPC compiler nvc++ in the OpenMPTarget backend does not pass the perf_tests.
-IF (KOKKOS_ENABLE_OPENMPTARGET
-    AND (KOKKOS_CXX_COMPILER_ID STREQUAL PGI
-         OR KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC))
+IF (KOKKOS_ENABLE_OPENMPTARGET AND KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC)
   RETURN()
 ENDIF()
 
diff --git a/packages/kokkos/core/perf_test/PerfTestGramSchmidt.cpp b/packages/kokkos/core/perf_test/PerfTestGramSchmidt.cpp
index dee21fd7a575bd5aa0f6838980c670510f475cab..b534c32c52c691f4c65c5442d89a9381516391f1 100644
--- a/packages/kokkos/core/perf_test/PerfTestGramSchmidt.cpp
+++ b/packages/kokkos/core/perf_test/PerfTestGramSchmidt.cpp
@@ -231,7 +231,7 @@ void run_test_gramschmidt(int exp_beg, int exp_end, int num_trials,
 
     std::cout << label_gramschmidt << " , " << parallel_work_length << " , "
               << min_seconds << " , " << (min_seconds / parallel_work_length)
-              << std::endl;
+              << ", " << avg_seconds << std::endl;
   }
 }
 
diff --git a/packages/kokkos/core/perf_test/PerfTestHexGrad.cpp b/packages/kokkos/core/perf_test/PerfTestHexGrad.cpp
index c431c2b0c86d30192edc63d7dfbc447887f227cf..24c1898e0a16a4149c21df08e82c3da54ef0a25a 100644
--- a/packages/kokkos/core/perf_test/PerfTestHexGrad.cpp
+++ b/packages/kokkos/core/perf_test/PerfTestHexGrad.cpp
@@ -280,7 +280,7 @@ void run_test_hexgrad(int exp_beg, int exp_end, int num_trials,
 
     std::cout << label_hexgrad << " , " << parallel_work_length << " , "
               << min_seconds << " , " << (min_seconds / parallel_work_length)
-              << std::endl;
+              << avg_seconds << std::endl;
   }
 }
 
diff --git a/packages/kokkos/core/perf_test/PerfTest_ExecSpacePartitioning.cpp b/packages/kokkos/core/perf_test/PerfTest_ExecSpacePartitioning.cpp
index 50bbc78a6b75815ad59ea73c0077dc27ae2dccfa..5b7c2a7a03907f8f0c854482c06ef155441c097d 100644
--- a/packages/kokkos/core/perf_test/PerfTest_ExecSpacePartitioning.cpp
+++ b/packages/kokkos/core/perf_test/PerfTest_ExecSpacePartitioning.cpp
@@ -205,7 +205,7 @@ TEST(default_exec, overlap_range_policy) {
   double time_end = timer.seconds();
 
   if (SpaceInstance<TEST_EXECSPACE>::overlap()) {
-    ASSERT_TRUE((time_end > 1.5 * time_overlap));
+    ASSERT_GT(time_end, 1.5 * time_overlap);
   }
   printf("Time RangePolicy: NonOverlap: %lf Time Overlap: %lf\n", time_end,
          time_overlap);
@@ -238,7 +238,7 @@ TEST(default_exec, overlap_range_policy) {
   double time_not_fenced = timer.seconds();
   Kokkos::fence();
   if (SpaceInstance<TEST_EXECSPACE>::overlap()) {
-    ASSERT_TRUE(time_fenced > 2.0 * time_not_fenced);
+    ASSERT_GT(time_fenced, 2.0 * time_not_fenced);
   }
 
   timer.reset();
@@ -280,7 +280,7 @@ TEST(default_exec, overlap_range_policy) {
   ASSERT_EQ(h_result2(), h_result());
 
   if (SpaceInstance<TEST_EXECSPACE>::overlap()) {
-    ASSERT_TRUE(time_overlapped_reduce < 1.5 * time_no_overlapped_reduce);
+    ASSERT_LT(time_overlapped_reduce, 1.5 * time_no_overlapped_reduce);
   }
   printf("Time RangePolicy Reduce: NonOverlap: %lf Time Overlap: %lf\n",
          time_no_overlapped_reduce, time_overlapped_reduce);
@@ -378,7 +378,7 @@ TEST(default_exec, overlap_mdrange_policy) {
   double time_end = timer.seconds();
 
   if (SpaceInstance<TEST_EXECSPACE>::overlap()) {
-    ASSERT_TRUE((time_end > 1.5 * time_overlap));
+    ASSERT_GT(time_end, 1.5 * time_overlap);
   }
   printf("Time MDRangePolicy: NonOverlap: %lf Time Overlap: %lf\n", time_end,
          time_overlap);
@@ -413,7 +413,7 @@ TEST(default_exec, overlap_mdrange_policy) {
   double time_not_fenced = timer.seconds();
   Kokkos::fence();
   if (SpaceInstance<TEST_EXECSPACE>::overlap()) {
-    ASSERT_TRUE(time_fenced > 2.0 * time_not_fenced);
+    ASSERT_GT(time_fenced, 2.0 * time_not_fenced);
   }
 
   timer.reset();
@@ -459,7 +459,7 @@ TEST(default_exec, overlap_mdrange_policy) {
   ASSERT_EQ(h_result2(), h_result());
 
   if (SpaceInstance<TEST_EXECSPACE>::overlap()) {
-    ASSERT_TRUE(time_overlapped_reduce < 1.5 * time_no_overlapped_reduce);
+    ASSERT_LT(time_overlapped_reduce, 1.5 * time_no_overlapped_reduce);
   }
   printf("Time MDRangePolicy Reduce: NonOverlap: %lf Time Overlap: %lf\n",
          time_no_overlapped_reduce, time_overlapped_reduce);
@@ -548,7 +548,7 @@ TEST(default_exec, overlap_team_policy) {
   double time_end = timer.seconds();
 
   if (SpaceInstance<TEST_EXECSPACE>::overlap()) {
-    ASSERT_TRUE((time_end > 1.5 * time_overlap));
+    ASSERT_GT(time_end, 1.5 * time_overlap);
   }
   printf("Time TeamPolicy: NonOverlap: %lf Time Overlap: %lf\n", time_end,
          time_overlap);
@@ -581,7 +581,7 @@ TEST(default_exec, overlap_team_policy) {
   double time_not_fenced = timer.seconds();
   Kokkos::fence();
   if (SpaceInstance<TEST_EXECSPACE>::overlap()) {
-    ASSERT_TRUE(time_fenced > 2.0 * time_not_fenced);
+    ASSERT_GT(time_fenced, 2.0 * time_not_fenced);
   }
   timer.reset();
   Kokkos::parallel_reduce(
@@ -622,7 +622,7 @@ TEST(default_exec, overlap_team_policy) {
   ASSERT_EQ(h_result2(), h_result());
 
   if (SpaceInstance<TEST_EXECSPACE>::overlap()) {
-    ASSERT_TRUE(time_overlapped_reduce < 1.5 * time_no_overlapped_reduce);
+    ASSERT_LT(time_overlapped_reduce, 1.5 * time_no_overlapped_reduce);
   }
   printf("Time TeamPolicy Reduce: NonOverlap: %lf Time Overlap: %lf\n",
          time_no_overlapped_reduce, time_overlapped_reduce);
diff --git a/packages/kokkos/core/perf_test/PerfTest_ViewAllocate.cpp b/packages/kokkos/core/perf_test/PerfTest_ViewAllocate.cpp
index 550316bec997121a58a8b44f6df8efdced16a623..555a05ea279cd2280c510a03976dd75e8ee171f2 100644
--- a/packages/kokkos/core/perf_test/PerfTest_ViewAllocate.cpp
+++ b/packages/kokkos/core/perf_test/PerfTest_ViewAllocate.cpp
@@ -120,7 +120,8 @@ void run_allocateview_tests(int N, int R) {
   {
     Kokkos::Timer timer;
     for (int r = 0; r < R; r++) {
-      double* a_ptr = (double*)Kokkos::kokkos_malloc("A", sizeof(double) * N8);
+      double* a_ptr =
+          static_cast<double*>(Kokkos::kokkos_malloc("A", sizeof(double) * N8));
       Kokkos::parallel_for(
           N8, KOKKOS_LAMBDA(const int& i) { a_ptr[i] = 0.0; });
       Kokkos::fence();
diff --git a/packages/kokkos/core/perf_test/PerfTest_ViewResize_8.cpp b/packages/kokkos/core/perf_test/PerfTest_ViewResize_8.cpp
index afeeb643569ecd1a981132bed08944288ec3ca72..b0562f2fd12227f1e3270c332cd3f1a6c4298fad 100644
--- a/packages/kokkos/core/perf_test/PerfTest_ViewResize_8.cpp
+++ b/packages/kokkos/core/perf_test/PerfTest_ViewResize_8.cpp
@@ -47,10 +47,18 @@
 namespace Test {
 
 TEST(default_exec, ViewResize_Rank8) {
+// FIXME_SYCL Avoid running out of resources on the CUDA GPU used in the CI
+#ifdef KOKKOS_ENABLE_SYCL
+  printf("Resize View Performance for LayoutLeft:\n");
+  run_resizeview_tests8<Kokkos::LayoutLeft>(9, 1);
+  printf("Resize View Performance for LayoutRight:\n");
+  run_resizeview_tests8<Kokkos::LayoutRight>(9, 1);
+#else
   printf("Resize View Performance for LayoutLeft:\n");
   run_resizeview_tests8<Kokkos::LayoutLeft>(10, 1);
   printf("Resize View Performance for LayoutRight:\n");
   run_resizeview_tests8<Kokkos::LayoutRight>(10, 1);
+#endif
 }
 
 }  // namespace Test
diff --git a/packages/kokkos/core/perf_test/test_atomic.cpp b/packages/kokkos/core/perf_test/test_atomic.cpp
index 59820f3bdd2e83291dfd524325d3b7be6ba918ef..54824e5b39b91456a81e541e39a441d20b2879a7 100644
--- a/packages/kokkos/core/perf_test/test_atomic.cpp
+++ b/packages/kokkos/core/perf_test/test_atomic.cpp
@@ -47,7 +47,7 @@
 #include <cstdlib>
 
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 using exec_space = Kokkos::DefaultExecutionSpace;
 
@@ -401,7 +401,7 @@ template <class T>
 void Loop(int loop, int test, const char* type_name) {
   LoopVariant<T>(loop, test);
 
-  Kokkos::Impl::Timer timer;
+  Kokkos::Timer timer;
   T res       = LoopVariant<T>(loop, test);
   double time = timer.seconds();
 
diff --git a/packages/kokkos/core/perf_test/test_atomic_minmax_simple.cpp b/packages/kokkos/core/perf_test/test_atomic_minmax_simple.cpp
index eec1c8eacc7779121fd54e23d1ad7e4efa80902c..4086ef58163ff4ec144d2c151241952752d3453d 100644
--- a/packages/kokkos/core/perf_test/test_atomic_minmax_simple.cpp
+++ b/packages/kokkos/core/perf_test/test_atomic_minmax_simple.cpp
@@ -12,13 +12,13 @@
 #include <typeinfo>
 
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 using exec_space = Kokkos::DefaultExecutionSpace;
 
 template <typename T>
 void test(const int length) {
-  Kokkos::Impl::Timer timer;
+  Kokkos::Timer timer;
 
   using vector = Kokkos::View<T*, exec_space>;
 
diff --git a/packages/kokkos/core/perf_test/test_mempool.cpp b/packages/kokkos/core/perf_test/test_mempool.cpp
index 9aab119774c49d99ec112c527c79364a9c02ddc6..7887d4ba55196ddbc283baa023884e2f43a48991 100644
--- a/packages/kokkos/core/perf_test/test_mempool.cpp
+++ b/packages/kokkos/core/perf_test/test_mempool.cpp
@@ -48,7 +48,7 @@
 #include <limits>
 
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 using ExecSpace   = Kokkos::DefaultExecutionSpace;
 using MemorySpace = Kokkos::DefaultExecutionSpace::memory_space;
@@ -100,7 +100,7 @@ struct TestFunctor {
 
       const unsigned size_alloc = chunk * (1 + (j % chunk_span));
 
-      ptrs(j) = (uintptr_t)pool.allocate(size_alloc);
+      ptrs(j) = reinterpret_cast<uintptr_t>(pool.allocate(size_alloc));
 
       if (ptrs(j)) ++update;
     }
@@ -129,7 +129,7 @@ struct TestFunctor {
 
       const unsigned size_alloc = chunk * (1 + (j % chunk_span));
 
-      pool.deallocate((void*)ptrs(j), size_alloc);
+      pool.deallocate(reinterpret_cast<void*>(ptrs(j)), size_alloc);
     }
   }
 
@@ -153,9 +153,9 @@ struct TestFunctor {
         for (unsigned k = 0; k < repeat_inner; ++k) {
           const unsigned size_alloc = chunk * (1 + (j % chunk_span));
 
-          pool.deallocate((void*)ptrs(j), size_alloc);
+          pool.deallocate(reinterpret_cast<void*>(ptrs(j)), size_alloc);
 
-          ptrs(j) = (uintptr_t)pool.allocate(size_alloc);
+          ptrs(j) = reinterpret_cast<uintptr_t>(pool.allocate(size_alloc));
 
           if (0 == ptrs(j)) update++;
         }
@@ -266,7 +266,7 @@ int main(int argc, char* argv[]) {
     TestFunctor functor(total_alloc_size, min_superblock_size, number_alloc,
                         fill_stride, chunk_span, repeat_inner);
 
-    Kokkos::Impl::Timer timer;
+    Kokkos::Timer timer;
 
     if (!functor.test_fill()) {
       Kokkos::abort("fill ");
diff --git a/packages/kokkos/core/perf_test/test_taskdag.cpp b/packages/kokkos/core/perf_test/test_taskdag.cpp
index b2f936a955eca4a6d8a3c0eec928e01c5de66e51..49957ae9323db825ebcfa6d3c19dddb38856862d 100644
--- a/packages/kokkos/core/perf_test/test_taskdag.cpp
+++ b/packages/kokkos/core/perf_test/test_taskdag.cpp
@@ -56,7 +56,7 @@ int main() { return 0; }
 #include <cstdlib>
 #include <limits>
 
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 using ExecSpace = Kokkos::DefaultExecutionSpace;
 
@@ -220,7 +220,7 @@ int main(int argc, char* argv[]) {
     double time_sum = 0;
 
     for (int i = 0; i < test_repeat_outer; ++i) {
-      Kokkos::Impl::Timer timer;
+      Kokkos::Timer timer;
 
       Functor::FutureType ftmp =
           Kokkos::host_spawn(Kokkos::TaskSingle(sched), Functor(fib_input));
diff --git a/packages/kokkos/core/src/CMakeLists.txt b/packages/kokkos/core/src/CMakeLists.txt
index 2ab0989805723ce32115d379dd39708b5edd8209..499736c60d55b7746682f8828a9af45fc6c0aa8b 100644
--- a/packages/kokkos/core/src/CMakeLists.txt
+++ b/packages/kokkos/core/src/CMakeLists.txt
@@ -9,6 +9,8 @@ INSTALL (DIRECTORY
   "${CMAKE_CURRENT_SOURCE_DIR}/"
   DESTINATION ${KOKKOS_HEADER_DIR}
   FILES_MATCHING
+  PATTERN "*.inc"
+  PATTERN "*.inc_*"
   PATTERN "*.hpp"
   PATTERN "*.h"
 )
@@ -65,6 +67,15 @@ IF (KOKKOS_ENABLE_SYCL)
   APPEND_GLOB(KOKKOS_CORE_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/SYCL/*.hpp)
 ENDIF()
 
+IF (KOKKOS_ENABLE_IMPL_DESUL_ATOMICS)
+  APPEND_GLOB(KOKKOS_CORE_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/desul/src/*.cpp)
+  APPEND_GLOB(KOKKOS_CORE_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/desul/*.hpp)
+  APPEND_GLOB(KOKKOS_CORE_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/desul/*/*.hpp)
+  APPEND_GLOB(KOKKOS_CORE_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/desul/*/*/*.hpp)
+  APPEND_GLOB(KOKKOS_CORE_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/desul/*/*/*.inc)
+ENDIF()
+
+
 KOKKOS_ADD_LIBRARY(
   kokkoscore
   SOURCES ${KOKKOS_CORE_SRCS}
@@ -86,3 +97,15 @@ KOKKOS_LINK_TPL(kokkoscore PUBLIC LIBDL)
 KOKKOS_LINK_TPL(kokkoscore PUBLIC LIBRT)
 KOKKOS_LINK_TPL(kokkoscore PUBLIC PTHREAD)
 KOKKOS_LINK_TPL(kokkoscore PUBLIC ROCM)
+
+# FIXME: We need a proper solution to figure out whether to enable
+#        libatomic
+# XL requires libatomic even for 64 bit CAS, most others only for 128
+# I (CT) had removed 128bit CAS from desul to not need libatomic.
+IF (KOKKOS_ENABLE_IMPL_DESUL_ATOMICS AND
+    (KOKKOS_ENABLE_OPENMPTARGET OR (CMAKE_CXX_COMPILER_ID STREQUAL XLClang)))
+  target_link_libraries(kokkoscore PUBLIC atomic)
+ENDIF()
+
+
+KOKKOS_LINK_TPL(kokkoscore PUBLIC LIBQUADMATH)
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp b/packages/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp
index 916f109758de4ba3cf469659d7458ae77cf464da..f6b276240316ec1e6edc332d680eb72853c980b1 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_CudaSpace.cpp
@@ -90,43 +90,25 @@ static std::atomic<int> num_uvm_allocations(0);
 
 }  // namespace
 
-DeepCopy<CudaSpace, CudaSpace, Cuda>::DeepCopy(void *dst, const void *src,
-                                               size_t n) {
-  CUDA_SAFE_CALL(cudaMemcpy(dst, src, n, cudaMemcpyDefault));
+void DeepCopyCuda(void *dst, const void *src, size_t n) {
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaMemcpy(dst, src, n, cudaMemcpyDefault));
 }
 
-DeepCopy<HostSpace, CudaSpace, Cuda>::DeepCopy(void *dst, const void *src,
-                                               size_t n) {
-  CUDA_SAFE_CALL(cudaMemcpy(dst, src, n, cudaMemcpyDefault));
-}
-
-DeepCopy<CudaSpace, HostSpace, Cuda>::DeepCopy(void *dst, const void *src,
-                                               size_t n) {
-  CUDA_SAFE_CALL(cudaMemcpy(dst, src, n, cudaMemcpyDefault));
-}
-
-DeepCopy<CudaSpace, CudaSpace, Cuda>::DeepCopy(const Cuda &instance, void *dst,
-                                               const void *src, size_t n) {
-  CUDA_SAFE_CALL(
-      cudaMemcpyAsync(dst, src, n, cudaMemcpyDefault, instance.cuda_stream()));
-}
-
-DeepCopy<HostSpace, CudaSpace, Cuda>::DeepCopy(const Cuda &instance, void *dst,
-                                               const void *src, size_t n) {
-  CUDA_SAFE_CALL(
-      cudaMemcpyAsync(dst, src, n, cudaMemcpyDefault, instance.cuda_stream()));
-}
-
-DeepCopy<CudaSpace, HostSpace, Cuda>::DeepCopy(const Cuda &instance, void *dst,
-                                               const void *src, size_t n) {
-  CUDA_SAFE_CALL(
+void DeepCopyAsyncCuda(const Cuda &instance, void *dst, const void *src,
+                       size_t n) {
+  KOKKOS_IMPL_CUDA_SAFE_CALL(
       cudaMemcpyAsync(dst, src, n, cudaMemcpyDefault, instance.cuda_stream()));
 }
 
 void DeepCopyAsyncCuda(void *dst, const void *src, size_t n) {
   cudaStream_t s = cuda_get_deep_copy_stream();
-  CUDA_SAFE_CALL(cudaMemcpyAsync(dst, src, n, cudaMemcpyDefault, s));
-  cudaStreamSynchronize(s);
+  KOKKOS_IMPL_CUDA_SAFE_CALL(
+      cudaMemcpyAsync(dst, src, n, cudaMemcpyDefault, s));
+  Impl::cuda_stream_synchronize(
+      s,
+      Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+          DeepCopyResourceSynchronization,
+      "Kokkos::Impl::DeepCopyAsyncCuda: Deep Copy Stream Sync");
 }
 
 }  // namespace Impl
@@ -137,6 +119,7 @@ void DeepCopyAsyncCuda(void *dst, const void *src, size_t n) {
 
 namespace Kokkos {
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 KOKKOS_DEPRECATED void CudaSpace::access_error() {
   const std::string msg(
       "Kokkos::CudaSpace::access_error attempt to execute Cuda function from "
@@ -150,6 +133,7 @@ KOKKOS_DEPRECATED void CudaSpace::access_error(const void *const) {
       "non-Cuda space");
   Kokkos::Impl::throw_runtime_exception(msg);
 }
+#endif
 
 /*--------------------------------------------------------------------------*/
 
@@ -164,9 +148,11 @@ bool CudaUVMSpace::available() {
 
 /*--------------------------------------------------------------------------*/
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 int CudaUVMSpace::number_of_allocations() {
   return Kokkos::Impl::num_uvm_allocations.load();
 }
+#endif
 #ifdef KOKKOS_IMPL_DEBUG_CUDA_PIN_UVM_TO_HOST
 // The purpose of the following variable is to allow a state-based choice
 // for pinning UVM allocations to the CPU. For now this is considered
@@ -204,6 +190,8 @@ CudaUVMSpace::CudaUVMSpace() : m_device(Kokkos::Cuda().cuda_device()) {}
 
 CudaHostPinnedSpace::CudaHostPinnedSpace() {}
 
+int memory_threshold_g = 40000;  // 40 kB
+
 //==============================================================================
 // <editor-fold desc="allocate()"> {{{1
 
@@ -221,7 +209,19 @@ void *CudaSpace::impl_allocate(
     const Kokkos::Tools::SpaceHandle arg_handle) const {
   void *ptr = nullptr;
 
+#ifndef CUDART_VERSION
+#error CUDART_VERSION undefined!
+#elif (defined(KOKKOS_ENABLE_IMPL_CUDA_MALLOC_ASYNC) && CUDART_VERSION >= 11020)
+  cudaError_t error_code;
+  if (arg_alloc_size >= memory_threshold_g) {
+    error_code = cudaMallocAsync(&ptr, arg_alloc_size, 0);
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaDeviceSynchronize());
+  } else {
+    error_code = cudaMalloc(&ptr, arg_alloc_size);
+  }
+#else
   auto error_code = cudaMalloc(&ptr, arg_alloc_size);
+#endif
   if (error_code != cudaSuccess) {  // TODO tag as unlikely branch
     cudaGetLastError();  // This is the only way to clear the last error, which
                          // we should do here since we're turning it into an
@@ -253,7 +253,8 @@ void *CudaUVMSpace::impl_allocate(
     const Kokkos::Tools::SpaceHandle arg_handle) const {
   void *ptr = nullptr;
 
-  Cuda::impl_static_fence();
+  Cuda::impl_static_fence(
+      "Kokkos::CudaUVMSpace::impl_allocate: Pre UVM Allocation");
   if (arg_alloc_size > 0) {
     Kokkos::Impl::num_uvm_allocations++;
 
@@ -276,7 +277,8 @@ void *CudaUVMSpace::impl_allocate(
               CudaMallocManaged);
     }
   }
-  Cuda::impl_static_fence();
+  Cuda::impl_static_fence(
+      "Kokkos::CudaUVMSpace::impl_allocate: Post UVM Allocation");
   if (Kokkos::Profiling::profileLibraryLoaded()) {
     const size_t reported_size =
         (arg_logical_size > 0) ? arg_logical_size : arg_alloc_size;
@@ -337,9 +339,20 @@ void CudaSpace::impl_deallocate(
     Kokkos::Profiling::deallocateData(arg_handle, arg_label, arg_alloc_ptr,
                                       reported_size);
   }
-
   try {
-    CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr));
+#ifndef CUDART_VERSION
+#error CUDART_VERSION undefined!
+#elif (defined(KOKKOS_ENABLE_IMPL_CUDA_MALLOC_ASYNC) && CUDART_VERSION >= 11020)
+    if (arg_alloc_size >= memory_threshold_g) {
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaDeviceSynchronize());
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFreeAsync(arg_alloc_ptr, 0));
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaDeviceSynchronize());
+    } else {
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr));
+    }
+#else
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr));
+#endif
   } catch (...) {
   }
 }
@@ -362,7 +375,8 @@ void CudaUVMSpace::impl_deallocate(
     ,
     const size_t arg_logical_size,
     const Kokkos::Tools::SpaceHandle arg_handle) const {
-  Cuda::impl_static_fence();
+  Cuda::impl_static_fence(
+      "Kokkos::CudaUVMSpace::impl_deallocate: Pre UVM Deallocation");
   if (Kokkos::Profiling::profileLibraryLoaded()) {
     const size_t reported_size =
         (arg_logical_size > 0) ? arg_logical_size : arg_alloc_size;
@@ -372,11 +386,12 @@ void CudaUVMSpace::impl_deallocate(
   try {
     if (arg_alloc_ptr != nullptr) {
       Kokkos::Impl::num_uvm_allocations--;
-      CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr));
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(arg_alloc_ptr));
     }
   } catch (...) {
   }
-  Cuda::impl_static_fence();
+  Cuda::impl_static_fence(
+      "Kokkos::CudaUVMSpace::impl_deallocate: Post UVM Deallocation");
 }
 
 void CudaHostPinnedSpace::deallocate(void *const arg_alloc_ptr,
@@ -401,7 +416,7 @@ void CudaHostPinnedSpace::impl_deallocate(
                                       reported_size);
   }
   try {
-    CUDA_SAFE_CALL(cudaFreeHost(arg_alloc_ptr));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFreeHost(arg_alloc_ptr));
   } catch (...) {
   }
 }
@@ -462,7 +477,7 @@ SharedAllocationRecord<Kokkos::CudaSpace, void>::attach_texture_object(
   resDesc.res.linear.sizeInBytes = alloc_size;
   resDesc.res.linear.devPtr      = alloc_ptr;
 
-  CUDA_SAFE_CALL(
+  KOKKOS_IMPL_CUDA_SAFE_CALL(
       cudaCreateTextureObject(&tex_obj, &resDesc, &texDesc, nullptr));
 
   return tex_obj;
@@ -581,7 +596,7 @@ void cuda_prefetch_pointer(const Cuda &space, const void *ptr, size_t bytes,
                            bool to_device) {
   if ((ptr == nullptr) || (bytes == 0)) return;
   cudaPointerAttributes attr;
-  CUDA_SAFE_CALL(cudaPointerGetAttributes(&attr, ptr));
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaPointerGetAttributes(&attr, ptr));
   // I measured this and it turns out prefetching towards the host slows
   // DualView syncs down. Probably because the latency is not too bad in the
   // first place for the pull down. If we want to change that provde
@@ -593,8 +608,8 @@ void cuda_prefetch_pointer(const Cuda &space, const void *ptr, size_t bytes,
 #endif
   if (to_device && is_managed &&
       space.cuda_device_prop().concurrentManagedAccess) {
-    CUDA_SAFE_CALL(cudaMemPrefetchAsync(ptr, bytes, space.cuda_device(),
-                                        space.cuda_stream()));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaMemPrefetchAsync(
+        ptr, bytes, space.cuda_device(), space.cuda_stream()));
   }
 }
 
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_BlockSize_Deduction.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_BlockSize_Deduction.hpp
index 0f4259072d97f26c0032e674bdf60b9031fcee11..993c8d1bbadc4ebff2fcc9bdc905fca6bb37a9cf 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_BlockSize_Deduction.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_BlockSize_Deduction.hpp
@@ -134,7 +134,12 @@ inline int cuda_deduce_block_size(bool early_termination,
     }
 
     if (blocks_per_sm >= min_blocks_per_sm) {
-      if (threads_per_sm >= opt_threads_per_sm) {
+      // The logic prefers smaller block sizes over larger ones to
+      // give more flexibility to the scheduler.
+      // But don't go below 128 where performance suffers significantly
+      // for simple copy/set kernels.
+      if ((threads_per_sm > opt_threads_per_sm) ||
+          ((block_size >= 128) && (threads_per_sm == opt_threads_per_sm))) {
         opt_block_size     = block_size;
         opt_threads_per_sm = threads_per_sm;
       }
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Error.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Error.hpp
index 4759001d81f99afc0a1e2aa6cf64462d9e7fcdc9..36df0d2564ae8ab86849837cf60cb6d93727aab2 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Error.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Error.hpp
@@ -49,13 +49,19 @@
 #ifdef KOKKOS_ENABLE_CUDA
 
 #include <impl/Kokkos_Error.hpp>
-
+#include <impl/Kokkos_Profiling.hpp>
 #include <iosfwd>
 
 namespace Kokkos {
 namespace Impl {
 
-void cuda_device_synchronize();
+void cuda_stream_synchronize(
+    const cudaStream_t stream,
+    Kokkos::Tools::Experimental::SpecialSynchronizationCases reason,
+    const std::string& name);
+void cuda_device_synchronize(const std::string& name);
+void cuda_stream_synchronize(const cudaStream_t stream,
+                             const std::string& name);
 
 void cuda_internal_error_throw(cudaError e, const char* name,
                                const char* file = nullptr, const int line = 0);
@@ -68,9 +74,24 @@ inline void cuda_internal_safe_call(cudaError e, const char* name,
   }
 }
 
-#define CUDA_SAFE_CALL(call) \
+#define KOKKOS_IMPL_CUDA_SAFE_CALL(call) \
   Kokkos::Impl::cuda_internal_safe_call(call, #call, __FILE__, __LINE__)
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
+
+KOKKOS_DEPRECATED
+inline void cuda_internal_safe_call_deprecated(cudaError e, const char* name,
+                                               const char* file = nullptr,
+                                               const int line   = 0) {
+  cuda_internal_safe_call(e, name, file, line);
+}
+
+#define CUDA_SAFE_CALL(call)                                              \
+  Kokkos::Impl::cuda_internal_safe_call_deprecated(call, #call, __FILE__, \
+                                                   __LINE__)
+
+#endif
+
 }  // namespace Impl
 
 namespace Experimental {
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Graph_Impl.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Graph_Impl.hpp
index 3de7a69916130de41077bae684df0cbc87daea4b..bd514f5e88d915b46eccc6ccd5da7baa311088e3 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Graph_Impl.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Graph_Impl.hpp
@@ -60,6 +60,7 @@
 
 #include <Kokkos_Cuda.hpp>
 #include <cuda_runtime_api.h>
+#include <Cuda/Kokkos_Cuda_Error.hpp>
 
 namespace Kokkos {
 namespace Impl {
@@ -82,8 +83,8 @@ struct GraphImpl<Kokkos::Cuda> {
     constexpr size_t error_log_size = 256;
     cudaGraphNode_t error_node      = nullptr;
     char error_log[error_log_size];
-    CUDA_SAFE_CALL(cudaGraphInstantiate(&m_graph_exec, m_graph, &error_node,
-                                        error_log, error_log_size));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGraphInstantiate(
+        &m_graph_exec, m_graph, &error_node, error_log, error_log_size));
     // TODO @graphs print out errors
   }
 
@@ -107,26 +108,27 @@ struct GraphImpl<Kokkos::Cuda> {
     // TODO @graphs we need to somehow indicate the need for a fence in the
     //              destructor of the GraphImpl object (so that we don't have to
     //              just always do it)
-    m_execution_space.fence();
+    m_execution_space.fence("Kokkos::GraphImpl::~GraphImpl: Graph Destruction");
     KOKKOS_EXPECTS(bool(m_graph))
     if (bool(m_graph_exec)) {
-      CUDA_SAFE_CALL(cudaGraphExecDestroy(m_graph_exec));
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGraphExecDestroy(m_graph_exec));
     }
-    CUDA_SAFE_CALL(cudaGraphDestroy(m_graph));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGraphDestroy(m_graph));
   };
 
   explicit GraphImpl(Kokkos::Cuda arg_instance)
       : m_execution_space(std::move(arg_instance)) {
-    CUDA_SAFE_CALL(cudaGraphCreate(&m_graph, cuda_graph_flags_t{0}));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
+        cudaGraphCreate(&m_graph, cuda_graph_flags_t{0}));
   }
 
   void add_node(std::shared_ptr<aggregate_node_impl_t> const& arg_node_ptr) {
     // All of the predecessors are just added as normal, so all we need to
     // do here is add an empty node
-    CUDA_SAFE_CALL(cudaGraphAddEmptyNode(&(arg_node_ptr->node_details_t::node),
-                                         m_graph,
-                                         /* dependencies = */ nullptr,
-                                         /* numDependencies = */ 0));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
+        cudaGraphAddEmptyNode(&(arg_node_ptr->node_details_t::node), m_graph,
+                              /* dependencies = */ nullptr,
+                              /* numDependencies = */ 0));
   }
 
   template <class NodeImpl>
@@ -171,7 +173,7 @@ struct GraphImpl<Kokkos::Cuda> {
     auto /*const*/& cuda_node = arg_node_ptr->node_details_t::node;
     KOKKOS_EXPECTS(bool(cuda_node))
 
-    CUDA_SAFE_CALL(
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
         cudaGraphAddDependencies(m_graph, &pred_cuda_node, &cuda_node, 1));
   }
 
@@ -179,7 +181,7 @@ struct GraphImpl<Kokkos::Cuda> {
     if (!bool(m_graph_exec)) {
       _instantiate_graph();
     }
-    CUDA_SAFE_CALL(
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
         cudaGraphLaunch(m_graph_exec, m_execution_space.cuda_stream()));
   }
 
@@ -192,9 +194,10 @@ struct GraphImpl<Kokkos::Cuda> {
     KOKKOS_EXPECTS(!bool(m_graph_exec))
     auto rv = std::make_shared<root_node_impl_t>(
         get_execution_space(), _graph_node_is_root_ctor_tag{});
-    CUDA_SAFE_CALL(cudaGraphAddEmptyNode(&(rv->node_details_t::node), m_graph,
-                                         /* dependencies = */ nullptr,
-                                         /* numDependencies = */ 0));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
+        cudaGraphAddEmptyNode(&(rv->node_details_t::node), m_graph,
+                              /* dependencies = */ nullptr,
+                              /* numDependencies = */ 0));
     KOKKOS_ENSURES(bool(rv->node_details_t::node))
     return rv;
   }
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Half.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Half.hpp
index ec9c434fe663900a5d5029896a5c98ce13266605..c81286eb1004b10219b64f38563bc3e8af257ae9 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Half.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Half.hpp
@@ -51,6 +51,9 @@
     !(defined(KOKKOS_ARCH_KEPLER) || defined(KOKKOS_ARCH_MAXWELL50) ||  \
       defined(KOKKOS_ARCH_MAXWELL52))
 #include <cuda_fp16.h>
+#include <iosfwd>  // istream & ostream for extraction and insertion ops
+#include <string>
+#include <Kokkos_NumericTraits.hpp>  // reduction_identity
 
 #ifndef KOKKOS_IMPL_HALF_TYPE_DEFINED
 // Make sure no one else tries to define half_t
@@ -127,7 +130,7 @@ KOKKOS_INLINE_FUNCTION
     std::enable_if_t<std::is_same<T, unsigned long long>::value, T>
         cast_from_half(half_t);
 
-class half_t {
+class alignas(2) half_t {
  public:
   using impl_type = Kokkos::Impl::half_impl_t::type;
 
@@ -138,6 +141,22 @@ class half_t {
   KOKKOS_FUNCTION
   half_t() : val(0.0F) {}
 
+  // Copy constructors
+  KOKKOS_DEFAULTED_FUNCTION
+  half_t(const half_t&) noexcept = default;
+
+  KOKKOS_INLINE_FUNCTION
+  half_t(const volatile half_t& rhs) {
+#ifdef __CUDA_ARCH__
+    val = rhs.val;
+#else
+    const volatile uint16_t* rv_ptr =
+        reinterpret_cast<const volatile uint16_t*>(&rhs.val);
+    const uint16_t rv_val = *rv_ptr;
+    val                   = reinterpret_cast<const impl_type&>(rv_val);
+#endif  // __CUDA_ARCH__
+  }
+
   // Don't support implicit conversion back to impl_type.
   // impl_type is a storage only type on host.
   KOKKOS_FUNCTION
@@ -219,7 +238,7 @@ class half_t {
 #ifdef __CUDA_ARCH__
     tmp.val = +tmp.val;
 #else
-    tmp.val   = __float2half(+__half2float(tmp.val));
+    tmp.val               = __float2half(+__half2float(tmp.val));
 #endif
     return tmp;
   }
@@ -230,7 +249,7 @@ class half_t {
 #ifdef __CUDA_ARCH__
     tmp.val = -tmp.val;
 #else
-    tmp.val   = __float2half(-__half2float(tmp.val));
+    tmp.val               = __float2half(-__half2float(tmp.val));
 #endif
     return tmp;
   }
@@ -241,7 +260,7 @@ class half_t {
 #ifdef __CUDA_ARCH__
     ++val;
 #else
-    float tmp = __half2float(val);
+    float tmp             = __half2float(val);
     ++tmp;
     val       = __float2half(tmp);
 #endif
@@ -255,7 +274,7 @@ class half_t {
 #else
     float tmp = __half2float(val);
     --tmp;
-    val = __float2half(tmp);
+    val     = __float2half(tmp);
 #endif
     return *this;
   }
@@ -290,7 +309,10 @@ class half_t {
 
   template <class T>
   KOKKOS_FUNCTION void operator=(T rhs) volatile {
-    val = cast_to_half(rhs).val;
+    impl_type new_val = cast_to_half(rhs).val;
+    volatile uint16_t* val_ptr =
+        reinterpret_cast<volatile uint16_t*>(const_cast<impl_type*>(&val));
+    *val_ptr = reinterpret_cast<uint16_t&>(new_val);
   }
 
   // Compound operators
@@ -299,30 +321,21 @@ class half_t {
 #ifdef __CUDA_ARCH__
     val += rhs.val;
 #else
-    val = __float2half(__half2float(val) + __half2float(rhs.val));
+    val     = __float2half(__half2float(val) + __half2float(rhs.val));
 #endif
     return *this;
   }
 
   KOKKOS_FUNCTION
-  volatile half_t& operator+=(half_t rhs) volatile {
-#ifdef __CUDA_ARCH__
-    // Cuda 10 supports __half volatile stores but not volatile arithmetic
-    // operands. Cast away volatile-ness of val for arithmetic but not for store
-    // location.
-    val = const_cast<impl_type&>(val) + rhs.val;
-#else
-    // Use non-volatile val_ref to suppress:
-    // "warning: implicit dereference will not access object of type ‘volatile
-    // __half’ in statement"
-    auto val_ref = const_cast<impl_type&>(val);
-    val_ref      = __float2half(__half2float(const_cast<impl_type&>(val)) +
-                           __half2float(rhs.val));
-#endif
-    return *this;
+  void operator+=(const volatile half_t& rhs) volatile {
+    half_t tmp_rhs = rhs;
+    half_t tmp_lhs = *this;
+
+    tmp_lhs += tmp_rhs;
+    *this = tmp_lhs;
   }
 
-  // Compund operators: upcast overloads for +=
+  // Compound operators: upcast overloads for +=
   template <class T>
   KOKKOS_FUNCTION std::enable_if_t<
       std::is_same<T, float>::value || std::is_same<T, double>::value, T> friend
@@ -350,27 +363,18 @@ class half_t {
 #ifdef __CUDA_ARCH__
     val -= rhs.val;
 #else
-    val          = __float2half(__half2float(val) - __half2float(rhs.val));
+    val     = __float2half(__half2float(val) - __half2float(rhs.val));
 #endif
     return *this;
   }
 
   KOKKOS_FUNCTION
-  volatile half_t& operator-=(half_t rhs) volatile {
-#ifdef __CUDA_ARCH__
-    // Cuda 10 supports __half volatile stores but not volatile arithmetic
-    // operands. Cast away volatile-ness of val for arithmetic but not for store
-    // location.
-    val = const_cast<impl_type&>(val) - rhs.val;
-#else
-    // Use non-volatile val_ref to suppress:
-    // "warning: implicit dereference will not access object of type ‘volatile
-    // __half’ in statement"
-    auto val_ref = const_cast<impl_type&>(val);
-    val_ref      = __float2half(__half2float(const_cast<impl_type&>(val)) -
-                           __half2float(rhs.val));
-#endif
-    return *this;
+  void operator-=(const volatile half_t& rhs) volatile {
+    half_t tmp_rhs = rhs;
+    half_t tmp_lhs = *this;
+
+    tmp_lhs -= tmp_rhs;
+    *this = tmp_lhs;
   }
 
   // Compund operators: upcast overloads for -=
@@ -401,27 +405,18 @@ class half_t {
 #ifdef __CUDA_ARCH__
     val *= rhs.val;
 #else
-    val          = __float2half(__half2float(val) * __half2float(rhs.val));
+    val     = __float2half(__half2float(val) * __half2float(rhs.val));
 #endif
     return *this;
   }
 
   KOKKOS_FUNCTION
-  volatile half_t& operator*=(half_t rhs) volatile {
-#ifdef __CUDA_ARCH__
-    // Cuda 10 supports __half volatile stores but not volatile arithmetic
-    // operands. Cast away volatile-ness of val for arithmetic but not for store
-    // location.
-    val = const_cast<impl_type&>(val) * rhs.val;
-#else
-    // Use non-volatile val_ref to suppress:
-    // "warning: implicit dereference will not access object of type ‘volatile
-    // __half’ in statement"
-    auto val_ref = const_cast<impl_type&>(val);
-    val_ref      = __float2half(__half2float(const_cast<impl_type&>(val)) *
-                           __half2float(rhs.val));
-#endif
-    return *this;
+  void operator*=(const volatile half_t& rhs) volatile {
+    half_t tmp_rhs = rhs;
+    half_t tmp_lhs = *this;
+
+    tmp_lhs *= tmp_rhs;
+    *this = tmp_lhs;
   }
 
   // Compund operators: upcast overloads for *=
@@ -452,27 +447,18 @@ class half_t {
 #ifdef __CUDA_ARCH__
     val /= rhs.val;
 #else
-    val          = __float2half(__half2float(val) / __half2float(rhs.val));
+    val     = __float2half(__half2float(val) / __half2float(rhs.val));
 #endif
     return *this;
   }
 
   KOKKOS_FUNCTION
-  volatile half_t& operator/=(half_t rhs) volatile {
-#ifdef __CUDA_ARCH__
-    // Cuda 10 supports __half volatile stores but not volatile arithmetic
-    // operands. Cast away volatile-ness of val for arithmetic but not for store
-    // location.
-    val = const_cast<impl_type&>(val) / rhs.val;
-#else
-    // Use non-volatile val_ref to suppress:
-    // "warning: implicit dereference will not access object of type ‘volatile
-    // __half’ in statement"
-    auto val_ref = const_cast<impl_type&>(val);
-    val_ref      = __float2half(__half2float(const_cast<impl_type&>(val)) /
-                           __half2float(rhs.val));
-#endif
-    return *this;
+  void operator/=(const volatile half_t& rhs) volatile {
+    half_t tmp_rhs = rhs;
+    half_t tmp_lhs = *this;
+
+    tmp_lhs /= tmp_rhs;
+    *this = tmp_lhs;
   }
 
   // Compund operators: upcast overloads for /=
@@ -504,7 +490,7 @@ class half_t {
 #ifdef __CUDA_ARCH__
     lhs.val += rhs.val;
 #else
-    lhs.val      = __float2half(__half2float(lhs.val) + __half2float(rhs.val));
+    lhs.val = __float2half(__half2float(lhs.val) + __half2float(rhs.val));
 #endif
     return lhs;
   }
@@ -529,7 +515,7 @@ class half_t {
 #ifdef __CUDA_ARCH__
     lhs.val -= rhs.val;
 #else
-    lhs.val      = __float2half(__half2float(lhs.val) - __half2float(rhs.val));
+    lhs.val = __float2half(__half2float(lhs.val) - __half2float(rhs.val));
 #endif
     return lhs;
   }
@@ -554,7 +540,7 @@ class half_t {
 #ifdef __CUDA_ARCH__
     lhs.val *= rhs.val;
 #else
-    lhs.val      = __float2half(__half2float(lhs.val) * __half2float(rhs.val));
+    lhs.val = __float2half(__half2float(lhs.val) * __half2float(rhs.val));
 #endif
     return lhs;
   }
@@ -579,7 +565,7 @@ class half_t {
 #ifdef __CUDA_ARCH__
     lhs.val /= rhs.val;
 #else
-    lhs.val      = __float2half(__half2float(lhs.val) / __half2float(rhs.val));
+    lhs.val = __float2half(__half2float(lhs.val) / __half2float(rhs.val));
 #endif
     return lhs;
   }
@@ -683,6 +669,62 @@ class half_t {
     return __half2float(val) >= __half2float(rhs.val);
 #endif
   }
+
+  KOKKOS_FUNCTION
+  friend bool operator==(const volatile half_t& lhs,
+                         const volatile half_t& rhs) {
+    half_t tmp_lhs = lhs, tmp_rhs = rhs;
+    return tmp_lhs == tmp_rhs;
+  }
+
+  KOKKOS_FUNCTION
+  friend bool operator!=(const volatile half_t& lhs,
+                         const volatile half_t& rhs) {
+    half_t tmp_lhs = lhs, tmp_rhs = rhs;
+    return tmp_lhs != tmp_rhs;
+  }
+
+  KOKKOS_FUNCTION
+  friend bool operator<(const volatile half_t& lhs,
+                        const volatile half_t& rhs) {
+    half_t tmp_lhs = lhs, tmp_rhs = rhs;
+    return tmp_lhs < tmp_rhs;
+  }
+
+  KOKKOS_FUNCTION
+  friend bool operator>(const volatile half_t& lhs,
+                        const volatile half_t& rhs) {
+    half_t tmp_lhs = lhs, tmp_rhs = rhs;
+    return tmp_lhs > tmp_rhs;
+  }
+
+  KOKKOS_FUNCTION
+  friend bool operator<=(const volatile half_t& lhs,
+                         const volatile half_t& rhs) {
+    half_t tmp_lhs = lhs, tmp_rhs = rhs;
+    return tmp_lhs <= tmp_rhs;
+  }
+
+  KOKKOS_FUNCTION
+  friend bool operator>=(const volatile half_t& lhs,
+                         const volatile half_t& rhs) {
+    half_t tmp_lhs = lhs, tmp_rhs = rhs;
+    return tmp_lhs >= tmp_rhs;
+  }
+
+  // Insertion and extraction operators
+  friend std::ostream& operator<<(std::ostream& os, const half_t& x) {
+    const std::string out = std::to_string(static_cast<double>(x));
+    os << out;
+    return os;
+  }
+
+  friend std::istream& operator>>(std::istream& is, half_t& x) {
+    std::string in;
+    is >> in;
+    x = std::stod(in);
+    return is;
+  }
 };
 
 // CUDA before 11.1 only has the half <-> float conversions marked host device
@@ -943,6 +985,25 @@ KOKKOS_INLINE_FUNCTION
 }
 #endif
 }  // namespace Experimental
+
+// use float as the return type for sum and prod since cuda_fp16.h
+// has no constexpr functions for casting to __half
+template <>
+struct reduction_identity<Kokkos::Experimental::half_t> {
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static float sum() noexcept {
+    return 0.0F;
+  }
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static float prod() noexcept {
+    return 1.0F;
+  }
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static float max() noexcept {
+    return -65504.0F;
+  }
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static float min() noexcept {
+    return 65504.0F;
+  }
+};
+
 }  // namespace Kokkos
 #endif  // KOKKOS_IMPL_HALF_TYPE_DEFINED
 #endif  // KOKKOS_ENABLE_CUDA
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp
index 016cb6cdcbdd37740613724bb99efb9b4c32d7d4..6964d5b41b72368e9b4305d37e156e08321f7814 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.cpp
@@ -119,7 +119,7 @@ int cuda_kernel_arch() {
   int arch    = 0;
   int *d_arch = nullptr;
 
-  cudaMalloc((void **)&d_arch, sizeof(int));
+  cudaMalloc(reinterpret_cast<void **>(&d_arch), sizeof(int));
   cudaMemcpy(d_arch, &arch, sizeof(int), cudaMemcpyDefault);
 
   query_cuda_kernel_arch<<<1, 1>>>(d_arch);
@@ -141,7 +141,36 @@ bool cuda_launch_blocking() {
 
 }  // namespace
 
-void cuda_device_synchronize() { CUDA_SAFE_CALL(cudaDeviceSynchronize()); }
+void cuda_device_synchronize(const std::string &name) {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<Kokkos::Cuda>(
+      name,
+      Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+          GlobalDeviceSynchronization,
+      []() {  // TODO: correct device ID
+        KOKKOS_IMPL_CUDA_SAFE_CALL(cudaDeviceSynchronize());
+      });
+}
+
+void cuda_stream_synchronize(const cudaStream_t stream, const CudaInternal *ptr,
+                             const std::string &name) {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<Kokkos::Cuda>(
+      name,
+      Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{
+          ptr->impl_get_instance_id()},
+      [&]() {  // TODO: correct device ID
+        KOKKOS_IMPL_CUDA_SAFE_CALL(cudaStreamSynchronize(stream));
+      });
+}
+
+void cuda_stream_synchronize(
+    const cudaStream_t stream,
+    Kokkos::Tools::Experimental::SpecialSynchronizationCases reason,
+    const std::string &name) {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<Kokkos::Cuda>(
+      name, reason, [&]() {  // TODO: correct device ID
+        KOKKOS_IMPL_CUDA_SAFE_CALL(cudaStreamSynchronize(stream));
+      });
+}
 
 void cuda_internal_error_throw(cudaError e, const char *name, const char *file,
                                const int line) {
@@ -221,7 +250,7 @@ CudaInternalDevices::CudaInternalDevices() {
   // See 'cudaSetDeviceFlags' for host-device thread interaction
   // Section 4.4.2.6 of the CUDA Toolkit Reference Manual
 
-  CUDA_SAFE_CALL(cudaGetDeviceCount(&m_cudaDevCount));
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGetDeviceCount(&m_cudaDevCount));
 
   if (m_cudaDevCount > MAXIMUM_DEVICE_COUNT) {
     Kokkos::abort(
@@ -229,7 +258,7 @@ CudaInternalDevices::CudaInternalDevices() {
         "have. Please report this to github.com/kokkos/kokkos.");
   }
   for (int i = 0; i < m_cudaDevCount; ++i) {
-    CUDA_SAFE_CALL(cudaGetDeviceProperties(m_cudaProp + i, i));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGetDeviceProperties(m_cudaProp + i, i));
   }
 }
 
@@ -277,25 +306,27 @@ CudaInternal::~CudaInternal() {
               << std::endl;
   }
 
-  m_cudaDev                   = -1;
-  m_cudaArch                  = -1;
-  m_multiProcCount            = 0;
-  m_maxWarpCount              = 0;
-  m_maxBlock                  = 0;
-  m_maxSharedWords            = 0;
-  m_maxConcurrency            = 0;
-  m_scratchSpaceCount         = 0;
-  m_scratchFlagsCount         = 0;
-  m_scratchUnifiedCount       = 0;
-  m_scratchUnifiedSupported   = 0;
-  m_streamCount               = 0;
-  m_scratchSpace              = nullptr;
-  m_scratchFlags              = nullptr;
-  m_scratchUnified            = nullptr;
-  m_scratchConcurrentBitset   = nullptr;
-  m_stream                    = nullptr;
-  m_team_scratch_current_size = 0;
-  m_team_scratch_ptr          = nullptr;
+  m_cudaDev                 = -1;
+  m_cudaArch                = -1;
+  m_multiProcCount          = 0;
+  m_maxWarpCount            = 0;
+  m_maxBlock                = 0;
+  m_maxSharedWords          = 0;
+  m_maxConcurrency          = 0;
+  m_scratchSpaceCount       = 0;
+  m_scratchFlagsCount       = 0;
+  m_scratchUnifiedCount     = 0;
+  m_scratchUnifiedSupported = 0;
+  m_streamCount             = 0;
+  m_scratchSpace            = nullptr;
+  m_scratchFlags            = nullptr;
+  m_scratchUnified          = nullptr;
+  m_scratchConcurrentBitset = nullptr;
+  m_stream                  = nullptr;
+  for (int i = 0; i < m_n_team_scratch; ++i) {
+    m_team_scratch_current_size[i] = 0;
+    m_team_scratch_ptr[i]          = nullptr;
+  }
 }
 
 int CudaInternal::verify_is_initialized(const char *const label) const {
@@ -305,16 +336,20 @@ int CudaInternal::verify_is_initialized(const char *const label) const {
   }
   return 0 <= m_cudaDev;
 }
-
+uint32_t CudaInternal::impl_get_instance_id() const { return m_instance_id; }
 CudaInternal &CudaInternal::singleton() {
   static CudaInternal self;
   return self;
 }
+void CudaInternal::fence(const std::string &name) const {
+  Impl::cuda_stream_synchronize(m_stream, this, name);
+}
 void CudaInternal::fence() const {
-  CUDA_SAFE_CALL(cudaStreamSynchronize(m_stream));
+  fence("Kokkos::CudaInternal::fence(): Unnamed Instance Fence");
 }
 
-void CudaInternal::initialize(int cuda_device_id, cudaStream_t stream) {
+void CudaInternal::initialize(int cuda_device_id, cudaStream_t stream,
+                              bool manage_stream) {
   if (was_finalized)
     Kokkos::abort("Calling Cuda::initialize after Cuda::finalize is illegal\n");
   was_initialized = true;
@@ -350,8 +385,9 @@ void CudaInternal::initialize(int cuda_device_id, cudaStream_t stream) {
     m_cudaDev    = cuda_device_id;
     m_deviceProp = cudaProp;
 
-    CUDA_SAFE_CALL(cudaSetDevice(m_cudaDev));
-    Kokkos::Impl::cuda_device_synchronize();
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaSetDevice(m_cudaDev));
+    Kokkos::Impl::cuda_device_synchronize(
+        "Kokkos::CudaInternal::initialize: Fence on space initialization");
 
     // Query what compute capability architecture a kernel executes:
     m_cudaArch = cuda_kernel_arch();
@@ -464,8 +500,8 @@ void CudaInternal::initialize(int cuda_device_id, cudaStream_t stream) {
 
       m_scratchConcurrentBitset = reinterpret_cast<uint32_t *>(r->data());
 
-      CUDA_SAFE_CALL(cudaMemset(m_scratchConcurrentBitset, 0,
-                                sizeof(uint32_t) * buffer_bound));
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaMemset(m_scratchConcurrentBitset, 0,
+                                            sizeof(uint32_t) * buffer_bound));
     }
     //----------------------------------
 
@@ -535,15 +571,19 @@ Kokkos::Cuda::initialize WARNING: Cuda is allocating into UVMSpace by default
   // Allocate a staging buffer for constant mem in pinned host memory
   // and an event to avoid overwriting driver for previous kernel launches
   if (stream == nullptr) {
-    CUDA_SAFE_CALL(cudaMallocHost((void **)&constantMemHostStaging,
-                                  CudaTraits::ConstantMemoryUsage));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
+        cudaMallocHost(reinterpret_cast<void **>(&constantMemHostStaging),
+                       CudaTraits::ConstantMemoryUsage));
 
-    CUDA_SAFE_CALL(cudaEventCreate(&constantMemReusable));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaEventCreate(&constantMemReusable));
   }
 
-  m_stream                    = stream;
-  m_team_scratch_current_size = 0;
-  m_team_scratch_ptr          = nullptr;
+  m_stream        = stream;
+  m_manage_stream = manage_stream;
+  for (int i = 0; i < m_n_team_scratch; ++i) {
+    m_team_scratch_current_size[i] = 0;
+    m_team_scratch_ptr[i]          = nullptr;
+  }
 }
 
 //----------------------------------------------------------------------------
@@ -569,7 +609,7 @@ Cuda::size_type *CudaInternal::scratch_flags(const Cuda::size_type size) const {
 
     m_scratchFlags = reinterpret_cast<size_type *>(r->data());
 
-    CUDA_SAFE_CALL(
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
         cudaMemset(m_scratchFlags, 0, m_scratchFlagsCount * sizeScratchGrain));
   }
 
@@ -645,20 +685,37 @@ Cuda::size_type *CudaInternal::scratch_functor(
   return m_scratchFunctor;
 }
 
-void *CudaInternal::resize_team_scratch_space(std::int64_t bytes,
-                                              bool force_shrink) {
-  if (m_team_scratch_current_size == 0) {
-    m_team_scratch_current_size = bytes;
-    m_team_scratch_ptr          = Kokkos::kokkos_malloc<Kokkos::CudaSpace>(
-        "Kokkos::CudaSpace::TeamScratchMemory", m_team_scratch_current_size);
+std::pair<void *, int> CudaInternal::resize_team_scratch_space(
+    std::int64_t bytes, bool force_shrink) {
+  // Multiple ParallelFor/Reduce Teams can call this function at the same time
+  // and invalidate the m_team_scratch_ptr. We use a pool to avoid any race
+  // condition.
+
+  int current_team_scratch = 0;
+  int zero                 = 0;
+  int one                  = 1;
+  while (m_team_scratch_pool[current_team_scratch].compare_exchange_weak(
+      zero, one, std::memory_order_release, std::memory_order_relaxed)) {
+    current_team_scratch = (current_team_scratch + 1) % m_n_team_scratch;
   }
-  if ((bytes > m_team_scratch_current_size) ||
-      ((bytes < m_team_scratch_current_size) && (force_shrink))) {
-    m_team_scratch_current_size = bytes;
-    m_team_scratch_ptr          = Kokkos::kokkos_realloc<Kokkos::CudaSpace>(
-        m_team_scratch_ptr, m_team_scratch_current_size);
+  if (m_team_scratch_current_size[current_team_scratch] == 0) {
+    m_team_scratch_current_size[current_team_scratch] = bytes;
+    m_team_scratch_ptr[current_team_scratch] =
+        Kokkos::kokkos_malloc<Kokkos::CudaSpace>(
+            "Kokkos::CudaSpace::TeamScratchMemory",
+            m_team_scratch_current_size[current_team_scratch]);
   }
-  return m_team_scratch_ptr;
+  if ((bytes > m_team_scratch_current_size[current_team_scratch]) ||
+      ((bytes < m_team_scratch_current_size[current_team_scratch]) &&
+       (force_shrink))) {
+    m_team_scratch_current_size[current_team_scratch] = bytes;
+    m_team_scratch_ptr[current_team_scratch] =
+        Kokkos::kokkos_realloc<Kokkos::CudaSpace>(
+            m_team_scratch_ptr[current_team_scratch],
+            m_team_scratch_current_size[current_team_scratch]);
+  }
+  return std::make_pair(m_team_scratch_ptr[current_team_scratch],
+                        current_team_scratch);
 }
 
 //----------------------------------------------------------------------------
@@ -685,36 +742,43 @@ void CudaInternal::finalize() {
     if (m_scratchFunctorSize > 0)
       RecordCuda::decrement(RecordCuda::get_record(m_scratchFunctor));
 
-    if (m_team_scratch_current_size > 0)
-      Kokkos::kokkos_free<Kokkos::CudaSpace>(m_team_scratch_ptr);
-
-    m_cudaDev                   = -1;
-    m_multiProcCount            = 0;
-    m_maxWarpCount              = 0;
-    m_maxBlock                  = 0;
-    m_maxSharedWords            = 0;
-    m_scratchSpaceCount         = 0;
-    m_scratchFlagsCount         = 0;
-    m_scratchUnifiedCount       = 0;
-    m_streamCount               = 0;
-    m_scratchSpace              = nullptr;
-    m_scratchFlags              = nullptr;
-    m_scratchUnified            = nullptr;
-    m_scratchConcurrentBitset   = nullptr;
-    m_stream                    = nullptr;
-    m_team_scratch_current_size = 0;
-    m_team_scratch_ptr          = nullptr;
+    for (int i = 0; i < m_n_team_scratch; ++i) {
+      if (m_team_scratch_current_size[i] > 0)
+        Kokkos::kokkos_free<Kokkos::CudaSpace>(m_team_scratch_ptr[i]);
+    }
+
+    if (m_manage_stream && m_stream != nullptr)
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaStreamDestroy(m_stream));
+
+    m_cudaDev                 = -1;
+    m_multiProcCount          = 0;
+    m_maxWarpCount            = 0;
+    m_maxBlock                = 0;
+    m_maxSharedWords          = 0;
+    m_scratchSpaceCount       = 0;
+    m_scratchFlagsCount       = 0;
+    m_scratchUnifiedCount     = 0;
+    m_streamCount             = 0;
+    m_scratchSpace            = nullptr;
+    m_scratchFlags            = nullptr;
+    m_scratchUnified          = nullptr;
+    m_scratchConcurrentBitset = nullptr;
+    m_stream                  = nullptr;
+    for (int i = 0; i < m_n_team_scratch; ++i) {
+      m_team_scratch_current_size[i] = 0;
+      m_team_scratch_ptr[i]          = nullptr;
+    }
   }
 
   // only destroy these if we're finalizing the singleton
   if (this == &singleton()) {
-    cudaFreeHost(constantMemHostStaging);
-    cudaEventDestroy(constantMemReusable);
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFreeHost(constantMemHostStaging));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaEventDestroy(constantMemReusable));
     auto &deep_copy_space =
         Kokkos::Impl::cuda_get_deep_copy_space(/*initialize*/ false);
     if (deep_copy_space)
       deep_copy_space->impl_internal_space_instance()->finalize();
-    cudaStreamDestroy(cuda_get_deep_copy_stream());
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaStreamDestroy(cuda_get_deep_copy_stream()));
   }
 }
 
@@ -823,7 +887,7 @@ Cuda::Cuda()
       "Cuda instance constructor");
 }
 
-Cuda::Cuda(cudaStream_t stream)
+Cuda::Cuda(cudaStream_t stream, bool manage_stream)
     : m_space_instance(new Impl::CudaInternal, [](Impl::CudaInternal *ptr) {
         ptr->finalize();
         delete ptr;
@@ -831,18 +895,31 @@ Cuda::Cuda(cudaStream_t stream)
   Impl::CudaInternal::singleton().verify_is_initialized(
       "Cuda instance constructor");
   m_space_instance->initialize(Impl::CudaInternal::singleton().m_cudaDev,
-                               stream);
+                               stream, manage_stream);
 }
 
 void Cuda::print_configuration(std::ostream &s, const bool) {
   Impl::CudaInternal::singleton().print_configuration(s);
 }
 
-void Cuda::impl_static_fence() { Kokkos::Impl::cuda_device_synchronize(); }
+void Cuda::impl_static_fence(const std::string &name) {
+  Kokkos::Impl::cuda_device_synchronize(name);
+}
+void Cuda::impl_static_fence() {
+  impl_static_fence("Kokkos::Cuda::impl_static_fence(): Unnamed Static Fence");
+}
 
-void Cuda::fence() const { m_space_instance->fence(); }
+void Cuda::fence() const {
+  fence("Kokkos::Cuda::fence(): Unnamed Instance Fence");
+}
+void Cuda::fence(const std::string &name) const {
+  m_space_instance->fence(name);
+}
 
 const char *Cuda::name() { return "Cuda"; }
+uint32_t Cuda::impl_instance_id() const noexcept {
+  return m_space_instance->impl_get_instance_id();
+}
 
 cudaStream_t Cuda::cuda_stream() const { return m_space_instance->m_stream; }
 int Cuda::cuda_device() const { return m_space_instance->m_cudaDev; }
@@ -877,7 +954,15 @@ void CudaSpaceInitializer::finalize(bool all_spaces) {
   }
 }
 
-void CudaSpaceInitializer::fence() { Kokkos::Cuda::impl_static_fence(); }
+void CudaSpaceInitializer::fence() {
+  Kokkos::Cuda::impl_static_fence(
+      "Kokkos::CudaSpaceInitializer::fence: Initializer Fence");
+}
+void CudaSpaceInitializer::fence(const std::string &name) {
+  // Kokkos::Cuda::impl_static_fence("Kokkos::CudaSpaceInitializer::fence:
+  // "+name); //TODO: or this
+  Kokkos::Cuda::impl_static_fence(name);
+}
 
 void CudaSpaceInitializer::print_configuration(std::ostream &msg,
                                                const bool detail) {
@@ -916,12 +1001,6 @@ void CudaSpaceInitializer::print_configuration(std::ostream &msg,
   msg << "yes\n";
 #else
   msg << "no\n";
-#endif
-  msg << "  KOKKOS_ENABLE_CUSPARSE: ";
-#ifdef KOKKOS_ENABLE_CUSPARSE
-  msg << "yes\n";
-#else
-  msg << "no\n";
 #endif
   msg << "  KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA: ";
 #ifdef KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.hpp
index aaec2c29260a5ad2b82e2daa653a58372253cd4d..7eb169838c05dc144e9789d4466f83d3febfe926 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Instance.hpp
@@ -3,6 +3,9 @@
 
 #include <vector>
 #include <impl/Kokkos_Tools.hpp>
+#include <atomic>
+#include <Cuda/Kokkos_Cuda_Error.hpp>
+
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 // These functions fulfill the purpose of allowing to work around
@@ -114,10 +117,14 @@ class CudaInternal {
   mutable size_type* m_scratchFunctor;
   uint32_t* m_scratchConcurrentBitset;
   cudaStream_t m_stream;
+  uint32_t m_instance_id;
+  bool m_manage_stream;
 
   // Team Scratch Level 1 Space
-  mutable int64_t m_team_scratch_current_size;
-  mutable void* m_team_scratch_ptr;
+  int m_n_team_scratch = 10;
+  mutable int64_t m_team_scratch_current_size[10];
+  mutable void* m_team_scratch_ptr[10];
+  mutable std::atomic_int m_team_scratch_pool[10];
 
   bool was_initialized = false;
   bool was_finalized   = false;
@@ -135,7 +142,8 @@ class CudaInternal {
     return nullptr != m_scratchSpace && nullptr != m_scratchFlags;
   }
 
-  void initialize(int cuda_device_id, cudaStream_t stream = nullptr);
+  void initialize(int cuda_device_id, cudaStream_t stream = nullptr,
+                  bool manage_stream = false);
   void finalize();
 
   void print_configuration(std::ostream&) const;
@@ -145,6 +153,7 @@ class CudaInternal {
   static void cuda_set_serial_execution(bool);
 #endif
 
+  void fence(const std::string&) const;
   void fence() const;
 
   ~CudaInternal();
@@ -175,20 +184,68 @@ class CudaInternal {
         m_scratchFunctor(nullptr),
         m_scratchConcurrentBitset(nullptr),
         m_stream(nullptr),
-        m_team_scratch_current_size(0),
-        m_team_scratch_ptr(nullptr) {}
+        m_instance_id(
+            Kokkos::Tools::Experimental::Impl::idForInstance<Kokkos::Cuda>(
+                reinterpret_cast<uintptr_t>(this))) {
+    for (int i = 0; i < m_n_team_scratch; ++i) {
+      m_team_scratch_current_size[i] = 0;
+      m_team_scratch_ptr[i]          = nullptr;
+      m_team_scratch_pool[i]         = 0;
+    }
+  }
 
   // Resizing of reduction related scratch spaces
   size_type* scratch_space(const size_type size) const;
   size_type* scratch_flags(const size_type size) const;
   size_type* scratch_unified(const size_type size) const;
   size_type* scratch_functor(const size_type size) const;
-
+  uint32_t impl_get_instance_id() const;
   // Resizing of team level 1 scratch
-  void* resize_team_scratch_space(std::int64_t bytes,
-                                  bool force_shrink = false);
+  std::pair<void*, int> resize_team_scratch_space(std::int64_t bytes,
+                                                  bool force_shrink = false);
 };
 
 }  // Namespace Impl
+
+namespace Experimental {
+// Partitioning an Execution Space: expects space and integer arguments for
+// relative weight
+//   Customization point for backends
+//   Default behavior is to return the passed in instance
+
+namespace Impl {
+inline void create_Cuda_instances(std::vector<Cuda>& instances) {
+  for (int s = 0; s < int(instances.size()); s++) {
+    cudaStream_t stream;
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaStreamCreate(&stream));
+    instances[s] = Cuda(stream, true);
+  }
+}
+}  // namespace Impl
+
+template <class... Args>
+std::vector<Cuda> partition_space(const Cuda&, Args...) {
+#ifdef __cpp_fold_expressions
+  static_assert(
+      (... && std::is_arithmetic_v<Args>),
+      "Kokkos Error: partitioning arguments must be integers or floats");
+#endif
+  std::vector<Cuda> instances(sizeof...(Args));
+  Impl::create_Cuda_instances(instances);
+  return instances;
+}
+
+template <class T>
+std::vector<Cuda> partition_space(const Cuda&, std::vector<T>& weights) {
+  static_assert(
+      std::is_arithmetic<T>::value,
+      "Kokkos Error: partitioning arguments must be integers or floats");
+
+  std::vector<Cuda> instances(weights.size());
+  Impl::create_Cuda_instances(instances);
+  return instances;
+}
+}  // namespace Experimental
+
 }  // Namespace Kokkos
 #endif
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_KernelLaunch.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_KernelLaunch.hpp
index d892a893b330772ec5e4306ed20a44f8aa2369f1..4b01798f5e2cad495c897b8110d96eec87fe429f 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_KernelLaunch.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_KernelLaunch.hpp
@@ -167,7 +167,7 @@ inline void configure_shmem_preference(KernelFuncPtr const& func,
 #ifndef KOKKOS_ARCH_KEPLER
   // On Kepler the L1 has no benefit since it doesn't cache reads
   auto set_cache_config = [&] {
-    CUDA_SAFE_CALL(cudaFuncSetCacheConfig(
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFuncSetCacheConfig(
         func,
         (prefer_shmem ? cudaFuncCachePreferShared : cudaFuncCachePreferL1)));
     return prefer_shmem;
@@ -372,14 +372,15 @@ struct CudaParallelLaunchKernelInvoker<
       params.kernelParams   = (void**)args;
       params.extra          = nullptr;
 
-      CUDA_SAFE_CALL(cudaGraphAddKernelNode(
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGraphAddKernelNode(
           &graph_node, graph, /* dependencies = */ nullptr,
           /* numDependencies = */ 0, &params));
     } else {
       // We still need an empty node for the dependency structure
-      CUDA_SAFE_CALL(cudaGraphAddEmptyNode(&graph_node, graph,
-                                           /* dependencies = */ nullptr,
-                                           /* numDependencies = */ 0));
+      KOKKOS_IMPL_CUDA_SAFE_CALL(
+          cudaGraphAddEmptyNode(&graph_node, graph,
+                                /* dependencies = */ nullptr,
+                                /* numDependencies = */ 0));
     }
     KOKKOS_ENSURES(bool(graph_node))
   }
@@ -475,14 +476,15 @@ struct CudaParallelLaunchKernelInvoker<
       params.kernelParams   = (void**)args;
       params.extra          = nullptr;
 
-      CUDA_SAFE_CALL(cudaGraphAddKernelNode(
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGraphAddKernelNode(
           &graph_node, graph, /* dependencies = */ nullptr,
           /* numDependencies = */ 0, &params));
     } else {
       // We still need an empty node for the dependency structure
-      CUDA_SAFE_CALL(cudaGraphAddEmptyNode(&graph_node, graph,
-                                           /* dependencies = */ nullptr,
-                                           /* numDependencies = */ 0));
+      KOKKOS_IMPL_CUDA_SAFE_CALL(
+          cudaGraphAddEmptyNode(&graph_node, graph,
+                                /* dependencies = */ nullptr,
+                                /* numDependencies = */ 0));
     }
     KOKKOS_ENSURES(bool(graph_node))
   }
@@ -538,7 +540,8 @@ struct CudaParallelLaunchKernelInvoker<
                             dim3 const& block, int shmem,
                             CudaInternal const* cuda_instance) {
     // Wait until the previous kernel that uses the constant buffer is done
-    CUDA_SAFE_CALL(cudaEventSynchronize(cuda_instance->constantMemReusable));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
+        cudaEventSynchronize(cuda_instance->constantMemReusable));
 
     // Copy functor (synchronously) to staging buffer in pinned host memory
     unsigned long* staging = cuda_instance->constantMemHostStaging;
@@ -554,8 +557,9 @@ struct CudaParallelLaunchKernelInvoker<
          get_kernel_func())<<<grid, block, shmem, cuda_instance->m_stream>>>();
 
     // Record an event that says when the constant buffer can be reused
-    CUDA_SAFE_CALL(cudaEventRecord(cuda_instance->constantMemReusable,
-                                   cudaStream_t(cuda_instance->m_stream)));
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
+        cudaEventRecord(cuda_instance->constantMemReusable,
+                        cudaStream_t(cuda_instance->m_stream)));
   }
 
 #ifdef KOKKOS_CUDA_ENABLE_GRAPHS
@@ -637,8 +641,9 @@ struct CudaParallelLaunchImpl<
       base_t::invoke_kernel(driver, grid, block, shmem, cuda_instance);
 
 #if defined(KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK)
-      CUDA_SAFE_CALL(cudaGetLastError());
-      cuda_instance->fence();
+      KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGetLastError());
+      cuda_instance->fence(
+          "Kokkos::Impl::launch_kernel: Debug Only Check for Execution Error");
 #endif
     }
   }
@@ -650,7 +655,7 @@ struct CudaParallelLaunchImpl<
     // the code and the result is visible.
     auto wrap_get_attributes = []() -> cudaFuncAttributes {
       cudaFuncAttributes attr_tmp;
-      CUDA_SAFE_CALL(
+      KOKKOS_IMPL_CUDA_SAFE_CALL(
           cudaFuncGetAttributes(&attr_tmp, base_t::get_kernel_func()));
       return attr_tmp;
     };
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Locks.cpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Locks.cpp
index ff31649544033b773519152ca25a22494fdd2f5f..1f3024f3186a14d847a6999b995832e7782b62e9 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Locks.cpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Locks.cpp
@@ -81,22 +81,34 @@ namespace Impl {
 CudaLockArrays g_host_cuda_lock_arrays = {nullptr, nullptr, 0};
 
 void initialize_host_cuda_lock_arrays() {
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+  desul::Impl::init_lock_arrays();
+
+  DESUL_ENSURE_CUDA_LOCK_ARRAYS_ON_DEVICE();
+#endif
   if (g_host_cuda_lock_arrays.atomic != nullptr) return;
-  CUDA_SAFE_CALL(cudaMalloc(&g_host_cuda_lock_arrays.atomic,
-                            sizeof(int) * (CUDA_SPACE_ATOMIC_MASK + 1)));
-  CUDA_SAFE_CALL(cudaMalloc(&g_host_cuda_lock_arrays.scratch,
-                            sizeof(int) * (Cuda::concurrency())));
-  CUDA_SAFE_CALL(cudaDeviceSynchronize());
+  KOKKOS_IMPL_CUDA_SAFE_CALL(
+      cudaMalloc(&g_host_cuda_lock_arrays.atomic,
+                 sizeof(int) * (CUDA_SPACE_ATOMIC_MASK + 1)));
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaMalloc(&g_host_cuda_lock_arrays.scratch,
+                                        sizeof(int) * (Cuda::concurrency())));
+  Impl::cuda_device_synchronize(
+      "Kokkos::Impl::initialize_host_cuda_lock_arrays: Pre Init Lock Arrays");
   g_host_cuda_lock_arrays.n = Cuda::concurrency();
   KOKKOS_COPY_CUDA_LOCK_ARRAYS_TO_DEVICE();
   init_lock_array_kernel_atomic<<<(CUDA_SPACE_ATOMIC_MASK + 1 + 255) / 256,
                                   256>>>();
   init_lock_array_kernel_threadid<<<(Kokkos::Cuda::concurrency() + 255) / 256,
                                     256>>>(Kokkos::Cuda::concurrency());
-  CUDA_SAFE_CALL(cudaDeviceSynchronize());
+  Impl::cuda_device_synchronize(
+      "Kokkos::Impl::initialize_host_cuda_lock_arrays: Post Init Lock Arrays");
 }
 
 void finalize_host_cuda_lock_arrays() {
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+  desul::Impl::finalize_lock_arrays();
+#endif
+
   if (g_host_cuda_lock_arrays.atomic == nullptr) return;
   cudaFree(g_host_cuda_lock_arrays.atomic);
   g_host_cuda_lock_arrays.atomic = nullptr;
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Locks.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Locks.hpp
index 7640b8084d16a210408deb94a35f8962dfc92c99..04fb7cb345a27e9d9932d188216d5261d8606939 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Locks.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Locks.hpp
@@ -53,6 +53,10 @@
 
 #include <Cuda/Kokkos_Cuda_Error.hpp>
 
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+#include <desul/atomics/Lock_Array_Cuda.hpp>
+#endif
+
 namespace Kokkos {
 namespace Impl {
 
@@ -150,13 +154,14 @@ inline int eliminate_warning_for_lock_array() { return lock_array_copied; }
 }  // namespace
 }  // namespace Impl
 }  // namespace Kokkos
+
 /* Dan Ibanez: it is critical that this code be a macro, so that it will
    capture the right address for Kokkos::Impl::g_device_cuda_lock_arrays!
    putting this in an inline function will NOT do the right thing! */
 #define KOKKOS_COPY_CUDA_LOCK_ARRAYS_TO_DEVICE()                      \
   {                                                                   \
     if (::Kokkos::Impl::lock_array_copied == 0) {                     \
-      CUDA_SAFE_CALL(                                                 \
+      KOKKOS_IMPL_CUDA_SAFE_CALL(                                     \
           cudaMemcpyToSymbol(Kokkos::Impl::g_device_cuda_lock_arrays, \
                              &Kokkos::Impl::g_host_cuda_lock_arrays,  \
                              sizeof(Kokkos::Impl::CudaLockArrays)));  \
@@ -164,6 +169,8 @@ inline int eliminate_warning_for_lock_array() { return lock_array_copied; }
     lock_array_copied = 1;                                            \
   }
 
+#ifndef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+
 #ifdef KOKKOS_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE
 #define KOKKOS_ENSURE_CUDA_LOCK_ARRAYS_ON_DEVICE()
 #else
@@ -171,6 +178,19 @@ inline int eliminate_warning_for_lock_array() { return lock_array_copied; }
   KOKKOS_COPY_CUDA_LOCK_ARRAYS_TO_DEVICE()
 #endif
 
+#else
+
+#ifdef KOKKOS_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE
+#define KOKKOS_ENSURE_CUDA_LOCK_ARRAYS_ON_DEVICE()
+#else
+// Still Need COPY_CUDA_LOCK_ARRAYS for team scratch etc.
+#define KOKKOS_ENSURE_CUDA_LOCK_ARRAYS_ON_DEVICE() \
+  KOKKOS_COPY_CUDA_LOCK_ARRAYS_TO_DEVICE()         \
+  DESUL_ENSURE_CUDA_LOCK_ARRAYS_ON_DEVICE()
+#endif
+
+#endif /* defined( KOKKOS_ENABLE_IMPL_DESUL_ATOMICS ) */
+
 #endif /* defined( KOKKOS_ENABLE_CUDA ) */
 
 #endif /* #ifndef KOKKOS_CUDA_LOCKS_HPP */
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel.hpp
index 2834e6f3de012b718ae06ebb6f87d7d24e3e5756..f83b43e608855492f9d4df725533a08184f5edaf 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Parallel.hpp
@@ -62,7 +62,6 @@
 #include <Cuda/Kokkos_Cuda_Locks.hpp>
 #include <Cuda/Kokkos_Cuda_Team.hpp>
 #include <Kokkos_Vectorization.hpp>
-#include <Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp>
 
 #include <impl/Kokkos_Tools.hpp>
 #include <typeinfo>
@@ -240,9 +239,11 @@ class TeamPolicyInternal<Kokkos::Cuda, Properties...>
 
   //----------------------------------------
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
   KOKKOS_DEPRECATED inline int vector_length() const {
     return impl_vector_length();
   }
+#endif
   inline int impl_vector_length() const { return m_vector_length; }
   inline int team_size() const { return m_team_size; }
   inline int league_size() const { return m_league_size; }
@@ -687,6 +688,7 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
   int m_shmem_size;
   void* m_scratch_ptr[2];
   int m_scratch_size[2];
+  int m_scratch_pool_id = -1;
 
   template <class TagType>
   __device__ inline
@@ -797,15 +799,19 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
     // Functor's reduce memory, team scan memory, and team shared memory depend
     // upon team size.
     m_scratch_ptr[0] = nullptr;
-    m_scratch_ptr[1] =
-        m_team_size <= 0
-            ? nullptr
-            : m_policy.space()
-                  .impl_internal_space_instance()
-                  ->resize_team_scratch_space(
-                      static_cast<ptrdiff_t>(m_scratch_size[1]) *
-                      static_cast<ptrdiff_t>(Cuda::concurrency() /
-                                             (m_team_size * m_vector_size)));
+    if (m_team_size <= 0) {
+      m_scratch_ptr[1] = nullptr;
+    } else {
+      auto scratch_ptr_id =
+          m_policy.space()
+              .impl_internal_space_instance()
+              ->resize_team_scratch_space(
+                  static_cast<std::int64_t>(m_scratch_size[1]) *
+                  (static_cast<std::int64_t>(Cuda::concurrency() /
+                                             (m_team_size * m_vector_size))));
+      m_scratch_ptr[1]  = scratch_ptr_id.first;
+      m_scratch_pool_id = scratch_ptr_id.second;
+    }
 
     const int shmem_size_total = m_shmem_begin + m_shmem_size;
     if (m_policy.space().impl_internal_space_instance()->m_maxShmemPerBlock <
@@ -829,6 +835,14 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
           "Kokkos::Impl::ParallelFor< Cuda > requested too large team size."));
     }
   }
+
+  ~ParallelFor() {
+    if (m_scratch_pool_id >= 0) {
+      m_policy.space()
+          .impl_internal_space_instance()
+          ->m_team_scratch_pool[m_scratch_pool_id] = 0;
+    }
+  }
 };
 
 }  // namespace Impl
@@ -870,9 +884,24 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
   using value_type     = typename ValueTraits::value_type;
   using reference_type = typename ValueTraits::reference_type;
   using functor_type   = FunctorType;
-  using size_type      = Kokkos::Cuda::size_type;
-  using index_type     = typename Policy::index_type;
-  using reducer_type   = ReducerType;
+  // Conditionally set word_size_type to int16_t or int8_t if value_type is
+  // smaller than int32_t (Kokkos::Cuda::size_type)
+  // word_size_type is used to determine the word count, shared memory buffer
+  // size, and global memory buffer size before the reduction is performed.
+  // Within the reduction, the word count is recomputed based on word_size_type
+  // and when calculating indexes into the shared/global memory buffers for
+  // performing the reduction, word_size_type is used again.
+  // For scalars > 4 bytes in size, indexing into shared/global memory relies
+  // on the block and grid dimensions to ensure that we index at the correct
+  // offset rather than at every 4 byte word; such that, when the join is
+  // performed, we have the correct data that was copied over in chunks of 4
+  // bytes.
+  using word_size_type = typename std::conditional<
+      sizeof(value_type) < sizeof(Kokkos::Cuda::size_type),
+      typename std::conditional<sizeof(value_type) == 2, int16_t, int8_t>::type,
+      Kokkos::Cuda::size_type>::type;
+  using index_type   = typename Policy::index_type;
+  using reducer_type = ReducerType;
 
   // Algorithmic constraints: blockSize is a power of two AND blockDim.y ==
   // blockDim.z == 1
@@ -883,9 +912,11 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
   const pointer_type m_result_ptr;
   const bool m_result_ptr_device_accessible;
   const bool m_result_ptr_host_accessible;
-  size_type* m_scratch_space;
-  size_type* m_scratch_flags;
-  size_type* m_unified_space;
+  word_size_type* m_scratch_space;
+  // m_scratch_flags must be of type Cuda::size_type due to use of atomics
+  // for tracking metadata in Kokkos_Cuda_ReduceScan.hpp
+  Cuda::size_type* m_scratch_flags;
+  word_size_type* m_unified_space;
 
   // Shall we use the shfl based reduction or not (only use it for static sized
   // types of more than 128bit)
@@ -924,16 +955,16 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
       __device__ inline
       void run(const DummySHMEMReductionType& ) const
       {*/
-    const integral_nonzero_constant<size_type, ValueTraits::StaticValueSize /
-                                                   sizeof(size_type)>
+    const integral_nonzero_constant<
+        word_size_type, ValueTraits::StaticValueSize / sizeof(word_size_type)>
         word_count(ValueTraits::value_size(
                        ReducerConditional::select(m_functor, m_reducer)) /
-                   sizeof(size_type));
+                   sizeof(word_size_type));
 
     {
       reference_type value =
           ValueInit::init(ReducerConditional::select(m_functor, m_reducer),
-                          kokkos_impl_cuda_shared_memory<size_type>() +
+                          kokkos_impl_cuda_shared_memory<word_size_type>() +
                               threadIdx.y * word_count.value);
 
       // Number of blocks is bounded so that the reduction can be limited to two
@@ -958,11 +989,12 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
       // This is the final block with the final result at the final threads'
       // location
 
-      size_type* const shared = kokkos_impl_cuda_shared_memory<size_type>() +
-                                (blockDim.y - 1) * word_count.value;
-      size_type* const global =
+      word_size_type* const shared =
+          kokkos_impl_cuda_shared_memory<word_size_type>() +
+          (blockDim.y - 1) * word_count.value;
+      word_size_type* const global =
           m_result_ptr_device_accessible
-              ? reinterpret_cast<size_type*>(m_result_ptr)
+              ? reinterpret_cast<word_size_type*>(m_result_ptr)
               : (m_unified_space ? m_unified_space : m_scratch_space);
 
       if (threadIdx.y == 0) {
@@ -985,17 +1017,17 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
         if (cuda_single_inter_block_reduce_scan<false, ReducerTypeFwd,
                                                 WorkTagFwd>(
                 ReducerConditional::select(m_functor, m_reducer), blockIdx.x,
-                gridDim.x, kokkos_impl_cuda_shared_memory<size_type>(),
+                gridDim.x, kokkos_impl_cuda_shared_memory<word_size_type>(),
                 m_scratch_space, m_scratch_flags)) {
           // This is the final block with the final result at the final threads'
           // location
 
-          size_type* const shared =
-              kokkos_impl_cuda_shared_memory<size_type>() +
+          word_size_type* const shared =
+              kokkos_impl_cuda_shared_memory<word_size_type>() +
               (blockDim.y - 1) * word_count.value;
-          size_type* const global =
+          word_size_type* const global =
               m_result_ptr_device_accessible
-                  ? reinterpret_cast<size_type*>(m_result_ptr)
+                  ? reinterpret_cast<word_size_type*>(m_result_ptr)
                   : (m_unified_space ? m_unified_space : m_scratch_space);
 
           if (threadIdx.y == 0) {
@@ -1100,15 +1132,21 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
 
       KOKKOS_ASSERT(block_size > 0);
 
-      m_scratch_space = cuda_internal_scratch_space(
+      // TODO: down casting these uses more space than required?
+      m_scratch_space = (word_size_type*)cuda_internal_scratch_space(
           m_policy.space(), ValueTraits::value_size(ReducerConditional::select(
                                 m_functor, m_reducer)) *
                                 block_size /* block_size == max block_count */);
-      m_scratch_flags =
-          cuda_internal_scratch_flags(m_policy.space(), sizeof(size_type));
-      m_unified_space = cuda_internal_scratch_unified(
-          m_policy.space(), ValueTraits::value_size(ReducerConditional::select(
-                                m_functor, m_reducer)));
+
+      // Intentionally do not downcast to word_size_type since we use Cuda
+      // atomics in Kokkos_Cuda_ReduceScan.hpp
+      m_scratch_flags = cuda_internal_scratch_flags(m_policy.space(),
+                                                    sizeof(Cuda::size_type));
+      m_unified_space =
+          reinterpret_cast<word_size_type*>(cuda_internal_scratch_unified(
+              m_policy.space(),
+              ValueTraits::value_size(
+                  ReducerConditional::select(m_functor, m_reducer))));
 
       // REQUIRED ( 1 , N , 1 )
       dim3 block(1, block_size, 1);
@@ -1139,7 +1177,9 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
           false);  // copy to device and execute
 
       if (!m_result_ptr_device_accessible) {
-        m_policy.space().fence();
+        m_policy.space().fence(
+            "Kokkos::Impl::ParallelReduce<Cuda, RangePolicy>::execute: Result "
+            "Not Device Accessible");
 
         if (m_result_ptr) {
           if (m_unified_space) {
@@ -1459,7 +1499,9 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
           false);  // copy to device and execute
 
       if (!m_result_ptr_device_accessible) {
-        m_policy.space().fence();
+        m_policy.space().fence(
+            "Kokkos::Impl::ParallelReduce<Cuda, MDRangePolicy>::execute: "
+            "Result Not Device Accessible");
 
         if (m_result_ptr) {
           if (m_unified_space) {
@@ -1580,6 +1622,7 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
   size_type m_shmem_size;
   void* m_scratch_ptr[2];
   int m_scratch_size[2];
+  int m_scratch_pool_id = -1;
   const size_type m_league_size;
   int m_team_size;
   const size_type m_vector_size;
@@ -1821,7 +1864,9 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
           true);  // copy to device and execute
 
       if (!m_result_ptr_device_accessible) {
-        m_policy.space().fence();
+        m_policy.space().fence(
+            "Kokkos::Impl::ParallelReduce<Cuda, TeamPolicy>::execute: Result "
+            "Not Device Accessible");
 
         if (m_result_ptr) {
           if (m_unified_space) {
@@ -1895,16 +1940,19 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
         FunctorTeamShmemSize<FunctorType>::value(arg_functor, m_team_size);
     m_scratch_size[0] = m_shmem_size;
     m_scratch_size[1] = m_policy.scratch_size(1, m_team_size);
-    m_scratch_ptr[1] =
-        m_team_size <= 0
-            ? nullptr
-            : m_policy.space()
-                  .impl_internal_space_instance()
-                  ->resize_team_scratch_space(
-                      static_cast<std::int64_t>(m_scratch_size[1]) *
-                      (static_cast<std::int64_t>(
-                          Cuda::concurrency() /
-                          (m_team_size * m_vector_size))));
+    if (m_team_size <= 0) {
+      m_scratch_ptr[1] = nullptr;
+    } else {
+      auto scratch_ptr_id =
+          m_policy.space()
+              .impl_internal_space_instance()
+              ->resize_team_scratch_space(
+                  static_cast<std::int64_t>(m_scratch_size[1]) *
+                  (static_cast<std::int64_t>(Cuda::concurrency() /
+                                             (m_team_size * m_vector_size))));
+      m_scratch_ptr[1]  = scratch_ptr_id.first;
+      m_scratch_pool_id = scratch_ptr_id.second;
+    }
 
     // The global parallel_reduce does not support vector_length other than 1 at
     // the moment
@@ -1973,6 +2021,8 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
     cudaFuncAttributes attr =
         CudaParallelLaunch<ParallelReduce,
                            LaunchBounds>::get_cuda_func_attributes();
+
+    // Valid team size not provided, deduce team size
     m_team_size =
         m_team_size >= 0
             ? m_team_size
@@ -1994,15 +2044,19 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
         FunctorTeamShmemSize<FunctorType>::value(arg_functor, m_team_size);
     m_scratch_size[0] = m_shmem_size;
     m_scratch_size[1] = m_policy.scratch_size(1, m_team_size);
-    m_scratch_ptr[1] =
-        m_team_size <= 0
-            ? nullptr
-            : m_policy.space()
-                  .impl_internal_space_instance()
-                  ->resize_team_scratch_space(
-                      static_cast<ptrdiff_t>(m_scratch_size[1]) *
-                      static_cast<ptrdiff_t>(Cuda::concurrency() /
-                                             (m_team_size * m_vector_size)));
+    if (m_team_size <= 0) {
+      m_scratch_ptr[1] = nullptr;
+    } else {
+      auto scratch_ptr_id =
+          m_policy.space()
+              .impl_internal_space_instance()
+              ->resize_team_scratch_space(
+                  static_cast<std::int64_t>(m_scratch_size[1]) *
+                  (static_cast<std::int64_t>(Cuda::concurrency() /
+                                             (m_team_size * m_vector_size))));
+      m_scratch_ptr[1]  = scratch_ptr_id.first;
+      m_scratch_pool_id = scratch_ptr_id.second;
+    }
 
     // The global parallel_reduce does not support vector_length other than 1 at
     // the moment
@@ -2030,13 +2084,28 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
       Kokkos::Impl::throw_runtime_exception(
           std::string("Kokkos::Impl::ParallelReduce< Cuda > bad team size"));
     }
-    if (int(m_team_size) >
-        arg_policy.team_size_max(m_functor, m_reducer, ParallelReduceTag())) {
+
+    size_type team_size_max =
+        Kokkos::Impl::cuda_get_max_block_size<FunctorType, LaunchBounds>(
+            m_policy.space().impl_internal_space_instance(), attr, m_functor,
+            m_vector_size, m_policy.team_scratch_size(0),
+            m_policy.thread_scratch_size(0)) /
+        m_vector_size;
+
+    if ((int)m_team_size > (int)team_size_max) {
       Kokkos::Impl::throw_runtime_exception(
           std::string("Kokkos::Impl::ParallelReduce< Cuda > requested too "
                       "large team size."));
     }
   }
+
+  ~ParallelReduce() {
+    if (m_scratch_pool_id >= 0) {
+      m_policy.space()
+          .impl_internal_space_instance()
+          ->m_team_scratch_pool[m_scratch_pool_id] = 0;
+    }
+  }
 };
 
 }  // namespace Impl
@@ -2167,9 +2236,7 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>, Kokkos::Cuda> {
 
     for (typename Policy::member_type iwork_base = range.begin();
          iwork_base < range.end(); iwork_base += blockDim.y) {
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      unsigned MASK = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-#endif
+      unsigned MASK                            = __activemask();
       const typename Policy::member_type iwork = iwork_base + threadIdx.y;
 
       __syncthreads();  // Don't overwrite previous iteration values until they
@@ -2182,11 +2249,7 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>, Kokkos::Cuda> {
       for (unsigned i = threadIdx.y; i < word_count.value; ++i) {
         shared_data[i + word_count.value] = shared_data[i] = shared_accum[i];
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      KOKKOS_IMPL_CUDA_SYNCWARP_MASK(MASK);
-#else
-      KOKKOS_IMPL_CUDA_SYNCWARP;
-#endif
+      __syncwarp(MASK);
       if (CudaTraits::WarpSize < word_count.value) {
         __syncthreads();
       }  // Protect against large scan values.
@@ -2457,9 +2520,7 @@ class ParallelScanWithTotal<FunctorType, Kokkos::RangePolicy<Traits...>,
 
     for (typename Policy::member_type iwork_base = range.begin();
          iwork_base < range.end(); iwork_base += blockDim.y) {
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      unsigned MASK = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-#endif
+      unsigned MASK = __activemask();
 
       const typename Policy::member_type iwork = iwork_base + threadIdx.y;
 
@@ -2474,11 +2535,7 @@ class ParallelScanWithTotal<FunctorType, Kokkos::RangePolicy<Traits...>,
         shared_data[i + word_count.value] = shared_data[i] = shared_accum[i];
       }
 
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      KOKKOS_IMPL_CUDA_SYNCWARP_MASK(MASK);
-#else
-      KOKKOS_IMPL_CUDA_SYNCWARP;
-#endif
+      __syncwarp(MASK);
       if (CudaTraits::WarpSize < word_count.value) {
         __syncthreads();
       }  // Protect against large scan values.
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_ReduceScan.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_ReduceScan.hpp
index fc9fc3770bead16eff4a0b5b6fea8b0a2039200f..e5b05bcc64f183ef98248a239e6b305fae9410ea 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_ReduceScan.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_ReduceScan.hpp
@@ -191,48 +191,28 @@ __device__ bool cuda_inter_block_reduction(
         value_type tmp = Kokkos::shfl_down(value, 1, 32);
         if (id + 1 < int(gridDim.x)) join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      unsigned int mask = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-      int active        = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      int active = KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      unsigned int mask = __activemask();
+      int active        = __ballot_sync(mask, 1);
       if (int(blockDim.x * blockDim.y) > 2) {
         value_type tmp = Kokkos::shfl_down(value, 2, 32);
         if (id + 2 < int(gridDim.x)) join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      active += KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      active += KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      active += __ballot_sync(mask, 1);
       if (int(blockDim.x * blockDim.y) > 4) {
         value_type tmp = Kokkos::shfl_down(value, 4, 32);
         if (id + 4 < int(gridDim.x)) join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      active += KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      active += KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      active += __ballot_sync(mask, 1);
       if (int(blockDim.x * blockDim.y) > 8) {
         value_type tmp = Kokkos::shfl_down(value, 8, 32);
         if (id + 8 < int(gridDim.x)) join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      active += KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      active += KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      active += __ballot_sync(mask, 1);
       if (int(blockDim.x * blockDim.y) > 16) {
         value_type tmp = Kokkos::shfl_down(value, 16, 32);
         if (id + 16 < int(gridDim.x)) join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      active += KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      active += KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      active += __ballot_sync(mask, 1);
     }
   }
   // The last block has in its thread=0 the global reduction value through
@@ -388,48 +368,28 @@ __device__ inline
         value_type tmp = Kokkos::shfl_down(value, 1, 32);
         if (id + 1 < int(gridDim.x)) reducer.join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      unsigned int mask = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-      int active        = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      int active = KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      unsigned int mask = __activemask();
+      int active        = __ballot_sync(mask, 1);
       if (int(blockDim.x * blockDim.y) > 2) {
         value_type tmp = Kokkos::shfl_down(value, 2, 32);
         if (id + 2 < int(gridDim.x)) reducer.join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      active += KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      active += KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      active += __ballot_sync(mask, 1);
       if (int(blockDim.x * blockDim.y) > 4) {
         value_type tmp = Kokkos::shfl_down(value, 4, 32);
         if (id + 4 < int(gridDim.x)) reducer.join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      active += KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      active += KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      active += __ballot_sync(mask, 1);
       if (int(blockDim.x * blockDim.y) > 8) {
         value_type tmp = Kokkos::shfl_down(value, 8, 32);
         if (id + 8 < int(gridDim.x)) reducer.join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      active += KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      active += KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      active += __ballot_sync(mask, 1);
       if (int(blockDim.x * blockDim.y) > 16) {
         value_type tmp = Kokkos::shfl_down(value, 16, 32);
         if (id + 16 < int(gridDim.x)) reducer.join(value, tmp);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      active += KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-      active += KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+      active += __ballot_sync(mask, 1);
     }
   }
 
@@ -573,23 +533,17 @@ struct CudaReductionsFunctor<FunctorType, ArgTag, false, false> {
                                // part of the reduction
       const int width)         // How much of the warp participates
   {
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
     unsigned mask =
         width == 32
             ? 0xffffffff
             : ((1 << width) - 1)
                   << ((threadIdx.y * blockDim.x + threadIdx.x) / width) * width;
-#endif
     const int lane_id = (threadIdx.y * blockDim.x + threadIdx.x) % 32;
     for (int delta = skip_vector ? blockDim.x : 1; delta < width; delta *= 2) {
       if (lane_id + delta < 32) {
         ValueJoin::join(functor, value, value + delta);
       }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-      KOKKOS_IMPL_CUDA_SYNCWARP_MASK(mask);
-#else
-      KOKKOS_IMPL_CUDA_SYNCWARP;
-#endif
+      __syncwarp(mask);
     }
     *value = *(value - lane_id);
   }
@@ -612,17 +566,18 @@ struct CudaReductionsFunctor<FunctorType, ArgTag, false, false> {
       const unsigned int delta = (threadIdx.y * blockDim.x + threadIdx.x) * 32;
       if (delta < blockDim.x * blockDim.y)
         *my_shared_team_buffer_element = shared_team_buffer_element[delta];
-      KOKKOS_IMPL_CUDA_SYNCWARP;
+      __syncwarp(0xffffffff);
       scalar_intra_warp_reduction(functor, my_shared_team_buffer_element, false,
                                   blockDim.x * blockDim.y / 32);
       if (threadIdx.x + threadIdx.y == 0) *result = *shared_team_buffer_element;
     }
   }
 
+  template <class SizeType = Cuda::size_type>
   __device__ static inline bool scalar_inter_block_reduction(
       const FunctorType& functor, const Cuda::size_type /*block_id*/,
-      const Cuda::size_type block_count, Cuda::size_type* const shared_data,
-      Cuda::size_type* const global_data, Cuda::size_type* const global_flags) {
+      const Cuda::size_type block_count, SizeType* const shared_data,
+      SizeType* const global_data, Cuda::size_type* const global_flags) {
     Scalar* const global_team_buffer_element = ((Scalar*)global_data);
     Scalar* const my_global_team_buffer_element =
         global_team_buffer_element + blockIdx.x;
@@ -713,17 +668,17 @@ __device__ void cuda_intra_block_reduce_scan(
   const pointer_type tdata_intra = base_data + value_count * threadIdx.y;
 
   {  // Intra-warp reduction:
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_REDUCE_STEP(rtid_intra, tdata_intra, 0)
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_REDUCE_STEP(rtid_intra, tdata_intra, 1)
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_REDUCE_STEP(rtid_intra, tdata_intra, 2)
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_REDUCE_STEP(rtid_intra, tdata_intra, 3)
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_REDUCE_STEP(rtid_intra, tdata_intra, 4)
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
   }
 
   __syncthreads();  // Wait for all warps to reduce
@@ -732,57 +687,31 @@ __device__ void cuda_intra_block_reduce_scan(
     const unsigned rtid_inter = (threadIdx.y ^ BlockSizeMask)
                                 << CudaTraits::WarpIndexShift;
 
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-    unsigned inner_mask =
-        KOKKOS_IMPL_CUDA_BALLOT_MASK(0xffffffff, (rtid_inter < blockDim.y));
-#endif
+    unsigned inner_mask = __ballot_sync(0xffffffff, (rtid_inter < blockDim.y));
     if (rtid_inter < blockDim.y) {
       const pointer_type tdata_inter =
           base_data + value_count * (rtid_inter ^ BlockSizeMask);
 
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
       if ((1 << 5) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP_MASK(inner_mask);
+        __syncwarp(inner_mask);
         BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 5)
       }
       if ((1 << 6) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP_MASK(inner_mask);
+        __syncwarp(inner_mask);
         BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 6)
       }
       if ((1 << 7) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP_MASK(inner_mask);
+        __syncwarp(inner_mask);
         BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 7)
       }
       if ((1 << 8) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP_MASK(inner_mask);
+        __syncwarp(inner_mask);
         BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 8)
       }
       if ((1 << 9) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP_MASK(inner_mask);
+        __syncwarp(inner_mask);
         BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 9)
       }
-#else
-      if ((1 << 5) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP;
-        BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 5)
-      }
-      if ((1 << 6) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP;
-        BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 6)
-      }
-      if ((1 << 7) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP;
-        BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 7)
-      }
-      if ((1 << 8) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP;
-        BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 8)
-      }
-      if ((1 << 9) < BlockSizeMask) {
-        KOKKOS_IMPL_CUDA_SYNCWARP;
-        BLOCK_REDUCE_STEP(rtid_inter, tdata_inter, 9)
-      }
-#endif
 
       if (DoScan) {
         int n =
@@ -795,25 +724,14 @@ __device__ void cuda_intra_block_reduce_scan(
 
         if (!(rtid_inter + n < blockDim.y)) n = 0;
 
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-        KOKKOS_IMPL_CUDA_SYNCWARP_MASK(inner_mask);
-        BLOCK_SCAN_STEP(tdata_inter, n, 8)
-        KOKKOS_IMPL_CUDA_SYNCWARP_MASK(inner_mask);
-        BLOCK_SCAN_STEP(tdata_inter, n, 7)
-        KOKKOS_IMPL_CUDA_SYNCWARP_MASK(inner_mask);
-        BLOCK_SCAN_STEP(tdata_inter, n, 6)
-        KOKKOS_IMPL_CUDA_SYNCWARP_MASK(inner_mask);
-        BLOCK_SCAN_STEP(tdata_inter, n, 5)
-#else
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(inner_mask);
         BLOCK_SCAN_STEP(tdata_inter, n, 8)
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(inner_mask);
         BLOCK_SCAN_STEP(tdata_inter, n, 7)
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(inner_mask);
         BLOCK_SCAN_STEP(tdata_inter, n, 6)
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(inner_mask);
         BLOCK_SCAN_STEP(tdata_inter, n, 5)
-#endif
       }
     }
   }
@@ -832,17 +750,17 @@ __device__ void cuda_intra_block_reduce_scan(
                                               : ((rtid_intra & 16) ? 16 : 0))));
 
     if (!(rtid_intra + n < blockDim.y)) n = 0;
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_SCAN_STEP(tdata_intra, n, 4) __threadfence_block();
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_SCAN_STEP(tdata_intra, n, 3) __threadfence_block();
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_SCAN_STEP(tdata_intra, n, 2) __threadfence_block();
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_SCAN_STEP(tdata_intra, n, 1) __threadfence_block();
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
     BLOCK_SCAN_STEP(tdata_intra, n, 0) __threadfence_block();
-    KOKKOS_IMPL_CUDA_SYNCWARP;
+    __syncwarp(0xffffffff);
   }
 
 #undef BLOCK_SCAN_STEP
@@ -858,12 +776,13 @@ __device__ void cuda_intra_block_reduce_scan(
  *  Global reduce result is in the last threads' 'shared_data' location.
  */
 
-template <bool DoScan, class FunctorType, class ArgTag>
+template <bool DoScan, class FunctorType, class ArgTag,
+          class SizeType = Cuda::size_type>
 __device__ bool cuda_single_inter_block_reduce_scan2(
     const FunctorType& functor, const Cuda::size_type block_id,
-    const Cuda::size_type block_count, Cuda::size_type* const shared_data,
-    Cuda::size_type* const global_data, Cuda::size_type* const global_flags) {
-  using size_type   = Cuda::size_type;
+    const Cuda::size_type block_count, SizeType* const shared_data,
+    SizeType* const global_data, Cuda::size_type* const global_flags) {
+  using size_type   = SizeType;
   using ValueTraits = FunctorValueTraits<FunctorType, ArgTag>;
   using ValueJoin   = FunctorValueJoin<FunctorType, ArgTag>;
   using ValueInit   = FunctorValueInit<FunctorType, ArgTag>;
@@ -953,11 +872,12 @@ __device__ bool cuda_single_inter_block_reduce_scan2(
   return is_last_block;
 }
 
-template <bool DoScan, class FunctorType, class ArgTag>
+template <bool DoScan, class FunctorType, class ArgTag,
+          class SizeType = Cuda::size_type>
 __device__ bool cuda_single_inter_block_reduce_scan(
     const FunctorType& functor, const Cuda::size_type block_id,
-    const Cuda::size_type block_count, Cuda::size_type* const shared_data,
-    Cuda::size_type* const global_data, Cuda::size_type* const global_flags) {
+    const Cuda::size_type block_count, SizeType* const shared_data,
+    SizeType* const global_data, Cuda::size_type* const global_flags) {
   using ValueTraits = FunctorValueTraits<FunctorType, ArgTag>;
   if (!DoScan && ValueTraits::StaticValueSize > 0)
     return Kokkos::Impl::CudaReductionsFunctor<
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Task.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Task.hpp
index 2004edbeacdb4b5b309ea3bd6eb83b3abcfacea6..88ac0d1878a911a876210fe06cc52fa1d8285be6 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Task.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Task.hpp
@@ -54,11 +54,27 @@
 #include <Kokkos_Core_fwd.hpp>
 
 #include <impl/Kokkos_TaskBase.hpp>
-#include <Cuda/Kokkos_Cuda_Error.hpp>  // CUDA_SAFE_CALL
+#include <Cuda/Kokkos_Cuda_Error.hpp>  // KOKKOS_IMPL_CUDA_SAFE_CALL
 #include <impl/Kokkos_TaskTeamMember.hpp>
 
 //----------------------------------------------------------------------------
 
+#if defined(__CUDA_ARCH__)
+#define KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN(MSG)                           \
+  {                                                                        \
+    __syncwarp();                                                          \
+    const unsigned b = __activemask();                                     \
+    if (b != 0xffffffff) {                                                 \
+      printf(" SYNCWARP AT %s (%d,%d,%d) (%d,%d,%d) failed %x\n", MSG,     \
+             blockIdx.x, blockIdx.y, blockIdx.z, threadIdx.x, threadIdx.y, \
+             threadIdx.z, b);                                              \
+      return;                                                              \
+    }                                                                      \
+  }
+#else
+#define KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN(MSG)
+#endif
+
 namespace Kokkos {
 namespace Impl {
 namespace {
@@ -138,13 +154,13 @@ class TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::Cuda, QueueType>> {
       // Broadcast task pointer:
 
       // Sync before the broadcast
-      KOKKOS_IMPL_CUDA_SYNCWARP;
+      __syncwarp(0xffffffff);
 
       // pretend it's an int* for shuffle purposes
       ((int*)&current_task)[0] =
-          KOKKOS_IMPL_CUDA_SHFL(((int*)&current_task)[0], 0, 32);
+          __shfl_sync(0xffffffff, ((int*)&current_task)[0], 0, 32);
       ((int*)&current_task)[1] =
-          KOKKOS_IMPL_CUDA_SHFL(((int*)&current_task)[1], 0, 32);
+          __shfl_sync(0xffffffff, ((int*)&current_task)[1], 0, 32);
 
       if (current_task) {
         KOKKOS_ASSERT(!current_task->as_runnable_task().get_respawn_flag());
@@ -168,7 +184,7 @@ class TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::Cuda, QueueType>> {
 
         // Synchronize threads of the warp and insure memory
         // writes are visible to all threads in the warp.
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(0xffffffff);
 
         if (shared_memory_task_copy->is_team_runnable()) {
           // Thread Team Task
@@ -182,7 +198,7 @@ class TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::Cuda, QueueType>> {
         // Synchronize threads of the warp and insure memory
         // writes are visible to all threads in the warp.
 
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(0xffffffff);
 
         // if(warp_lane < b % CudaTraits::WarpSize) b += CudaTraits::WarpSize;
         // b -= b % CudaTraits::WarpSize;
@@ -196,7 +212,7 @@ class TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::Cuda, QueueType>> {
         // writes are visible to root thread of the warp for
         // respawn or completion.
 
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(0xffffffff);
 
         if (warp_lane == 0) {
           // If respawn requested copy respawn data back to main memory
@@ -249,12 +265,14 @@ class TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::Cuda, QueueType>> {
 
     auto& queue = scheduler.queue();
 
-    CUDA_SAFE_CALL(cudaDeviceSynchronize());
+    Impl::cuda_device_synchronize(
+        "Kokkos::Impl::TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::"
+        "Cuda>::execute: Pre Task Execution");
 
     // Query the stack size, in bytes:
 
     size_t previous_stack_size = 0;
-    CUDA_SAFE_CALL(
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
         cudaDeviceGetLimit(&previous_stack_size, cudaLimitStackSize));
 
     // If not large enough then set the stack size, in bytes:
@@ -262,18 +280,21 @@ class TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::Cuda, QueueType>> {
     const size_t larger_stack_size = 1 << 11;
 
     if (previous_stack_size < larger_stack_size) {
-      CUDA_SAFE_CALL(cudaDeviceSetLimit(cudaLimitStackSize, larger_stack_size));
+      KOKKOS_IMPL_CUDA_SAFE_CALL(
+          cudaDeviceSetLimit(cudaLimitStackSize, larger_stack_size));
     }
 
     cuda_task_queue_execute<<<grid, block, shared_total, stream>>>(
         scheduler, shared_per_warp);
 
-    CUDA_SAFE_CALL(cudaGetLastError());
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGetLastError());
 
-    CUDA_SAFE_CALL(cudaDeviceSynchronize());
+    Impl::cuda_device_synchronize(
+        "Kokkos::Impl::TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::"
+        "Cuda>::execute: Post Task Execution");
 
     if (previous_stack_size < larger_stack_size) {
-      CUDA_SAFE_CALL(
+      KOKKOS_IMPL_CUDA_SAFE_CALL(
           cudaDeviceSetLimit(cudaLimitStackSize, previous_stack_size));
     }
   }
@@ -295,13 +316,17 @@ class TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::Cuda, QueueType>> {
     destroy_type* dtor_ptr =
         (destroy_type*)((char*)storage + sizeof(function_type));
 
-    CUDA_SAFE_CALL(cudaDeviceSynchronize());
+    Impl::cuda_device_synchronize(
+        "Kokkos::Impl::TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::"
+        "Cuda>::execute: Pre Get Function Pointer for Tasks");
 
     set_cuda_task_base_apply_function_pointer<TaskType>
         <<<1, 1>>>(ptr_ptr, dtor_ptr);
 
-    CUDA_SAFE_CALL(cudaGetLastError());
-    CUDA_SAFE_CALL(cudaDeviceSynchronize());
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGetLastError());
+    Impl::cuda_device_synchronize(
+        "Kokkos::Impl::TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::"
+        "Cuda>::execute: Post Get Function Pointer for Tasks");
 
     ptr  = *ptr_ptr;
     dtor = *dtor_ptr;
@@ -372,23 +397,20 @@ class TaskQueueSpecializationConstrained<
           // count of 0 also. Otherwise, returns a task from another queue
           // or `end` if one couldn't be popped
           task_ptr = team_queue.attempt_to_steal_task();
-#if 0
-          if(task != no_more_tasks_sentinel && task != end) {
-            std::printf("task stolen on rank %d\n", team_exec.league_rank());
-          }
-#endif
         }
       }
 
       // Synchronize warp with memory fence before broadcasting task pointer:
 
       // KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN( "A" );
-      KOKKOS_IMPL_CUDA_SYNCWARP;
+      __syncwarp(0xffffffff);
 
       // Broadcast task pointer:
 
-      ((int*)&task_ptr)[0] = KOKKOS_IMPL_CUDA_SHFL(((int*)&task_ptr)[0], 0, 32);
-      ((int*)&task_ptr)[1] = KOKKOS_IMPL_CUDA_SHFL(((int*)&task_ptr)[1], 0, 32);
+      ((int*)&task_ptr)[0] =
+          __shfl_sync(0xffffffff, ((int*)&task_ptr)[0], 0, 32);
+      ((int*)&task_ptr)[1] =
+          __shfl_sync(0xffffffff, ((int*)&task_ptr)[1], 0, 32);
 
 #if defined(KOKKOS_ENABLE_DEBUG)
       KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN("TaskQueue CUDA task_ptr");
@@ -418,7 +440,7 @@ class TaskQueueSpecializationConstrained<
         // writes are visible to all threads in the warp.
 
         // KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN( "B" );
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(0xffffffff);
 
         if (task_root_type::TaskTeam == task_shmem->m_task_type) {
           // Thread Team Task
@@ -432,7 +454,7 @@ class TaskQueueSpecializationConstrained<
         // writes are visible to all threads in the warp.
 
         // KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN( "C" );
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(0xffffffff);
 
         // copy task closure from shared to global memory:
 
@@ -445,7 +467,7 @@ class TaskQueueSpecializationConstrained<
         // respawn or completion.
 
         // KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN( "D" );
-        KOKKOS_IMPL_CUDA_SYNCWARP;
+        __syncwarp(0xffffffff);
 
         // If respawn requested copy respawn data back to main memory
 
@@ -475,12 +497,14 @@ class TaskQueueSpecializationConstrained<
     auto& queue = scheduler.queue();
     queue.initialize_team_queues(warps_per_block * grid.x);
 
-    CUDA_SAFE_CALL(cudaDeviceSynchronize());
+    Impl::cuda_device_synchronize(
+        "Kokkos::Impl::TaskQueueSpecializationConstrained<SimpleTaskScheduler<"
+        "Kokkos::Cuda>::execute: Pre Execute Task");
 
     // Query the stack size, in bytes:
 
     size_t previous_stack_size = 0;
-    CUDA_SAFE_CALL(
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
         cudaDeviceGetLimit(&previous_stack_size, cudaLimitStackSize));
 
     // If not large enough then set the stack size, in bytes:
@@ -488,18 +512,21 @@ class TaskQueueSpecializationConstrained<
     const size_t larger_stack_size = 2048;
 
     if (previous_stack_size < larger_stack_size) {
-      CUDA_SAFE_CALL(cudaDeviceSetLimit(cudaLimitStackSize, larger_stack_size));
+      KOKKOS_IMPL_CUDA_SAFE_CALL(
+          cudaDeviceSetLimit(cudaLimitStackSize, larger_stack_size));
     }
 
     cuda_task_queue_execute<<<grid, block, shared_total, stream>>>(
         scheduler, shared_per_warp);
 
-    CUDA_SAFE_CALL(cudaGetLastError());
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGetLastError());
 
-    CUDA_SAFE_CALL(cudaDeviceSynchronize());
+    Impl::cuda_device_synchronize(
+        "Kokkos::Impl::TaskQueueSpecializationConstrained<SimpleTaskScheduler<"
+        "Kokkos::Cuda>::execute: Post Execute Task");
 
     if (previous_stack_size < larger_stack_size) {
-      CUDA_SAFE_CALL(
+      KOKKOS_IMPL_CUDA_SAFE_CALL(
           cudaDeviceSetLimit(cudaLimitStackSize, previous_stack_size));
     }
   }
@@ -516,13 +543,17 @@ class TaskQueueSpecializationConstrained<
     destroy_type* dtor_ptr =
         (destroy_type*)((char*)storage + sizeof(function_type));
 
-    CUDA_SAFE_CALL(cudaDeviceSynchronize());
+    Impl::cuda_device_synchronize(
+        "Kokkos::Impl::TaskQueueSpecializationConstrained<SimpleTaskScheduler<"
+        "Kokkos::Cuda>::get_function_pointer: Pre Get Function Pointer");
 
     set_cuda_task_base_apply_function_pointer<TaskType>
         <<<1, 1>>>(ptr_ptr, dtor_ptr);
 
-    CUDA_SAFE_CALL(cudaGetLastError());
-    CUDA_SAFE_CALL(cudaDeviceSynchronize());
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaGetLastError());
+    Impl::cuda_device_synchronize(
+        "Kokkos::Impl::TaskQueueSpecializationConstrained<SimpleTaskScheduler<"
+        "Kokkos::Cuda>::get_function_pointer: Post Get Function Pointer");
 
     ptr  = *ptr_ptr;
     dtor = *dtor_ptr;
@@ -609,7 +640,7 @@ class TaskExec<Kokkos::Cuda, Scheduler> {
 
   __device__ void team_barrier() const {
     if (1 < m_team_size) {
-      KOKKOS_IMPL_CUDA_SYNCWARP;
+      __syncwarp(0xffffffff);
     }
   }
 
@@ -1205,5 +1236,7 @@ KOKKOS_INLINE_FUNCTION void single(
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 
+#undef KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN
+
 #endif /* #if defined( KOKKOS_ENABLE_TASKDAG ) */
 #endif /* #ifndef KOKKOS_IMPL_CUDA_TASK_HPP */
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Team.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Team.hpp
index e7806390155d46fd811a21432d9f9d268c457468..922b980a2545b4e35d573d44806d76fdf1ca1ea2 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Team.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Team.hpp
@@ -340,191 +340,6 @@ class CudaTeamMember {
 #endif
   }
 
-  //--------------------------------------------------------------------------
-  /**\brief  Global reduction across all blocks
-   *
-   *  Return !0 if reducer contains the final value
-   */
-  template <typename ReducerType>
-  KOKKOS_INLINE_FUNCTION static
-      typename std::enable_if<is_reducer<ReducerType>::value, int>::type
-      global_reduce(ReducerType const& reducer, int* const global_scratch_flags,
-                    void* const global_scratch_space, void* const shmem,
-                    int const shmem_size) {
-#ifdef __CUDA_ARCH__
-
-    using value_type   = typename ReducerType::value_type;
-    using pointer_type = value_type volatile*;
-
-    // Number of shared memory entries for the reduction:
-    const int nsh = shmem_size / sizeof(value_type);
-
-    // Number of CUDA threads in the block, rank within the block
-    const int nid = blockDim.x * blockDim.y * blockDim.z;
-    const int tid =
-        threadIdx.x + blockDim.x * (threadIdx.y + blockDim.y * threadIdx.z);
-
-    // Reduces within block using all available shared memory
-    // Contributes if it is the root "vector lane"
-
-    // wn == number of warps in the block
-    // wx == which lane within the warp
-    // wy == which warp within the block
-
-    const int wn =
-        (nid + CudaTraits::WarpIndexMask) >> CudaTraits::WarpIndexShift;
-    const int wx = tid & CudaTraits::WarpIndexMask;
-    const int wy = tid >> CudaTraits::WarpIndexShift;
-
-    //------------------------
-    {  // Intra warp shuffle reduction from contributing CUDA threads
-
-      value_type tmp(reducer.reference());
-
-      for (int i = CudaTraits::WarpSize; (int)blockDim.x <= (i >>= 1);) {
-        Impl::in_place_shfl_down(reducer.reference(), tmp, i,
-                                 CudaTraits::WarpSize);
-
-        // Root of each vector lane reduces "thread" contribution
-        if (0 == threadIdx.x && wx < i) {
-          reducer.join(&tmp, reducer.data());
-        }
-      }
-
-      // Reduce across warps using shared memory.
-      // Number of warps may not be power of two.
-
-      __syncthreads();  // Wait before shared data write
-
-      // Number of shared memory entries for the reduction
-      // is at most one per warp
-      const int nentry = wn < nsh ? wn : nsh;
-
-      if (0 == wx && wy < nentry) {
-        // Root thread of warp 'wy' has warp's value to contribute
-        ((value_type*)shmem)[wy] = tmp;
-      }
-
-      __syncthreads();  // Wait for write to be visible to block
-
-      // When more warps than shared entries
-      // then warps must take turns joining their contribution
-      // to the designated shared memory entry.
-      for (int i = nentry; i < wn; i += nentry) {
-        const int k = wy - i;
-
-        if (0 == wx && i <= wy && k < nentry) {
-          // Root thread of warp 'wy' has warp's value to contribute
-          reducer.join(((value_type*)shmem) + k, &tmp);
-        }
-
-        __syncthreads();  // Wait for write to be visible to block
-      }
-
-      // One warp performs the inter-warp reduction:
-
-      if (0 == wy) {
-        // Start fan-in at power of two covering nentry
-
-        for (int i = (1 << (32 - __clz(nentry - 1))); (i >>= 1);) {
-          const int k = wx + i;
-          if (wx < i && k < nentry) {
-            reducer.join(((pointer_type)shmem) + wx, ((pointer_type)shmem) + k);
-            __threadfence_block();  // Wait for write to be visible to warp
-          }
-        }
-      }
-    }
-    //------------------------
-    {  // Write block's value to global_scratch_memory
-
-      int last_block = 0;
-
-      if (0 == wx) {
-        reducer.copy(((pointer_type)global_scratch_space) +
-                         blockIdx.x * reducer.length(),
-                     reducer.data());
-
-        __threadfence();  // Wait until global write is visible.
-
-        last_block = (int)gridDim.x ==
-                     1 + Kokkos::atomic_fetch_add(global_scratch_flags, 1);
-
-        // If last block then reset count
-        if (last_block) *global_scratch_flags = 0;
-      }
-
-      last_block = __syncthreads_or(last_block);
-
-      if (!last_block) return 0;
-    }
-    //------------------------
-    // Last block reads global_scratch_memory into shared memory.
-
-    const int nentry = nid < gridDim.x ? (nid < nsh ? nid : nsh)
-                                       : (gridDim.x < nsh ? gridDim.x : nsh);
-
-    // nentry = min( nid , nsh , gridDim.x )
-
-    // whole block reads global memory into shared memory:
-
-    if (tid < nentry) {
-      const int offset = tid * reducer.length();
-
-      reducer.copy(((pointer_type)shmem) + offset,
-                   ((pointer_type)global_scratch_space) + offset);
-
-      for (int i = nentry + tid; i < (int)gridDim.x; i += nentry) {
-        reducer.join(
-            ((pointer_type)shmem) + offset,
-            ((pointer_type)global_scratch_space) + i * reducer.length());
-      }
-    }
-
-    __syncthreads();  // Wait for writes to be visible to block
-
-    if (0 == wy) {
-      // Iterate to reduce shared memory to single warp fan-in size
-
-      const int nreduce =
-          CudaTraits::WarpSize < nentry ? CudaTraits::WarpSize : nentry;
-
-      // nreduce = min( CudaTraits::WarpSize , nsh , gridDim.x )
-
-      if (wx < nreduce && nreduce < nentry) {
-        for (int i = nreduce + wx; i < nentry; i += nreduce) {
-          reducer.join(((pointer_type)shmem) + wx, ((pointer_type)shmem) + i);
-        }
-        __threadfence_block();  // Wait for writes to be visible to warp
-      }
-
-      // Start fan-in at power of two covering nentry
-
-      for (int i = (1 << (32 - __clz(nreduce - 1))); (i >>= 1);) {
-        const int k = wx + i;
-        if (wx < i && k < nreduce) {
-          reducer.join(((pointer_type)shmem) + wx, ((pointer_type)shmem) + k);
-          __threadfence_block();  // Wait for writes to be visible to warp
-        }
-      }
-
-      if (0 == wx) {
-        reducer.copy(reducer.data(), (pointer_type)shmem);
-        return 1;
-      }
-    }
-    return 0;
-
-#else
-    (void)reducer;
-    (void)global_scratch_flags;
-    (void)global_scratch_space;
-    (void)shmem;
-    (void)shmem_size;
-    return 0;
-#endif
-  }
-
   //----------------------------------------
   // Private for the driver
 
@@ -533,7 +348,7 @@ class CudaTeamMember {
                  void* scratch_level_1_ptr, const int scratch_level_1_size,
                  const int arg_league_rank, const int arg_league_size)
       : m_team_reduce(shared),
-        m_team_shared(((char*)shared) + shared_begin, shared_size,
+        m_team_shared(static_cast<char*>(shared) + shared_begin, shared_size,
                       scratch_level_1_ptr, scratch_level_1_size),
         m_team_reduce_size(shared_begin),
         m_league_rank(arg_league_rank),
@@ -854,14 +669,10 @@ KOKKOS_INLINE_FUNCTION void parallel_for(
        i += blockDim.x) {
     closure(i);
   }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-  KOKKOS_IMPL_CUDA_SYNCWARP_MASK(
-      blockDim.x == 32 ? 0xffffffff
-                       : ((1 << blockDim.x) - 1)
-                             << (threadIdx.y % (32 / blockDim.x)) * blockDim.x);
-#else
-  KOKKOS_IMPL_CUDA_SYNCWARP;
-#endif
+  __syncwarp(blockDim.x == 32
+                 ? 0xffffffff
+                 : ((1 << blockDim.x) - 1)
+                       << (threadIdx.y % (32 / blockDim.x)) * blockDim.x);
 #endif
 }
 
@@ -1100,14 +911,10 @@ KOKKOS_INLINE_FUNCTION void single(
   (void)lambda;
 #ifdef __CUDA_ARCH__
   if (threadIdx.x == 0) lambda();
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-  KOKKOS_IMPL_CUDA_SYNCWARP_MASK(
-      blockDim.x == 32 ? 0xffffffff
-                       : ((1 << blockDim.x) - 1)
-                             << (threadIdx.y % (32 / blockDim.x)) * blockDim.x);
-#else
-  KOKKOS_IMPL_CUDA_SYNCWARP;
-#endif
+  __syncwarp(blockDim.x == 32
+                 ? 0xffffffff
+                 : ((1 << blockDim.x) - 1)
+                       << (threadIdx.y % (32 / blockDim.x)) * blockDim.x);
 #endif
 }
 
@@ -1118,14 +925,10 @@ KOKKOS_INLINE_FUNCTION void single(
   (void)lambda;
 #ifdef __CUDA_ARCH__
   if (threadIdx.x == 0 && threadIdx.y == 0) lambda();
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-  KOKKOS_IMPL_CUDA_SYNCWARP_MASK(
-      blockDim.x == 32 ? 0xffffffff
-                       : ((1 << blockDim.x) - 1)
-                             << (threadIdx.y % (32 / blockDim.x)) * blockDim.x);
-#else
-  KOKKOS_IMPL_CUDA_SYNCWARP;
-#endif
+  __syncwarp(blockDim.x == 32
+                 ? 0xffffffff
+                 : ((1 << blockDim.x) - 1)
+                       << (threadIdx.y % (32 / blockDim.x)) * blockDim.x);
 #endif
 }
 
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Vectorization.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Vectorization.hpp
index 7f7b7b6e78adc3de9d5ae446565eedc7d00439f5..31d3c47e1c9c9af3b6c6d8c918abe01dd0b238fe 100644
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Vectorization.hpp
+++ b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Vectorization.hpp
@@ -48,7 +48,12 @@
 #ifdef KOKKOS_ENABLE_CUDA
 
 #include <type_traits>
-#include <Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp>
+
+#if !defined(KOKKOS_COMPILER_CLANG)
+#define KOKKOS_IMPL_CUDA_MAX_SHFL_SIZEOF sizeof(long long)
+#else
+#define KOKKOS_IMPL_CUDA_MAX_SHFL_SIZEOF sizeof(int)
+#endif
 
 namespace Kokkos {
 
@@ -61,7 +66,7 @@ constexpr unsigned shfl_all_mask = 0xffffffffu;
 // Shuffle operations require input to be a register (stack) variable
 
 // Derived implements do_shfl_op(unsigned mask, T& in, int lane, int width),
-// which turns in to one of KOKKOS_IMPL_CUDA_SHFL(_UP_|_DOWN_|_)MASK
+// which turns in to one of __shfl_sync(_up|_down)
 // Since the logic with respect to value sizes, etc., is the same everywhere,
 // put it all in one place.
 template <class Derived>
@@ -157,7 +162,7 @@ struct in_place_shfl_fn : in_place_shfl_op<in_place_shfl_fn> {
     (void)val;
     (void)lane;
     (void)width;
-    return KOKKOS_IMPL_CUDA_SHFL_MASK(mask, val, lane, width);
+    return __shfl_sync(mask, val, lane, width);
   }
 };
 template <class... Args>
@@ -170,7 +175,7 @@ struct in_place_shfl_up_fn : in_place_shfl_op<in_place_shfl_up_fn> {
   __device__ KOKKOS_IMPL_FORCEINLINE T do_shfl_op(unsigned mask, T& val,
                                                   int lane, int width) const
       noexcept {
-    return KOKKOS_IMPL_CUDA_SHFL_UP_MASK(mask, val, lane, width);
+    return __shfl_up_sync(mask, val, lane, width);
   }
 };
 template <class... Args>
@@ -188,7 +193,7 @@ struct in_place_shfl_down_fn : in_place_shfl_op<in_place_shfl_down_fn> {
     (void)val;
     (void)lane;
     (void)width;
-    return KOKKOS_IMPL_CUDA_SHFL_DOWN_MASK(mask, val, lane, width);
+    return __shfl_down_sync(mask, val, lane, width);
   }
 };
 template <class... Args>
@@ -228,5 +233,7 @@ __device__ inline T shfl_up(const T& val, int delta, int width,
 
 }  // end namespace Kokkos
 
+#undef KOKKOS_IMPL_CUDA_MAX_SHFL_SIZEOF
+
 #endif  // defined( KOKKOS_ENABLE_CUDA )
 #endif  // !defined( KOKKOS_CUDA_VECTORIZATION_HPP )
diff --git a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp b/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp
deleted file mode 100644
index 0cdd84ce27157e118065c6fbcf2da71a875b81e0..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/src/Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#include <Kokkos_Macros.hpp>
-
-#if defined(__CUDA_ARCH__)
-#define KOKKOS_IMPL_CUDA_ACTIVEMASK __activemask()
-#define KOKKOS_IMPL_CUDA_SYNCWARP __syncwarp(0xffffffff)
-#define KOKKOS_IMPL_CUDA_SYNCWARP_MASK(m) __syncwarp(m)
-#define KOKKOS_IMPL_CUDA_BALLOT(x) __ballot_sync(__activemask(), x)
-#define KOKKOS_IMPL_CUDA_BALLOT_MASK(m, x) __ballot_sync(m, x)
-#define KOKKOS_IMPL_CUDA_SHFL(x, y, z) __shfl_sync(0xffffffff, x, y, z)
-#define KOKKOS_IMPL_CUDA_SHFL_MASK(m, x, y, z) __shfl_sync(m, x, y, z)
-#define KOKKOS_IMPL_CUDA_SHFL_UP(x, y, z) __shfl_up_sync(0xffffffff, x, y, z)
-#define KOKKOS_IMPL_CUDA_SHFL_UP_MASK(m, x, y, z) __shfl_up_sync(m, x, y, z)
-#define KOKKOS_IMPL_CUDA_SHFL_DOWN(x, y, z) \
-  __shfl_down_sync(0xffffffff, x, y, z)
-#define KOKKOS_IMPL_CUDA_SHFL_DOWN_MASK(m, x, y, z) __shfl_down_sync(m, x, y, z)
-#else
-#define KOKKOS_IMPL_CUDA_ACTIVEMASK 0
-#define KOKKOS_IMPL_CUDA_SYNCWARP
-#define KOKKOS_IMPL_CUDA_SYNCWARP_MASK(m) (void)m
-#define KOKKOS_IMPL_CUDA_BALLOT(x) 0
-#define KOKKOS_IMPL_CUDA_BALLOT_MASK(m, x) 0
-#define KOKKOS_IMPL_CUDA_SHFL(x, y, z) 0
-#define KOKKOS_IMPL_CUDA_SHFL_MASK(m, x, y, z) 0
-#define KOKKOS_IMPL_CUDA_SHFL_UP(x, y, z) 0
-#define KOKKOS_IMPL_CUDA_SHFL_DOWN(x, y, z) 0
-#define KOKKOS_IMPL_CUDA_SHFL_DOWN_MASK(m, x, y, z) 0
-#endif
-
-#if !defined(KOKKOS_COMPILER_CLANG)
-#define KOKKOS_IMPL_CUDA_MAX_SHFL_SIZEOF sizeof(long long)
-#else
-#define KOKKOS_IMPL_CUDA_MAX_SHFL_SIZEOF sizeof(int)
-#endif
-
-#if defined(__CUDA_ARCH__)
-#define KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN(MSG)                           \
-  {                                                                        \
-    __syncwarp();                                                          \
-    const unsigned b = __activemask();                                     \
-    if (b != 0xffffffff) {                                                 \
-      printf(" SYNCWARP AT %s (%d,%d,%d) (%d,%d,%d) failed %x\n", MSG,     \
-             blockIdx.x, blockIdx.y, blockIdx.z, threadIdx.x, threadIdx.y, \
-             threadIdx.z, b);                                              \
-      return;                                                              \
-    }                                                                      \
-  }
-#else
-#define KOKKOS_IMPL_CUDA_SYNCWARP_OR_RETURN(MSG)
-#endif
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_BlockSize_Deduction.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_BlockSize_Deduction.hpp
index 9278d1bdc9efcc2a76183085c974afef41413e3c..7eb3e1e9f70fe4cf724e3b766e38ebc16b3c7c8f 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_BlockSize_Deduction.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_BlockSize_Deduction.hpp
@@ -45,6 +45,7 @@
 #ifndef KOKKOS_HIP_BLOCKSIZE_DEDUCTION_HPP
 #define KOKKOS_HIP_BLOCKSIZE_DEDUCTION_HPP
 
+#include <functional>
 #include <Kokkos_Macros.hpp>
 
 #if defined(__HIPCC__)
@@ -56,118 +57,239 @@ namespace Kokkos {
 namespace Experimental {
 namespace Impl {
 
-template <typename DriverType, bool, int MaxThreadsPerBlock, int MinBlocksPerSM>
-void hipOccupancy(int *numBlocks, int blockSize, int sharedmem) {
-  // FIXME_HIP - currently the "constant" path is unimplemented.
-  //             we should look at whether it's functional, and
-  //             perform some simple scaling studies to see when /
-  //             if the constant launcher outperforms the current
-  //             pass by pointer shared launcher
-  HIP_SAFE_CALL(hipOccupancyMaxActiveBlocksPerMultiprocessor(
-      numBlocks,
-      hip_parallel_launch_local_memory<DriverType, MaxThreadsPerBlock,
-                                       MinBlocksPerSM>,
-      blockSize, sharedmem));
-}
+enum class BlockType { Max, Preferred };
 
-template <typename DriverType, bool constant>
-void hipOccupancy(int *numBlocks, int blockSize, int sharedmem) {
-  hipOccupancy<DriverType, constant, HIPTraits::MaxThreadsPerBlock, 1>(
-      numBlocks, blockSize, sharedmem);
+template <typename DriverType, typename LaunchBounds = Kokkos::LaunchBounds<>,
+          HIPLaunchMechanism LaunchMechanism =
+              DeduceHIPLaunchMechanism<DriverType>::launch_mechanism>
+unsigned get_preferred_blocksize_impl() {
+  // FIXME_HIP - could be if constexpr for c++17
+  if (!HIPParallelLaunch<DriverType, LaunchBounds,
+                         LaunchMechanism>::default_launchbounds()) {
+    // use the user specified value
+    return LaunchBounds::maxTperB;
+  } else {
+    if (HIPParallelLaunch<DriverType, LaunchBounds,
+                          LaunchMechanism>::get_scratch_size() > 0) {
+      return HIPTraits::ConservativeThreadsPerBlock;
+    }
+    return HIPTraits::MaxThreadsPerBlock;
+  }
 }
 
-template <class FunctorType, class LaunchBounds, typename F>
-int hip_internal_get_block_size(const F &condition_check,
-                                const HIPInternal *hip_instance,
-                                const hipFuncAttributes &attr,
-                                const FunctorType &f,
-                                const size_t vector_length,
-                                const size_t shmem_block,
-                                const size_t shmem_thread) {
-  const int min_blocks_per_sm =
-      LaunchBounds::minBperSM == 0 ? 1 : LaunchBounds::minBperSM;
-  const int max_threads_per_block = LaunchBounds::maxTperB == 0
-                                        ? HIPTraits::MaxThreadsPerBlock
-                                        : LaunchBounds::maxTperB;
-
-  const int regs_per_wavefront  = std::max(attr.numRegs, 1);
-  const int regs_per_sm         = hip_instance->m_regsPerSM;
-  const int shmem_per_sm        = hip_instance->m_shmemPerSM;
-  const int max_shmem_per_block = hip_instance->m_maxShmemPerBlock;
-  const int max_blocks_per_sm   = hip_instance->m_maxBlocksPerSM;
-  const int max_threads_per_sm  = hip_instance->m_maxThreadsPerSM;
-
-  int block_size = max_threads_per_block;
-  KOKKOS_ASSERT(block_size > 0);
-  const int blocks_per_warp =
-      (block_size + HIPTraits::WarpSize - 1) / HIPTraits::WarpSize;
-
-  int functor_shmem = ::Kokkos::Impl::FunctorTeamShmemSize<FunctorType>::value(
-      f, block_size / vector_length);
-  int total_shmem = shmem_block + shmem_thread * (block_size / vector_length) +
-                    functor_shmem + attr.sharedSizeBytes;
-  int max_blocks_regs = regs_per_sm / (regs_per_wavefront * blocks_per_warp);
-  int max_blocks_shmem =
-      (total_shmem < max_shmem_per_block)
-          ? (total_shmem > 0 ? shmem_per_sm / total_shmem : max_blocks_regs)
-          : 0;
-  int blocks_per_sm  = std::min(max_blocks_regs, max_blocks_shmem);
-  int threads_per_sm = blocks_per_sm * block_size;
-  if (threads_per_sm > max_threads_per_sm) {
-    blocks_per_sm  = max_threads_per_sm / block_size;
-    threads_per_sm = blocks_per_sm * block_size;
+// FIXME_HIP - entire function could be constexpr for c++17
+template <typename DriverType, typename LaunchBounds = Kokkos::LaunchBounds<>,
+          HIPLaunchMechanism LaunchMechanism =
+              DeduceHIPLaunchMechanism<DriverType>::launch_mechanism>
+unsigned get_max_blocksize_impl() {
+  // FIXME_HIP - could be if constexpr for c++17
+  if (!HIPParallelLaunch<DriverType, LaunchBounds,
+                         LaunchMechanism>::default_launchbounds()) {
+    // use the user specified value
+    return LaunchBounds::maxTperB;
+  } else {
+    // we can always fit 1024 threads blocks if we only care about registers
+    // ... and don't mind spilling
+    return HIPTraits::MaxThreadsPerBlock;
   }
-  int opt_block_size =
-      (blocks_per_sm >= min_blocks_per_sm) ? block_size : min_blocks_per_sm;
-  int opt_threads_per_sm = threads_per_sm;
-  block_size -= HIPTraits::WarpSize;
-  while (condition_check(blocks_per_sm) &&
-         (block_size >= HIPTraits::WarpSize)) {
-    functor_shmem = ::Kokkos::Impl::FunctorTeamShmemSize<FunctorType>::value(
-        f, block_size / vector_length);
-    total_shmem = shmem_block + shmem_thread * (block_size / vector_length) +
-                  functor_shmem + attr.sharedSizeBytes;
-    max_blocks_regs = regs_per_sm / (regs_per_wavefront * blocks_per_warp);
-    max_blocks_shmem =
-        (total_shmem < max_shmem_per_block)
-            ? (total_shmem > 0 ? shmem_per_sm / total_shmem : max_blocks_regs)
-            : 0;
-    blocks_per_sm  = std::min(max_blocks_regs, max_blocks_shmem);
-    threads_per_sm = blocks_per_sm * block_size;
-    if (threads_per_sm > max_threads_per_sm) {
-      blocks_per_sm  = max_threads_per_sm / block_size;
-      threads_per_sm = blocks_per_sm * block_size;
-    }
-    if ((blocks_per_sm >= min_blocks_per_sm) &&
-        (blocks_per_sm <= max_blocks_per_sm)) {
-      if (threads_per_sm >= opt_threads_per_sm) {
-        opt_block_size     = block_size;
-        opt_threads_per_sm = threads_per_sm;
+}
+
+// convenience method to select and return the proper function attributes
+// for a kernel, given the launch bounds et al.
+template <typename DriverType, typename LaunchBounds = Kokkos::LaunchBounds<>,
+          BlockType BlockSize = BlockType::Max,
+          HIPLaunchMechanism LaunchMechanism =
+              DeduceHIPLaunchMechanism<DriverType>::launch_mechanism>
+hipFuncAttributes get_hip_func_attributes_impl() {
+  // FIXME_HIP - could be if constexpr for c++17
+  if (!HIPParallelLaunch<DriverType, LaunchBounds,
+                         LaunchMechanism>::default_launchbounds()) {
+    // for user defined, we *always* honor the request
+    return HIPParallelLaunch<DriverType, LaunchBounds,
+                             LaunchMechanism>::get_hip_func_attributes();
+  } else {
+    // FIXME_HIP - could be if constexpr for c++17
+    if (BlockSize == BlockType::Max) {
+      return HIPParallelLaunch<
+          DriverType, Kokkos::LaunchBounds<HIPTraits::MaxThreadsPerBlock, 1>,
+          LaunchMechanism>::get_hip_func_attributes();
+    } else {
+      const int blocksize =
+          get_preferred_blocksize_impl<DriverType, LaunchBounds,
+                                       LaunchMechanism>();
+      if (blocksize == HIPTraits::MaxThreadsPerBlock) {
+        return HIPParallelLaunch<
+            DriverType, Kokkos::LaunchBounds<HIPTraits::MaxThreadsPerBlock, 1>,
+            LaunchMechanism>::get_hip_func_attributes();
+      } else {
+        return HIPParallelLaunch<
+            DriverType,
+            Kokkos::LaunchBounds<HIPTraits::ConservativeThreadsPerBlock, 1>,
+            LaunchMechanism>::get_hip_func_attributes();
       }
     }
-    block_size -= HIPTraits::WarpSize;
   }
-  return opt_block_size;
 }
 
-template <class FunctorType, class LaunchBounds>
-int hip_get_max_block_size(const HIPInternal *hip_instance,
-                           const hipFuncAttributes &attr, const FunctorType &f,
-                           const size_t vector_length, const size_t shmem_block,
-                           const size_t shmem_thread) {
-  return hip_internal_get_block_size<FunctorType, LaunchBounds>(
-      [](int x) { return x == 0; }, hip_instance, attr, f, vector_length,
-      shmem_block, shmem_thread);
+// Given an initial block-size limitation based on register usage
+// determine the block size to select based on LDS limitation
+template <BlockType BlockSize, class DriverType, class LaunchBounds,
+          typename ShmemFunctor>
+unsigned hip_internal_get_block_size(const HIPInternal *hip_instance,
+                                     const ShmemFunctor &f,
+                                     const unsigned tperb_reg) {
+  // translate LB from CUDA to HIP
+  const unsigned min_waves_per_eu =
+      LaunchBounds::minBperSM ? LaunchBounds::minBperSM : 1;
+  const unsigned min_threads_per_sm = min_waves_per_eu * HIPTraits::WarpSize;
+  const unsigned shmem_per_sm       = hip_instance->m_shmemPerSM;
+  unsigned block_size               = tperb_reg;
+  do {
+    unsigned total_shmem = f(block_size);
+    // find how many threads we can fit with this blocksize based on LDS usage
+    unsigned tperb_shmem = total_shmem > shmem_per_sm ? 0 : block_size;
+
+    // FIXME_HIP - could be if constexpr for c++17
+    if (BlockSize == BlockType::Max) {
+      // we want the maximum blocksize possible
+      // just wait until we get a case where we can fit the LDS per SM
+      if (tperb_shmem) return block_size;
+    } else {
+      if (block_size == tperb_reg && tperb_shmem >= tperb_reg) {
+        // fast path for exit on first iteration if registers are more limiting
+        // than LDS usage, just use the register limited size
+        return tperb_reg;
+      }
+      // otherwise we need to apply a heuristic to choose the blocksize
+      // the current launchbound selection scheme is:
+      //      1. If no spills, choose 1024 [MaxThreadsPerBlock]
+      //      2. Otherwise, choose 256 [ConservativeThreadsPerBlock]
+      //
+      // For blocksizes between 256 and 1024, we'll be forced to use the 1024 LB
+      // and we'll already have pretty decent occupancy, thus dropping to 256
+      // *probably* isn't a concern
+      const unsigned blocks_per_cu_shmem = shmem_per_sm / total_shmem;
+      const unsigned tperb = tperb_shmem < tperb_reg ? tperb_shmem : tperb_reg;
+
+      // for anything with > 4 WF's & can fit multiple blocks
+      // we're probably not occupancy limited so just return that
+      if (blocks_per_cu_shmem > 1 &&
+          tperb > HIPTraits::ConservativeThreadsPerBlock) {
+        return block_size;
+      }
+
+      // otherwise, it's probably better to drop to the first valid size that
+      // fits in the ConservativeThreadsPerBlock
+      if (tperb >= min_threads_per_sm) return block_size;
+    }
+    block_size >>= 1;
+  } while (block_size >= HIPTraits::WarpSize);
+  // TODO: return a negative, add an error to kernel launch
+  return 0;
+}
+
+// Standardized blocksize deduction for parallel constructs with no LDS usage
+// Returns the preferred blocksize as dictated by register usage
+//
+// Note: a returned block_size of zero indicates that the algorithm could not
+//       find a valid block size.  The caller is responsible for error handling.
+template <typename DriverType, typename LaunchBounds>
+unsigned hip_get_preferred_blocksize() {
+  return get_preferred_blocksize_impl<DriverType, LaunchBounds>();
+}
+
+// Standardized blocksize deduction for parallel constructs with no LDS usage
+// Returns the max blocksize as dictated by register usage
+//
+// Note: a returned block_size of zero indicates that the algorithm could not
+//       find a valid block size.  The caller is responsible for error handling.
+template <typename DriverType, typename LaunchBounds>
+unsigned hip_get_max_blocksize() {
+  return get_max_blocksize_impl<DriverType, LaunchBounds>();
+}
+
+// Standardized blocksize deduction for non-teams parallel constructs with LDS
+// usage Returns the 'preferred' blocksize, as determined by the heuristics in
+// hip_internal_get_block_size
+//
+// The ShmemFunctor takes a single argument of the current blocksize under
+// consideration, and returns the LDS usage
+//
+// Note: a returned block_size of zero indicates that the algorithm could not
+//       find a valid block size.  The caller is responsible for error handling.
+template <typename DriverType, typename LaunchBounds, typename ShmemFunctor>
+unsigned hip_get_preferred_blocksize(HIPInternal const *hip_instance,
+                                     ShmemFunctor const &f) {
+  // get preferred blocksize limited by register usage
+  const unsigned tperb_reg =
+      hip_get_preferred_blocksize<DriverType, LaunchBounds>();
+  return hip_internal_get_block_size<BlockType::Preferred, DriverType,
+                                     LaunchBounds>(hip_instance, f, tperb_reg);
+}
+
+// Standardized blocksize deduction for teams-based parallel constructs with LDS
+// usage Returns the 'preferred' blocksize, as determined by the heuristics in
+// hip_internal_get_block_size
+//
+// The ShmemTeamsFunctor takes two arguments: the hipFunctionAttributes and
+//  the current blocksize under consideration, and returns the LDS usage
+//
+// Note: a returned block_size of zero indicates that the algorithm could not
+//       find a valid block size.  The caller is responsible for error handling.
+template <typename DriverType, typename LaunchBounds,
+          typename ShmemTeamsFunctor>
+unsigned hip_get_preferred_team_blocksize(HIPInternal const *hip_instance,
+                                          ShmemTeamsFunctor const &f) {
+  hipFuncAttributes attr =
+      get_hip_func_attributes_impl<DriverType, LaunchBounds,
+                                   BlockType::Preferred>();
+  // get preferred blocksize limited by register usage
+  using namespace std::placeholders;
+  const unsigned tperb_reg =
+      hip_get_preferred_blocksize<DriverType, LaunchBounds>();
+  return hip_internal_get_block_size<BlockType::Preferred, DriverType,
+                                     LaunchBounds>(
+      hip_instance, std::bind(f, attr, _1), tperb_reg);
+}
+
+// Standardized blocksize deduction for non-teams parallel constructs with LDS
+// usage Returns the maximum possible blocksize, as determined by the heuristics
+// in hip_internal_get_block_size
+//
+// The ShmemFunctor takes a single argument of the current blocksize under
+// consideration, and returns the LDS usage
+//
+// Note: a returned block_size of zero indicates that the algorithm could not
+//       find a valid block size.  The caller is responsible for error handling.
+template <typename DriverType, typename LaunchBounds, typename ShmemFunctor>
+unsigned hip_get_max_blocksize(HIPInternal const *hip_instance,
+                               ShmemFunctor const &f) {
+  // get max blocksize limited by register usage
+  const unsigned tperb_reg = hip_get_max_blocksize<DriverType, LaunchBounds>();
+  return hip_internal_get_block_size<BlockType::Max, DriverType, LaunchBounds>(
+      hip_instance, f, tperb_reg);
 }
 
-template <typename FunctorType, typename LaunchBounds>
-int hip_get_opt_block_size(HIPInternal const *hip_instance,
-                           hipFuncAttributes const &attr, FunctorType const &f,
-                           size_t const vector_length, size_t const shmem_block,
-                           size_t const shmem_thread) {
-  return hip_internal_get_block_size<FunctorType, LaunchBounds>(
-      [](int) { return true; }, hip_instance, attr, f, vector_length,
-      shmem_block, shmem_thread);
+// Standardized blocksize deduction for teams-based parallel constructs with LDS
+// usage Returns the maximum possible blocksize, as determined by the heuristics
+// in hip_internal_get_block_size
+//
+// The ShmemTeamsFunctor takes two arguments: the hipFunctionAttributes and
+//  the current blocksize under consideration, and returns the LDS usage
+//
+// Note: a returned block_size of zero indicates that the algorithm could not
+//       find a valid block size.  The caller is responsible for error handling.
+template <typename DriverType, typename LaunchBounds,
+          typename ShmemTeamsFunctor>
+unsigned hip_get_max_team_blocksize(HIPInternal const *hip_instance,
+                                    ShmemTeamsFunctor const &f) {
+  hipFuncAttributes attr =
+      get_hip_func_attributes_impl<DriverType, LaunchBounds, BlockType::Max>();
+  // get max blocksize
+  using namespace std::placeholders;
+  const unsigned tperb_reg = hip_get_max_blocksize<DriverType, LaunchBounds>();
+  return hip_internal_get_block_size<BlockType::Max, DriverType, LaunchBounds>(
+      hip_instance, std::bind(f, attr, _1), tperb_reg);
 }
 
 }  // namespace Impl
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Error.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Error.hpp
index b3480bcad00c7ec6bc1a011a49fe7f9ae5eba345..a75e7a4a6c9351c0d39f7b2f7e8719a1a81c0adf 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Error.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Error.hpp
@@ -66,12 +66,30 @@ inline void hip_internal_safe_call(hipError_t e, const char* name,
   }
 }
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
+
+KOKKOS_DEPRECATED
+inline void hip_internal_safe_call_deprecated(hipError_t e, const char* name,
+                                              const char* file = nullptr,
+                                              const int line   = 0) {
+  hip_internal_safe_call(e, name, file, line);
+}
+
+#endif
+
 }  // namespace Impl
 }  // namespace Kokkos
 
-#define HIP_SAFE_CALL(call) \
+#define KOKKOS_IMPL_HIP_SAFE_CALL(call) \
   Kokkos::Impl::hip_internal_safe_call(call, #call, __FILE__, __LINE__)
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
+#define HIP_SAFE_CALL(call)                                              \
+  Kokkos::Impl::hip_internal_safe_call_deprecated(call, #call, __FILE__, \
+                                                  __LINE__)
+
+#endif
+
 namespace Kokkos {
 namespace Experimental {
 
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp
index 18ef10e22cd39b30118f78882a3ce747c19b9901..336ac8c6987c6538836f49792c41fd5520d0af8a 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Instance.cpp
@@ -77,7 +77,7 @@ class HIPInternalDevices {
 };
 
 HIPInternalDevices::HIPInternalDevices() {
-  HIP_SAFE_CALL(hipGetDeviceCount(&m_hipDevCount));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipGetDeviceCount(&m_hipDevCount));
 
   if (m_hipDevCount > MAXIMUM_DEVICE_COUNT) {
     Kokkos::abort(
@@ -85,7 +85,7 @@ HIPInternalDevices::HIPInternalDevices() {
         "have. Please report this to github.com/kokkos/kokkos.");
   }
   for (int i = 0; i < m_hipDevCount; ++i) {
-    HIP_SAFE_CALL(hipGetDeviceProperties(m_hipProp + i, i));
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipGetDeviceProperties(m_hipProp + i, i));
   }
 }
 
@@ -95,6 +95,9 @@ const HIPInternalDevices &HIPInternalDevices::singleton() {
 }
 }  // namespace
 
+unsigned long *Impl::HIPInternal::constantMemHostStaging = nullptr;
+hipEvent_t Impl::HIPInternal::constantMemReusable        = nullptr;
+
 namespace Impl {
 
 //----------------------------------------------------------------------------
@@ -154,6 +157,9 @@ int HIPInternal::verify_is_initialized(const char *const label) const {
   return 0 <= m_hipDev;
 }
 
+uint32_t HIPInternal::impl_get_instance_id() const noexcept {
+  return m_instance_id;
+}
 HIPInternal &HIPInternal::singleton() {
   static HIPInternal *self = nullptr;
   if (!self) {
@@ -163,12 +169,23 @@ HIPInternal &HIPInternal::singleton() {
 }
 
 void HIPInternal::fence() const {
-  HIP_SAFE_CALL(hipStreamSynchronize(m_stream));
-  // can reset our cycle id now as well
-  m_cycleId = 0;
+  fence("Kokkos::HIPInternal::fence: Unnamed Internal Fence");
+}
+void HIPInternal::fence(const std::string &name) const {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<
+      Kokkos::Experimental::HIP>(
+      name,
+      Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{
+          impl_get_instance_id()},
+      [&]() {
+        KOKKOS_IMPL_HIP_SAFE_CALL(hipStreamSynchronize(m_stream));
+        // can reset our cycle id now as well
+        m_cycleId = 0;
+      });
 }
 
-void HIPInternal::initialize(int hip_device_id, hipStream_t stream) {
+void HIPInternal::initialize(int hip_device_id, hipStream_t stream,
+                             bool manage_stream) {
   if (was_finalized)
     Kokkos::abort("Calling HIP::initialize after HIP::finalize is illegal\n");
 
@@ -197,9 +214,10 @@ void HIPInternal::initialize(int hip_device_id, hipStream_t stream) {
     m_hipDev     = hip_device_id;
     m_deviceProp = hipProp;
 
-    HIP_SAFE_CALL(hipSetDevice(m_hipDev));
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipSetDevice(m_hipDev));
 
     m_stream                    = stream;
+    m_manage_stream             = manage_stream;
     m_team_scratch_current_size = 0;
     m_team_scratch_ptr          = nullptr;
 
@@ -222,7 +240,7 @@ void HIPInternal::initialize(int hip_device_id, hipStream_t stream) {
     // theoretically, we can get 40 WF's / CU, but only can sustain 32
     // see
     // https://github.com/ROCm-Developer-Tools/HIP/blob/a0b5dfd625d99af7e288629747b40dd057183173/vdi/hip_platform.cpp#L742
-    m_maxBlocksPerSM = 32;
+    m_maxWavesPerCU = 32;
     // FIXME_HIP - Nick to implement this upstream
     //             Register count comes from Sec. 2.2. "Data Sharing" of the
     //             Vega 7nm ISA document (see the diagram)
@@ -232,7 +250,7 @@ void HIPInternal::initialize(int hip_device_id, hipStream_t stream) {
     m_regsPerSM        = 65536;
     m_shmemPerSM       = hipProp.maxSharedMemoryPerMultiProcessor;
     m_maxShmemPerBlock = hipProp.sharedMemPerBlock;
-    m_maxThreadsPerSM  = m_maxBlocksPerSM * HIPTraits::WarpSize;
+    m_maxThreadsPerSM  = m_maxWavesPerCU * HIPTraits::WarpSize;
     //----------------------------------
     // Multiblock reduction uses scratch flags for counters
     // and scratch space for partial reduction values.
@@ -265,8 +283,8 @@ void HIPInternal::initialize(int hip_device_id, hipStream_t stream) {
 
       m_scratchConcurrentBitset = reinterpret_cast<uint32_t *>(r->data());
 
-      HIP_SAFE_CALL(hipMemset(m_scratchConcurrentBitset, 0,
-                              sizeof(uint32_t) * buffer_bound));
+      KOKKOS_IMPL_HIP_SAFE_CALL(hipMemset(m_scratchConcurrentBitset, 0,
+                                          sizeof(uint32_t) * buffer_bound));
     }
     //----------------------------------
 
@@ -287,6 +305,15 @@ void HIPInternal::initialize(int hip_device_id, hipStream_t stream) {
 
   // Init the array for used for arbitrarily sized atomics
   if (m_stream == nullptr) ::Kokkos::Impl::initialize_host_hip_lock_arrays();
+
+  // Allocate a staging buffer for constant mem in pinned host memory
+  // and an event to avoid overwriting driver for previous kernel launches
+  if (m_stream == nullptr) {
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipHostMalloc((void **)&constantMemHostStaging,
+                                            HIPTraits::ConstantMemoryUsage));
+
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipEventCreate(&constantMemReusable));
+  }
 }
 
 //----------------------------------------------------------------------------
@@ -339,7 +366,7 @@ Kokkos::Experimental::HIP::size_type *HIPInternal::scratch_flags(
 
     m_scratchFlags = reinterpret_cast<size_type *>(r->data());
 
-    HIP_SAFE_CALL(
+    KOKKOS_IMPL_HIP_SAFE_CALL(
         hipMemset(m_scratchFlags, 0, m_scratchFlagsCount * sizeScratchGrain));
   }
 
@@ -365,7 +392,7 @@ void *HIPInternal::resize_team_scratch_space(std::int64_t bytes,
 //----------------------------------------------------------------------------
 
 void HIPInternal::finalize() {
-  this->fence();
+  this->fence("Kokkos::HIPInternal::finalize: fence on finalization");
   was_finalized = true;
   if (nullptr != m_scratchSpace || nullptr != m_scratchFlags) {
     using RecordHIP =
@@ -378,6 +405,9 @@ void HIPInternal::finalize() {
     if (m_team_scratch_current_size > 0)
       Kokkos::kokkos_free<Kokkos::Experimental::HIPSpace>(m_team_scratch_ptr);
 
+    if (m_manage_stream && m_stream != nullptr)
+      KOKKOS_IMPL_HIP_SAFE_CALL(hipStreamDestroy(m_stream));
+
     m_hipDev                    = -1;
     m_hipArch                   = -1;
     m_multiProcCount            = 0;
@@ -395,28 +425,36 @@ void HIPInternal::finalize() {
     m_team_scratch_ptr          = nullptr;
   }
   if (nullptr != d_driverWorkArray) {
-    HIP_SAFE_CALL(hipHostFree(d_driverWorkArray));
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipHostFree(d_driverWorkArray));
     d_driverWorkArray = nullptr;
   }
+
+  // only destroy these if we're finalizing the singleton
+  if (this == &singleton()) {
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipHostFree(constantMemHostStaging));
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipEventDestroy(constantMemReusable));
+  }
 }
 
 char *HIPInternal::get_next_driver(size_t driverTypeSize) const {
   std::lock_guard<std::mutex> const lock(m_mutexWorkArray);
   if (d_driverWorkArray == nullptr) {
-    HIP_SAFE_CALL(
+    KOKKOS_IMPL_HIP_SAFE_CALL(
         hipHostMalloc(&d_driverWorkArray,
                       m_maxDriverCycles * m_maxDriverTypeSize * sizeof(char),
                       hipHostMallocNonCoherent));
   }
   if (driverTypeSize > m_maxDriverTypeSize) {
     // fence handles the cycle id reset for us
-    fence();
-    HIP_SAFE_CALL(hipHostFree(d_driverWorkArray));
+    fence(
+        "Kokkos::HIPInternal::get_next_driver: fence before reallocating "
+        "resources");
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipHostFree(d_driverWorkArray));
     m_maxDriverTypeSize = driverTypeSize;
     if (m_maxDriverTypeSize % 128 != 0)
       m_maxDriverTypeSize =
           m_maxDriverTypeSize + 128 - m_maxDriverTypeSize % 128;
-    HIP_SAFE_CALL(
+    KOKKOS_IMPL_HIP_SAFE_CALL(
         hipHostMalloc(&d_driverWorkArray,
                       m_maxDriverCycles * m_maxDriverTypeSize * sizeof(char),
                       hipHostMallocNonCoherent));
@@ -424,7 +462,9 @@ char *HIPInternal::get_next_driver(size_t driverTypeSize) const {
     m_cycleId = (m_cycleId + 1) % m_maxDriverCycles;
     if (m_cycleId == 0) {
       // ensure any outstanding kernels are completed before we wrap around
-      fence();
+      fence(
+          "Kokkos::HIPInternal::get_next_driver: fence before reusing first "
+          "driver");
     }
   }
   return &d_driverWorkArray[m_maxDriverTypeSize * m_cycleId];
@@ -462,7 +502,14 @@ Kokkos::Experimental::HIP::size_type *hip_internal_scratch_flags(
 
 namespace Kokkos {
 namespace Impl {
-void hip_device_synchronize() { HIP_SAFE_CALL(hipDeviceSynchronize()); }
+void hip_device_synchronize(const std::string &name) {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<
+      Kokkos::Experimental::HIP>(
+      name,
+      Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+          GlobalDeviceSynchronization,
+      [&]() { KOKKOS_IMPL_HIP_SAFE_CALL(hipDeviceSynchronize()); });
+}
 
 void hip_internal_error_throw(hipError_t e, const char *name, const char *file,
                               const int line) {
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Instance.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Instance.hpp
index f4f88628e313a2d22d23a09e4ce25630d242a566..967c6fdd4be63e11b00c6b7f97b8d3d0b27bbcfc 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Instance.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Instance.hpp
@@ -48,6 +48,7 @@
 #define KOKKOS_HIP_INSTANCE_HPP
 
 #include <Kokkos_HIP_Space.hpp>
+#include <HIP/Kokkos_HIP_Error.hpp>
 
 #include <mutex>
 
@@ -59,10 +60,12 @@ struct HIPTraits {
   static int constexpr WarpSize       = 64;
   static int constexpr WarpIndexMask  = 0x003f; /* hexadecimal for 63 */
   static int constexpr WarpIndexShift = 6;      /* WarpSize == 1 << WarpShift*/
+  static int constexpr ConservativeThreadsPerBlock =
+      256;  // conservative fallback blocksize in case of spills
   static int constexpr MaxThreadsPerBlock =
-      1024;  // FIXME_HIP -- assumed constant for now
-
+      1024;  // the maximum we can fit in a block
   static int constexpr ConstantMemoryUsage        = 0x008000; /* 32k bytes */
+  static int constexpr KernelArgumentLimit        = 0x001000; /*  4k bytes */
   static int constexpr ConstantMemoryUseThreshold = 0x000200; /* 512 bytes */
 };
 
@@ -90,7 +93,7 @@ class HIPInternal {
   unsigned m_multiProcCount = 0;
   unsigned m_maxWarpCount   = 0;
   unsigned m_maxBlock       = 0;
-  unsigned m_maxBlocksPerSM = 0;
+  unsigned m_maxWavesPerCU  = 0;
   unsigned m_maxSharedWords = 0;
   int m_regsPerSM;
   int m_shmemPerSM       = 0;
@@ -108,6 +111,8 @@ class HIPInternal {
   mutable int m_cycleId = 0;
   // mutex to access d_driverWorkArray
   mutable std::mutex m_mutexWorkArray;
+  // mutex to access shared memory
+  mutable std::mutex m_mutexSharedMemory;
 
   // Scratch Spaces for Reductions
   size_type m_scratchSpaceCount = 0;
@@ -119,7 +124,10 @@ class HIPInternal {
 
   hipDeviceProp_t m_deviceProp;
 
-  hipStream_t m_stream = nullptr;
+  hipStream_t m_stream   = nullptr;
+  uint32_t m_instance_id = Kokkos::Tools::Experimental::Impl::idForInstance<
+      Kokkos::Experimental::HIP>(reinterpret_cast<uintptr_t>(this));
+  bool m_manage_stream = false;
 
   // Team Scratch Level 1 Space
   mutable int64_t m_team_scratch_current_size = 0;
@@ -128,18 +136,25 @@ class HIPInternal {
 
   bool was_finalized = false;
 
+  // FIXME_HIP: these want to be per-device, not per-stream...  use of 'static'
+  // here will break once there are multiple devices though
+  static unsigned long *constantMemHostStaging;
+  static hipEvent_t constantMemReusable;
+
   static HIPInternal &singleton();
 
   int verify_is_initialized(const char *const label) const;
 
   int is_initialized() const { return m_hipDev >= 0; }
 
-  void initialize(int hip_device_id, hipStream_t stream = nullptr);
+  void initialize(int hip_device_id, hipStream_t stream = nullptr,
+                  bool manage_stream = false);
   void finalize();
 
   void print_configuration(std::ostream &) const;
 
   void fence() const;
+  void fence(const std::string &) const;
 
   // returns the next driver type pointer in our work array
   char *get_next_driver(size_t driverTypeSize) const;
@@ -151,13 +166,52 @@ class HIPInternal {
   // Resizing of reduction related scratch spaces
   size_type *scratch_space(const size_type size);
   size_type *scratch_flags(const size_type size);
-
+  uint32_t impl_get_instance_id() const noexcept;
   // Resizing of team level 1 scratch
   void *resize_team_scratch_space(std::int64_t bytes,
                                   bool force_shrink = false);
 };
 
 }  // namespace Impl
+
+// Partitioning an Execution Space: expects space and integer arguments for
+// relative weight
+//   Customization point for backends
+//   Default behavior is to return the passed in instance
+
+namespace Impl {
+inline void create_HIP_instances(std::vector<HIP> &instances) {
+  for (int s = 0; s < int(instances.size()); s++) {
+    hipStream_t stream;
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipStreamCreate(&stream));
+    instances[s] = HIP(stream, true);
+  }
+}
+}  // namespace Impl
+
+template <class... Args>
+std::vector<HIP> partition_space(const HIP &, Args...) {
+#ifdef __cpp_fold_expressions
+  static_assert(
+      (... && std::is_arithmetic_v<Args>),
+      "Kokkos Error: partitioning arguments must be integers or floats");
+#endif
+
+  std::vector<HIP> instances(sizeof...(Args));
+  Impl::create_HIP_instances(instances);
+  return instances;
+}
+
+template <class T>
+std::vector<HIP> partition_space(const HIP &, std::vector<T> &weights) {
+  static_assert(
+      std::is_arithmetic<T>::value,
+      "Kokkos Error: partitioning arguments must be integers or floats");
+
+  std::vector<HIP> instances(weights.size());
+  Impl::create_HIP_instances(instances);
+  return instances;
+}
 }  // namespace Experimental
 }  // namespace Kokkos
 
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_KernelLaunch.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_KernelLaunch.hpp
index f774423b378b0753a98c9e4df512b599910028dd..f209edf7c04ecc9b0001c4527e1bcebc0f24b256 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_KernelLaunch.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_KernelLaunch.hpp
@@ -52,6 +52,7 @@
 #include <HIP/Kokkos_HIP_Error.hpp>
 #include <HIP/Kokkos_HIP_Instance.hpp>
 #include <Kokkos_HIP_Space.hpp>
+#include <HIP/Kokkos_HIP_Locks.hpp>
 
 // Must use global variable on the device with HIP-Clang
 #ifdef __HIP__
@@ -64,7 +65,7 @@ namespace Kokkos {
 namespace Experimental {
 template <typename T>
 inline __device__ T *kokkos_impl_hip_shared_memory() {
-  HIP_DYNAMIC_SHARED(HIPSpace::size_type, sh);
+  extern __shared__ Kokkos::Experimental::HIPSpace::size_type sh[];
   return (T *)sh;
 }
 }  // namespace Experimental
@@ -74,10 +75,12 @@ namespace Kokkos {
 namespace Experimental {
 namespace Impl {
 
+// The hip_parallel_launch_*_memory code is identical to the cuda code
 template <typename DriverType>
 __global__ static void hip_parallel_launch_constant_memory() {
   const DriverType &driver = *(reinterpret_cast<const DriverType *>(
       kokkos_impl_hip_constant_memory_buffer));
+
   driver();
 }
 
@@ -87,12 +90,13 @@ __global__ __launch_bounds__(
   const DriverType &driver = *(reinterpret_cast<const DriverType *>(
       kokkos_impl_hip_constant_memory_buffer));
 
-  driver->operator()();
+  driver();
 }
 
 template <class DriverType>
 __global__ static void hip_parallel_launch_local_memory(
     const DriverType *driver) {
+  // FIXME_HIP driver() pass by copy
   driver->operator()();
 }
 
@@ -101,6 +105,21 @@ __global__ __launch_bounds__(
     maxTperB,
     minBperSM) static void hip_parallel_launch_local_memory(const DriverType
                                                                 *driver) {
+  // FIXME_HIP driver() pass by copy
+  driver->operator()();
+}
+
+template <typename DriverType>
+__global__ static void hip_parallel_launch_global_memory(
+    const DriverType *driver) {
+  driver->operator()();
+}
+
+template <typename DriverType, unsigned int maxTperB, unsigned int minBperSM>
+__global__ __launch_bounds__(
+    maxTperB,
+    minBperSM) static void hip_parallel_launch_global_memory(const DriverType
+                                                                 *driver) {
   driver->operator()();
 }
 
@@ -127,33 +146,238 @@ struct HIPDispatchProperties {
   HIPLaunchMechanism launch_mechanism = l;
 };
 
+// Use local memory up to ConstantMemoryUseThreshold
+// Use global memory above ConstantMemoryUsage
+// In between use ConstantMemory
+// The following code is identical to the cuda code
+template <typename DriverType>
+struct DeduceHIPLaunchMechanism {
+  static constexpr Kokkos::Experimental::WorkItemProperty::HintLightWeight_t
+      light_weight = Kokkos::Experimental::WorkItemProperty::HintLightWeight;
+  static constexpr Kokkos::Experimental::WorkItemProperty::HintHeavyWeight_t
+      heavy_weight = Kokkos::Experimental::WorkItemProperty::HintHeavyWeight;
+  static constexpr typename DriverType::Policy::work_item_property property =
+      typename DriverType::Policy::work_item_property();
+
+  static constexpr HIPLaunchMechanism valid_launch_mechanism =
+      // BuildValidMask
+      (sizeof(DriverType) < HIPTraits::KernelArgumentLimit
+           ? HIPLaunchMechanism::LocalMemory
+           : HIPLaunchMechanism::Default) |
+      (sizeof(DriverType) < HIPTraits::ConstantMemoryUsage
+           ? HIPLaunchMechanism::ConstantMemory
+           : HIPLaunchMechanism::Default) |
+      HIPLaunchMechanism::GlobalMemory;
+
+  static constexpr HIPLaunchMechanism requested_launch_mechanism =
+      (((property & light_weight) == light_weight)
+           ? HIPLaunchMechanism::LocalMemory
+           : HIPLaunchMechanism::ConstantMemory) |
+      HIPLaunchMechanism::GlobalMemory;
+
+  static constexpr HIPLaunchMechanism default_launch_mechanism =
+      // BuildValidMask
+      (sizeof(DriverType) < HIPTraits::ConstantMemoryUseThreshold)
+          ? HIPLaunchMechanism::LocalMemory
+          : ((sizeof(DriverType) < HIPTraits::ConstantMemoryUsage)
+                 ? HIPLaunchMechanism::ConstantMemory
+                 : HIPLaunchMechanism::GlobalMemory);
+
+  //              None                LightWeight    HeavyWeight
+  // F<UseT       LCG  LCG L  L       LCG  LG L  L   LCG  CG L  C
+  // UseT<F<KAL   LCG  LCG C  C       LCG  LG C  L   LCG  CG C  C
+  // Kal<F<CMU     CG  LCG C  C        CG  LG C  G    CG  CG C  C
+  // CMU<F          G  LCG G  G         G  LG G  G     G  CG G  G
+  static constexpr HIPLaunchMechanism launch_mechanism =
+      ((property & light_weight) == light_weight)
+          ? (sizeof(DriverType) < HIPTraits::KernelArgumentLimit
+                 ? HIPLaunchMechanism::LocalMemory
+                 : HIPLaunchMechanism::GlobalMemory)
+          : (((property & heavy_weight) == heavy_weight)
+                 ? (sizeof(DriverType) < HIPTraits::ConstantMemoryUsage
+                        ? HIPLaunchMechanism::ConstantMemory
+                        : HIPLaunchMechanism::GlobalMemory)
+                 : (default_launch_mechanism));
+};
+
+template <typename DriverType, typename LaunchBounds,
+          HIPLaunchMechanism LaunchMechanism>
+struct HIPParallelLaunchKernelFuncData {
+  static unsigned int get_scratch_size(
+      hipFuncAttributes const &hip_func_attributes) {
+    return hip_func_attributes.localSizeBytes;
+  }
+
+  static hipFuncAttributes get_hip_func_attributes(void const *kernel_func) {
+    static hipFuncAttributes attr = [=]() {
+      hipFuncAttributes attr;
+      KOKKOS_IMPL_HIP_SAFE_CALL(hipFuncGetAttributes(&attr, kernel_func));
+      return attr;
+    }();
+    return attr;
+  }
+};
+
+//---------------------------------------------------------------//
+// HIPParallelLaunchKernelFunc structure and its specializations //
+//---------------------------------------------------------------//
 template <typename DriverType, typename LaunchBounds,
           HIPLaunchMechanism LaunchMechanism>
 struct HIPParallelLaunchKernelFunc;
 
+// HIPLaunchMechanism::LocalMemory specializations
 template <typename DriverType, unsigned int MaxThreadsPerBlock,
           unsigned int MinBlocksPerSM>
 struct HIPParallelLaunchKernelFunc<
     DriverType, Kokkos::LaunchBounds<MaxThreadsPerBlock, MinBlocksPerSM>,
     HIPLaunchMechanism::LocalMemory> {
+  using funcdata_t = HIPParallelLaunchKernelFuncData<
+      DriverType, Kokkos::LaunchBounds<MaxThreadsPerBlock, MinBlocksPerSM>,
+      HIPLaunchMechanism::LocalMemory>;
   static auto get_kernel_func() {
     return hip_parallel_launch_local_memory<DriverType, MaxThreadsPerBlock,
                                             MinBlocksPerSM>;
   }
+
+  static constexpr auto default_launchbounds() { return false; }
+
+  static auto get_scratch_size() {
+    return funcdata_t::get_scratch_size(get_hip_func_attributes());
+  }
+
+  static hipFuncAttributes get_hip_func_attributes() {
+    return funcdata_t::get_hip_func_attributes(
+        reinterpret_cast<void const *>(get_kernel_func()));
+  }
 };
 
 template <typename DriverType>
 struct HIPParallelLaunchKernelFunc<DriverType, Kokkos::LaunchBounds<0, 0>,
                                    HIPLaunchMechanism::LocalMemory> {
+  using funcdata_t =
+      HIPParallelLaunchKernelFuncData<DriverType, Kokkos::LaunchBounds<0, 0>,
+                                      HIPLaunchMechanism::LocalMemory>;
+  static auto get_kernel_func() {
+    return HIPParallelLaunchKernelFunc<
+        DriverType, Kokkos::LaunchBounds<HIPTraits::MaxThreadsPerBlock, 1>,
+        HIPLaunchMechanism::LocalMemory>::get_kernel_func();
+  }
+
+  static constexpr auto default_launchbounds() { return true; }
+
+  static auto get_scratch_size() {
+    return funcdata_t::get_scratch_size(get_hip_func_attributes());
+  }
+
+  static hipFuncAttributes get_hip_func_attributes() {
+    return funcdata_t::get_hip_func_attributes(
+        reinterpret_cast<void const *>(get_kernel_func()));
+  }
+};
+
+// HIPLaunchMechanism::GlobalMemory specializations
+template <typename DriverType, unsigned int MaxThreadsPerBlock,
+          unsigned int MinBlocksPerSM>
+struct HIPParallelLaunchKernelFunc<
+    DriverType, Kokkos::LaunchBounds<MaxThreadsPerBlock, MinBlocksPerSM>,
+    HIPLaunchMechanism::GlobalMemory> {
+  using funcdata_t = HIPParallelLaunchKernelFuncData<
+      DriverType, Kokkos::LaunchBounds<MaxThreadsPerBlock, MinBlocksPerSM>,
+      HIPLaunchMechanism::GlobalMemory>;
+  static auto get_kernel_func() {
+    return hip_parallel_launch_global_memory<DriverType, MaxThreadsPerBlock,
+                                             MinBlocksPerSM>;
+  }
+
+  static constexpr auto default_launchbounds() { return false; }
+
+  static auto get_scratch_size() {
+    return funcdata_t::get_scratch_size(get_hip_func_attributes());
+  }
+
+  static hipFuncAttributes get_hip_func_attributes() {
+    return funcdata_t::get_hip_func_attributes(
+        reinterpret_cast<void const *>(get_kernel_func()));
+  }
+};
+
+template <typename DriverType>
+struct HIPParallelLaunchKernelFunc<DriverType, Kokkos::LaunchBounds<0, 0>,
+                                   HIPLaunchMechanism::GlobalMemory> {
+  using funcdata_t =
+      HIPParallelLaunchKernelFuncData<DriverType, Kokkos::LaunchBounds<0, 0>,
+                                      HIPLaunchMechanism::GlobalMemory>;
+  static auto get_kernel_func() {
+    return hip_parallel_launch_global_memory<DriverType>;
+  }
+
+  static constexpr auto default_launchbounds() { return true; }
+
+  static auto get_scratch_size() {
+    return funcdata_t::get_scratch_size(get_hip_func_attributes());
+  }
+
+  static hipFuncAttributes get_hip_func_attributes() {
+    return funcdata_t::get_hip_func_attributes(
+        reinterpret_cast<void const *>(get_kernel_func()));
+  }
+};
+
+// HIPLaunchMechanism::ConstantMemory specializations
+template <typename DriverType, unsigned int MaxThreadsPerBlock,
+          unsigned int MinBlocksPerSM>
+struct HIPParallelLaunchKernelFunc<
+    DriverType, Kokkos::LaunchBounds<MaxThreadsPerBlock, MinBlocksPerSM>,
+    HIPLaunchMechanism::ConstantMemory> {
+  using funcdata_t = HIPParallelLaunchKernelFuncData<
+      DriverType, Kokkos::LaunchBounds<MaxThreadsPerBlock, MinBlocksPerSM>,
+      HIPLaunchMechanism::ConstantMemory>;
   static auto get_kernel_func() {
-    return hip_parallel_launch_local_memory<DriverType, 1024, 1>;
+    return hip_parallel_launch_constant_memory<DriverType, MaxThreadsPerBlock,
+                                               MinBlocksPerSM>;
+  }
+
+  static constexpr auto default_launchbounds() { return false; }
+
+  static auto get_scratch_size() {
+    return funcdata_t::get_scratch_size(get_hip_func_attributes());
+  }
+
+  static hipFuncAttributes get_hip_func_attributes() {
+    return funcdata_t::get_hip_func_attributes(
+        reinterpret_cast<void const *>(get_kernel_func()));
   }
 };
 
+template <typename DriverType>
+struct HIPParallelLaunchKernelFunc<DriverType, Kokkos::LaunchBounds<0, 0>,
+                                   HIPLaunchMechanism::ConstantMemory> {
+  using funcdata_t =
+      HIPParallelLaunchKernelFuncData<DriverType, Kokkos::LaunchBounds<0, 0>,
+                                      HIPLaunchMechanism::ConstantMemory>;
+  static auto get_kernel_func() {
+    return hip_parallel_launch_constant_memory<DriverType>;
+  }
+  static constexpr auto default_launchbounds() { return true; }
+
+  static auto get_scratch_size() {
+    return funcdata_t::get_scratch_size(get_hip_func_attributes());
+  }
+
+  static hipFuncAttributes get_hip_func_attributes() {
+    return funcdata_t::get_hip_func_attributes(
+        reinterpret_cast<void const *>(get_kernel_func()));
+  }
+};
+
+//------------------------------------------------------------------//
+// HIPParallelLaunchKernelInvoker structure and its specializations //
+//------------------------------------------------------------------//
 template <typename DriverType, typename LaunchBounds,
           HIPLaunchMechanism LaunchMechanism>
 struct HIPParallelLaunchKernelInvoker;
 
+// HIPLaunchMechanism::LocalMemory specialization
 template <typename DriverType, typename LaunchBounds>
 struct HIPParallelLaunchKernelInvoker<DriverType, LaunchBounds,
                                       HIPLaunchMechanism::LocalMemory>
@@ -170,21 +394,83 @@ struct HIPParallelLaunchKernelInvoker<DriverType, LaunchBounds,
   }
 };
 
+// HIPLaunchMechanism::GlobalMemory specialization
+template <typename DriverType, typename LaunchBounds>
+struct HIPParallelLaunchKernelInvoker<DriverType, LaunchBounds,
+                                      HIPLaunchMechanism::GlobalMemory>
+    : HIPParallelLaunchKernelFunc<DriverType, LaunchBounds,
+                                  HIPLaunchMechanism::GlobalMemory> {
+  using base_t = HIPParallelLaunchKernelFunc<DriverType, LaunchBounds,
+                                             HIPLaunchMechanism::GlobalMemory>;
+
+  // FIXME_HIP the code is different than cuda because driver cannot be passed
+  // by copy
+  static void invoke_kernel(DriverType const *driver, dim3 const &grid,
+                            dim3 const &block, int shmem,
+                            HIPInternal const *hip_instance) {
+    (base_t::get_kernel_func())<<<grid, block, shmem, hip_instance->m_stream>>>(
+        driver);
+  }
+};
+
+// HIPLaunchMechanism::ConstantMemory specializations
+template <typename DriverType, typename LaunchBounds>
+struct HIPParallelLaunchKernelInvoker<DriverType, LaunchBounds,
+                                      HIPLaunchMechanism::ConstantMemory>
+    : HIPParallelLaunchKernelFunc<DriverType, LaunchBounds,
+                                  HIPLaunchMechanism::ConstantMemory> {
+  using base_t =
+      HIPParallelLaunchKernelFunc<DriverType, LaunchBounds,
+                                  HIPLaunchMechanism::ConstantMemory>;
+  static_assert(sizeof(DriverType) < HIPTraits::ConstantMemoryUsage,
+                "Kokkos Error: Requested HIPLaunchConstantMemory with a "
+                "Functor larger than 32kB.");
+
+  static void invoke_kernel(DriverType const *driver, dim3 const &grid,
+                            dim3 const &block, int shmem,
+                            HIPInternal const *hip_instance) {
+    // Wait until the previous kernel that uses the constant buffer is done
+    KOKKOS_IMPL_HIP_SAFE_CALL(
+        hipEventSynchronize(hip_instance->constantMemReusable));
+
+    // Copy functor (synchronously) to staging buffer in pinned host memory
+    unsigned long *staging = hip_instance->constantMemHostStaging;
+    std::memcpy((void *)staging, (void *)driver, sizeof(DriverType));
+
+    // Copy functor asynchronously from there to constant memory on the device
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipMemcpyToSymbolAsync(
+        HIP_SYMBOL(kokkos_impl_hip_constant_memory_buffer), staging,
+        sizeof(DriverType), 0, hipMemcpyHostToDevice, hip_instance->m_stream));
+
+    // Invoke the driver function on the device
+    (base_t::
+         get_kernel_func())<<<grid, block, shmem, hip_instance->m_stream>>>();
+
+    // Record an event that says when the constant buffer can be reused
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipEventRecord(hip_instance->constantMemReusable,
+                                             hip_instance->m_stream));
+  }
+};
+
+//-----------------------------//
+// HIPParallelLaunch structure //
+//-----------------------------//
 template <typename DriverType, typename LaunchBounds = Kokkos::LaunchBounds<>,
-          HIPLaunchMechanism LaunchMechanism = HIPLaunchMechanism::LocalMemory>
+          HIPLaunchMechanism LaunchMechanism =
+              DeduceHIPLaunchMechanism<DriverType>::launch_mechanism>
 struct HIPParallelLaunch;
 
 template <typename DriverType, unsigned int MaxThreadsPerBlock,
-          unsigned int MinBlocksPerSM>
+          unsigned int MinBlocksPerSM, HIPLaunchMechanism LaunchMechanism>
 struct HIPParallelLaunch<
     DriverType, Kokkos::LaunchBounds<MaxThreadsPerBlock, MinBlocksPerSM>,
-    HIPLaunchMechanism::LocalMemory>
+    LaunchMechanism>
     : HIPParallelLaunchKernelInvoker<
           DriverType, Kokkos::LaunchBounds<MaxThreadsPerBlock, MinBlocksPerSM>,
-          HIPLaunchMechanism::LocalMemory> {
+          LaunchMechanism> {
   using base_t = HIPParallelLaunchKernelInvoker<
       DriverType, Kokkos::LaunchBounds<MaxThreadsPerBlock, MinBlocksPerSM>,
-      HIPLaunchMechanism::LocalMemory>;
+      LaunchMechanism>;
 
   HIPParallelLaunch(const DriverType &driver, const dim3 &grid,
                     const dim3 &block, const int shmem,
@@ -205,22 +491,48 @@ struct HIPParallelLaunch<
       base_t::invoke_kernel(d_driver, grid, block, shmem, hip_instance);
 
 #if defined(KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK)
-      HIP_SAFE_CALL(hipGetLastError());
-      hip_instance->fence();
+      KOKKOS_IMPL_HIP_SAFE_CALL(hipGetLastError());
+      hip_instance->fence(
+          "Kokkos::Experimental::Impl::HIParallelLaunch: Debug Only Check for "
+          "Execution Error");
 #endif
     }
   }
+};
 
-  static hipFuncAttributes get_hip_func_attributes() {
-    static hipFuncAttributes attr = []() {
-      hipFuncAttributes attr;
-      HIP_SAFE_CALL(hipFuncGetAttributes(
-          &attr, reinterpret_cast<void const *>(base_t::get_kernel_func())));
-      return attr;
-    }();
-    return attr;
+// convenience method to launch the correct kernel given the launch bounds et
+// al.
+template <typename DriverType, typename LaunchBounds = Kokkos::LaunchBounds<>,
+          HIPLaunchMechanism LaunchMechanism =
+              DeduceHIPLaunchMechanism<DriverType>::launch_mechanism>
+void hip_parallel_launch(const DriverType &driver, const dim3 &grid,
+                         const dim3 &block, const int shmem,
+                         const HIPInternal *hip_instance,
+                         const bool prefer_shmem) {
+  // FIXME_HIP - could be if constexpr for c++17
+  if (!HIPParallelLaunch<DriverType, LaunchBounds,
+                         LaunchMechanism>::default_launchbounds()) {
+    // for user defined, we *always* honor the request
+    HIPParallelLaunch<DriverType, LaunchBounds, LaunchMechanism>(
+        driver, grid, block, shmem, hip_instance, prefer_shmem);
+  } else {
+    // we can do what we like
+    const unsigned flat_block_size = block.x * block.y * block.z;
+    if (flat_block_size <= HIPTraits::ConservativeThreadsPerBlock) {
+      // we have to use the large blocksize
+      HIPParallelLaunch<
+          DriverType,
+          Kokkos::LaunchBounds<HIPTraits::ConservativeThreadsPerBlock, 1>,
+          LaunchMechanism>(driver, grid, block, shmem, hip_instance,
+                           prefer_shmem);
+    } else {
+      HIPParallelLaunch<DriverType,
+                        Kokkos::LaunchBounds<HIPTraits::MaxThreadsPerBlock, 1>,
+                        LaunchMechanism>(driver, grid, block, shmem,
+                                         hip_instance, prefer_shmem);
+    }
   }
-};
+}
 }  // namespace Impl
 }  // namespace Experimental
 }  // namespace Kokkos
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Locks.cpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Locks.cpp
index 4f5271b6f644605e24ab277a7b08b25ba8c2ea84..c4292d35eca793bc58d76ba20db4358f85810996 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Locks.cpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Locks.cpp
@@ -84,11 +84,17 @@ namespace Impl {
 HIPLockArrays g_host_hip_lock_arrays = {nullptr, nullptr, 0};
 
 void initialize_host_hip_lock_arrays() {
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+  desul::Impl::init_lock_arrays();
+
+  DESUL_ENSURE_HIP_LOCK_ARRAYS_ON_DEVICE();
+#endif
+
   if (g_host_hip_lock_arrays.atomic != nullptr) return;
-  HIP_SAFE_CALL(hipMalloc(
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipMalloc(
       &g_host_hip_lock_arrays.atomic,
       sizeof(std::int32_t) * (KOKKOS_IMPL_HIP_SPACE_ATOMIC_MASK + 1)));
-  HIP_SAFE_CALL(hipMalloc(
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipMalloc(
       &g_host_hip_lock_arrays.scratch,
       sizeof(std::int32_t) * (::Kokkos::Experimental::HIP::concurrency())));
 
@@ -103,10 +109,14 @@ void initialize_host_hip_lock_arrays() {
 }
 
 void finalize_host_hip_lock_arrays() {
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+  desul::Impl::finalize_lock_arrays();
+#endif
+
   if (g_host_hip_lock_arrays.atomic == nullptr) return;
-  HIP_SAFE_CALL(hipFree(g_host_hip_lock_arrays.atomic));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipFree(g_host_hip_lock_arrays.atomic));
   g_host_hip_lock_arrays.atomic = nullptr;
-  HIP_SAFE_CALL(hipFree(g_host_hip_lock_arrays.scratch));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipFree(g_host_hip_lock_arrays.scratch));
   g_host_hip_lock_arrays.scratch = nullptr;
   g_host_hip_lock_arrays.n       = 0;
 #ifdef KOKKOS_ENABLE_HIP_RELOCATABLE_DEVICE_CODE
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Locks.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Locks.hpp
index f34f85f43b0bb2ac2b07d4149957f5027991395f..71b104c2e4b65aff7ab3b3688c0901d000e8d9d8 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Locks.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Locks.hpp
@@ -51,6 +51,10 @@
 
 #include <HIP/Kokkos_HIP_Error.hpp>
 
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+#include <desul/atomics/Lock_Array_HIP.hpp>
+#endif
+
 namespace Kokkos {
 namespace Impl {
 
@@ -147,7 +151,7 @@ inline int eliminate_warning_for_lock_array() { return lock_array_copied; }
 #define KOKKOS_COPY_HIP_LOCK_ARRAYS_TO_DEVICE()                 \
   {                                                             \
     if (::Kokkos::Impl::lock_array_copied == 0) {               \
-      HIP_SAFE_CALL(hipMemcpyToSymbol(                          \
+      KOKKOS_IMPL_HIP_SAFE_CALL(hipMemcpyToSymbol(              \
           HIP_SYMBOL(::Kokkos::Impl::g_device_hip_lock_arrays), \
           &::Kokkos::Impl::g_host_hip_lock_arrays,              \
           sizeof(::Kokkos::Impl::HIPLockArrays)));              \
@@ -155,6 +159,8 @@ inline int eliminate_warning_for_lock_array() { return lock_array_copied; }
     ::Kokkos::Impl::lock_array_copied = 1;                      \
   }
 
+#ifndef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+
 #ifdef KOKKOS_ENABLE_HIP_RELOCATABLE_DEVICE_CODE
 #define KOKKOS_ENSURE_HIP_LOCK_ARRAYS_ON_DEVICE()
 #else
@@ -162,6 +168,19 @@ inline int eliminate_warning_for_lock_array() { return lock_array_copied; }
   KOKKOS_COPY_HIP_LOCK_ARRAYS_TO_DEVICE()
 #endif
 
+#else
+
+#ifdef KOKKOS_ENABLE_HIP_RELOCATABLE_DEVICE_CODE
+#define KOKKOS_ENSURE_HIP_LOCK_ARRAYS_ON_DEVICE()
+#else
+// Still Need COPY_CUDA_LOCK_ARRAYS for team scratch etc.
+#define KOKKOS_ENSURE_HIP_LOCK_ARRAYS_ON_DEVICE() \
+  KOKKOS_COPY_HIP_LOCK_ARRAYS_TO_DEVICE()         \
+  DESUL_ENSURE_HIP_LOCK_ARRAYS_ON_DEVICE()
+#endif
+
+#endif /* defined( KOKKOS_ENABLE_IMPL_DESUL_ATOMICS ) */
+
 #endif /* defined( __HIPCC__ ) */
 
 #endif /* #ifndef KOKKOS_HIP_LOCKS_HPP */
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_MDRangePolicy.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_MDRangePolicy.hpp
index ce1aff9586d25911104d17d53860409f3e73b10b..acb538e1cb3970bab9bafc2f44d3568b34c6c31f 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_MDRangePolicy.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_MDRangePolicy.hpp
@@ -28,7 +28,8 @@ inline TileSizeProperties get_tile_size_properties<Kokkos::Experimental::HIP>(
       space.impl_internal_space_instance()->m_maxThreadsPerSM;
   properties.default_largest_tile_size = 16;
   properties.default_tile_size         = 4;
-  properties.max_total_tile_size       = 1024;
+  properties.max_total_tile_size =
+      Kokkos::Experimental::Impl::HIPTraits::MaxThreadsPerBlock;
   return properties;
 }
 
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_MDRange.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_MDRange.hpp
index 35e7d6fb853ae9e4f245e0fe0c2a71f4f2d4d6c2..eae323dd913d7f20383e3afcf5f1264d013b7be1 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_MDRange.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_MDRange.hpp
@@ -81,6 +81,8 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
   }
 
   inline void execute() const {
+    using ClosureType =
+        ParallelFor<FunctorType, Policy, Kokkos::Experimental::HIP>;
     if (m_policy.m_num_tiles == 0) return;
     array_index_type const maxblocks = static_cast<array_index_type>(
         m_policy.space().impl_internal_space_instance()->m_maxBlock);
@@ -94,7 +96,8 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
                        block.y,
                    maxblocks),
           1);
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelFor, LaunchBounds>(
+      Kokkos::Experimental::Impl::hip_parallel_launch<ClosureType,
+                                                      LaunchBounds>(
           *this, grid, block, 0,
           m_policy.space().impl_internal_space_instance(), false);
     } else if (Policy::rank == 3) {
@@ -110,7 +113,8 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
           std::min((m_policy.m_upper[2] - m_policy.m_lower[2] + block.z - 1) /
                        block.z,
                    maxblocks));
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelFor, LaunchBounds>(
+      Kokkos::Experimental::Impl::hip_parallel_launch<ClosureType,
+                                                      LaunchBounds>(
           *this, grid, block, 0,
           m_policy.space().impl_internal_space_instance(), false);
     } else if (Policy::rank == 4) {
@@ -128,7 +132,8 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
           std::min((m_policy.m_upper[3] - m_policy.m_lower[3] + block.z - 1) /
                        block.z,
                    maxblocks));
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelFor, LaunchBounds>(
+      Kokkos::Experimental::Impl::hip_parallel_launch<ClosureType,
+                                                      LaunchBounds>(
           *this, grid, block, 0,
           m_policy.space().impl_internal_space_instance(), false);
     } else if (Policy::rank == 5) {
@@ -147,7 +152,8 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
           std::min((m_policy.m_upper[4] - m_policy.m_lower[4] + block.z - 1) /
                        block.z,
                    maxblocks));
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelFor, LaunchBounds>(
+      Kokkos::Experimental::Impl::hip_parallel_launch<ClosureType,
+                                                      LaunchBounds>(
           *this, grid, block, 0,
           m_policy.space().impl_internal_space_instance(), false);
     } else if (Policy::rank == 6) {
@@ -165,7 +171,8 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
                       std::min(static_cast<index_type>(m_policy.m_tile_end[4] *
                                                        m_policy.m_tile_end[5]),
                                static_cast<index_type>(maxblocks)));
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelFor, LaunchBounds>(
+      Kokkos::Experimental::Impl::hip_parallel_launch<ClosureType,
+                                                      LaunchBounds>(
           *this, grid, block, 0,
           m_policy.space().impl_internal_space_instance(), false);
     } else {
@@ -178,22 +185,18 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
       : m_functor(arg_functor), m_policy(arg_policy) {}
 
   template <typename Policy, typename Functor>
-  static int max_tile_size_product(const Policy& pol, const Functor&) {
+  static int max_tile_size_product(const Policy&, const Functor&) {
     using closure_type =
         ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
                     Kokkos::Experimental::HIP>;
-    hipFuncAttributes attr = Kokkos::Experimental::Impl::HIPParallelLaunch<
-        closure_type, LaunchBounds>::get_hip_func_attributes();
-    auto const& prop = pol.space().hip_device_prop();
-    // Limits due to registers/SM, MDRange doesn't have
-    // shared memory constraints
-    int const regs_per_sm        = prop.regsPerMultiprocessor;
-    int const regs_per_thread    = attr.numRegs;
-    int const max_threads_per_sm = regs_per_sm / regs_per_thread;
-    return std::min(
-        max_threads_per_sm,
-        static_cast<int>(
-            Kokkos::Experimental::Impl::HIPTraits::MaxThreadsPerBlock));
+    unsigned block_size =
+        Kokkos::Experimental::Impl::hip_get_max_blocksize<closure_type,
+                                                          LaunchBounds>();
+    if (block_size == 0)
+      Kokkos::Impl::throw_runtime_exception(
+          std::string("Kokkos::Impl::ParallelFor< HIP > could not find a valid "
+                      "tile size."));
+    return block_size;
   }
 };
 
@@ -242,6 +245,9 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
   const bool m_result_ptr_device_accessible;
   size_type* m_scratch_space;
   size_type* m_scratch_flags;
+  // Only let one Parallel/Scan modify the shared memory. The
+  // constructor acquires the mutex which is released in the destructor.
+  std::unique_lock<std::mutex> m_shared_memory_lock;
 
   using DeviceIteratePattern = typename Kokkos::Impl::Reduce::DeviceIterateTile<
       Policy::rank, Policy, FunctorType, WorkTag, reference_type>;
@@ -307,32 +313,30 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
   // Determine block size constrained by shared memory:
   // This is copy/paste from Kokkos_HIP_Parallel_Range
   inline unsigned local_block_size(const FunctorType& f) {
-    unsigned int n =
-        ::Kokkos::Experimental::Impl::HIPTraits::MaxThreadsPerBlock;
-    int shmem_size = ::Kokkos::Impl::hip_single_inter_block_reduce_scan_shmem<
-        false, FunctorType, WorkTag>(f, n);
-    using closure_type = Impl::ParallelReduce<FunctorType, Policy, ReducerType>;
-    hipFuncAttributes attr = ::Kokkos::Experimental::Impl::HIPParallelLaunch<
-        closure_type, LaunchBounds>::get_hip_func_attributes();
-    while (
-        (n &&
-         (m_policy.space().impl_internal_space_instance()->m_maxShmemPerBlock <
-          shmem_size)) ||
-        (n >
-         static_cast<unsigned>(
-             ::Kokkos::Experimental::Impl::hip_get_max_block_size<FunctorType,
-                                                                  LaunchBounds>(
-                 m_policy.space().impl_internal_space_instance(), attr, f, 1,
-                 shmem_size, 0)))) {
-      n >>= 1;
-      shmem_size = ::Kokkos::Impl::hip_single_inter_block_reduce_scan_shmem<
-          false, FunctorType, WorkTag>(f, n);
+    const auto& instance = m_policy.space().impl_internal_space_instance();
+    auto shmem_functor   = [&f](unsigned n) {
+      return hip_single_inter_block_reduce_scan_shmem<false, FunctorType,
+                                                      WorkTag>(f, n);
+    };
+    using closure_type = ParallelReduce<FunctorType, Policy, ReducerType,
+                                        Kokkos::Experimental::HIP>;
+
+    unsigned block_size =
+        Kokkos::Experimental::Impl::hip_get_preferred_blocksize<closure_type,
+                                                                LaunchBounds>(
+            instance, shmem_functor);
+    if (block_size == 0) {
+      Kokkos::Impl::throw_runtime_exception(
+          std::string("Kokkos::Impl::ParallelReduce< HIP > could not find a "
+                      "valid tile size."));
     }
-    return n;
+    return block_size;
   }
 
   inline void execute() {
-    const int nwork = m_policy.m_num_tiles;
+    using ClosureType = ParallelReduce<FunctorType, Policy, ReducerType,
+                                       Kokkos::Experimental::HIP>;
+    const int nwork   = m_policy.m_num_tiles;
     if (nwork) {
       int block_size = m_policy.m_prod_tile_dims;
       // CONSTRAINT: Algorithm requires block_size >= product of tile dimensions
@@ -366,14 +370,16 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
           ::Kokkos::Impl::hip_single_inter_block_reduce_scan_shmem<
               false, FunctorType, WorkTag>(m_functor, block.y);
 
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelReduce,
-                                                    LaunchBounds>(
+      Kokkos::Experimental::Impl::hip_parallel_launch<ClosureType,
+                                                      LaunchBounds>(
           *this, grid, block, shmem,
           m_policy.space().impl_internal_space_instance(),
           false);  // copy to device and execute
 
       if (!m_result_ptr_device_accessible) {
-        m_policy.space().fence();
+        m_policy.space().fence(
+            "Kokkos::Impl::ParallelReduce<MDRangePolicy,HIP>: fence because "
+            "reduction can't access result storage location");
 
         if (m_result_ptr) {
           const int size = ValueTraits::value_size(
@@ -403,7 +409,10 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
             MemorySpaceAccess<Kokkos::Experimental::HIPSpace,
                               typename ViewType::memory_space>::accessible),
         m_scratch_space(nullptr),
-        m_scratch_flags(nullptr) {}
+        m_scratch_flags(nullptr),
+        m_shared_memory_lock(m_policy.space()
+                                 .impl_internal_space_instance()
+                                 ->m_mutexSharedMemory) {}
 
   ParallelReduce(const FunctorType& arg_functor, const Policy& arg_policy,
                  const ReducerType& reducer)
@@ -416,23 +425,25 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
                               typename ReducerType::result_view_type::
                                   memory_space>::accessible),
         m_scratch_space(nullptr),
-        m_scratch_flags(nullptr) {}
+        m_scratch_flags(nullptr),
+        m_shared_memory_lock(m_policy.space()
+                                 .impl_internal_space_instance()
+                                 ->m_mutexSharedMemory) {}
+
   template <typename Policy, typename Functor>
-  static int max_tile_size_product(const Policy& pol, const Functor&) {
+  static int max_tile_size_product(const Policy&, const Functor&) {
     using closure_type =
         ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>,
                        ReducerType, Kokkos::Experimental::HIP>;
-    hipFuncAttributes attr = Kokkos::Experimental::Impl::HIPParallelLaunch<
-        closure_type, LaunchBounds>::get_hip_func_attributes();
-    auto const& prop = pol.space().hip_device_prop();
-    // Limits due do registers/SM
-    int const regs_per_sm        = prop.regsPerMultiprocessor;
-    int const regs_per_thread    = attr.numRegs;
-    int const max_threads_per_sm = regs_per_sm / regs_per_thread;
-    return std::min(
-        max_threads_per_sm,
-        static_cast<int>(
-            Kokkos::Experimental::Impl::HIPTraits::MaxThreadsPerBlock));
+    unsigned block_size =
+        Kokkos::Experimental::Impl::hip_get_max_blocksize<closure_type,
+                                                          LaunchBounds>();
+    if (block_size == 0) {
+      Kokkos::Impl::throw_runtime_exception(
+          std::string("Kokkos::Impl::ParallelReduce< HIP > could not find a "
+                      "valid tile size."));
+    }
+    return block_size;
   }
 };
 }  // namespace Impl
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_Range.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_Range.hpp
index 7d2825eeb4c6be1d060d1e8d7c3eb67097729ccf..e02ead1e990151a30d0a87b280bada8c774ca5c5 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_Range.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_Range.hpp
@@ -108,16 +108,21 @@ class ParallelFor<FunctorType, Kokkos::RangePolicy<Traits...>,
   inline void execute() const {
     const typename Policy::index_type nwork = m_policy.end() - m_policy.begin();
 
+    using DriverType =
+        ParallelFor<FunctorType, Policy, Kokkos::Experimental::HIP>;
     const int block_size =
-        LaunchBounds::maxTperB
-            ? LaunchBounds::maxTperB
-            : ::Kokkos::Experimental::Impl::HIPTraits::
-                  MaxThreadsPerBlock;  // FIXME_HIP Choose block_size better
+        Kokkos::Experimental::Impl::hip_get_preferred_blocksize<DriverType,
+                                                                LaunchBounds>();
     const dim3 block(1, block_size, 1);
     const dim3 grid(
         typename Policy::index_type((nwork + block.y - 1) / block.y), 1, 1);
 
-    Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelFor, LaunchBounds>(
+    if (block_size == 0) {
+      Kokkos::Impl::throw_runtime_exception(
+          std::string("Kokkos::Impl::ParallelFor< HIP > could not find a "
+                      "valid execution configuration."));
+    }
+    Kokkos::Experimental::Impl::hip_parallel_launch<DriverType, LaunchBounds>(
         *this, grid, block, 0, m_policy.space().impl_internal_space_instance(),
         false);
   }
@@ -173,15 +178,12 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
   const bool m_result_ptr_host_accessible;
   size_type* m_scratch_space = nullptr;
   size_type* m_scratch_flags = nullptr;
+  // Only let one ParallelReduce/Scan modify the shared memory. The
+  // constructor acquires the mutex which is released in the destructor.
+  std::unique_lock<std::mutex> m_shared_memory_lock;
 
-#if HIP_VERSION < 401
-  static bool constexpr UseShflReduction =
-      ((sizeof(value_type) > 2 * sizeof(double)) &&
-       static_cast<bool>(ValueTraits::StaticValueSize));
-#else
   static bool constexpr UseShflReduction =
       static_cast<bool>(ValueTraits::StaticValueSize);
-#endif
 
  private:
   struct ShflReductionTag {};
@@ -328,30 +330,15 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
 
   // Determine block size constrained by shared memory:
   inline unsigned local_block_size(const FunctorType& f) {
-    unsigned int n =
-        ::Kokkos::Experimental::Impl::HIPTraits::MaxThreadsPerBlock;
-    int shmem_size =
-        hip_single_inter_block_reduce_scan_shmem<false, FunctorType, WorkTag>(
-            f, n);
-    using closure_type = Impl::ParallelReduce<FunctorType, Policy, ReducerType>;
-    hipFuncAttributes attr = ::Kokkos::Experimental::Impl::HIPParallelLaunch<
-        closure_type, LaunchBounds>::get_hip_func_attributes();
-    while (
-        (n &&
-         (m_policy.space().impl_internal_space_instance()->m_maxShmemPerBlock <
-          shmem_size)) ||
-        (n >
-         static_cast<unsigned int>(
-             ::Kokkos::Experimental::Impl::hip_get_max_block_size<FunctorType,
-                                                                  LaunchBounds>(
-                 m_policy.space().impl_internal_space_instance(), attr, f, 1,
-                 shmem_size, 0)))) {
-      n >>= 1;
-      shmem_size =
-          hip_single_inter_block_reduce_scan_shmem<false, FunctorType, WorkTag>(
-              f, n);
-    }
-    return n;
+    const auto& instance = m_policy.space().impl_internal_space_instance();
+    auto shmem_functor   = [&f](unsigned n) {
+      return hip_single_inter_block_reduce_scan_shmem<false, FunctorType,
+                                                      WorkTag>(f, n);
+    };
+    using DriverType = ParallelReduce<FunctorType, Policy, ReducerType,
+                                      Kokkos::Experimental::HIP>;
+    return Kokkos::Experimental::Impl::hip_get_preferred_blocksize<
+        DriverType, LaunchBounds>(instance, shmem_functor);
   }
 
   inline void execute() {
@@ -362,7 +349,11 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
                                  !std::is_same<ReducerType, InvalidType>::value;
     if ((nwork > 0) || need_device_set) {
       const int block_size = local_block_size(m_functor);
-      KOKKOS_ASSERT(block_size > 0);
+      if (block_size == 0) {
+        Kokkos::Impl::throw_runtime_exception(
+            std::string("Kokkos::Impl::ParallelReduce< HIP > could not find a "
+                        "valid execution configuration."));
+      }
 
       m_scratch_space =
           ::Kokkos::Experimental::Impl::hip_internal_scratch_space(
@@ -391,14 +382,17 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
                                                          WorkTag>(m_functor,
                                                                   block.y);
 
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelReduce,
-                                                    LaunchBounds>(
+      using DriverType = ParallelReduce<FunctorType, Policy, ReducerType,
+                                        Kokkos::Experimental::HIP>;
+      Kokkos::Experimental::Impl::hip_parallel_launch<DriverType, LaunchBounds>(
           *this, grid, block, shmem,
           m_policy.space().impl_internal_space_instance(),
           false);  // copy to device and execute
 
       if (!m_result_ptr_device_accessible) {
-        m_policy.space().impl_internal_space_instance()->fence();
+        m_policy.space().impl_internal_space_instance()->fence(
+            "Kokkos::Impl::ParallelReduce<RangePolicy,HIP>: fence because "
+            "reduction can't access result storage location");
 
         if (m_result_ptr) {
           const int size = ValueTraits::value_size(
@@ -429,7 +423,10 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
                               typename ViewType::memory_space>::accessible),
         m_result_ptr_host_accessible(
             MemorySpaceAccess<Kokkos::HostSpace,
-                              typename ViewType::memory_space>::accessible) {}
+                              typename ViewType::memory_space>::accessible),
+        m_shared_memory_lock(m_policy.space()
+                                 .impl_internal_space_instance()
+                                 ->m_mutexSharedMemory) {}
 
   ParallelReduce(const FunctorType& arg_functor, const Policy& arg_policy,
                  const ReducerType& reducer)
@@ -444,7 +441,10 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
         m_result_ptr_host_accessible(
             MemorySpaceAccess<Kokkos::HostSpace,
                               typename ReducerType::result_view_type::
-                                  memory_space>::accessible) {}
+                                  memory_space>::accessible),
+        m_shared_memory_lock(m_policy.space()
+                                 .impl_internal_space_instance()
+                                 ->m_mutexSharedMemory) {}
 };
 
 template <class FunctorType, class... Traits>
@@ -482,6 +482,9 @@ class ParallelScanHIPBase {
   size_type* m_scratch_flags = nullptr;
   size_type m_final          = false;
   int m_grid_x               = 0;
+  // Only let one ParallelReduce/Scan modify the shared memory. The
+  // constructor acquires the mutex which is released in the destructor.
+  std::unique_lock<std::mutex> m_shared_memory_lock;
 
  private:
   template <class TagType>
@@ -624,22 +627,7 @@ class ParallelScanHIPBase {
   }
 
   // Determine block size constrained by shared memory:
-  inline unsigned local_block_size(const FunctorType& f) {
-    // blockDim.y must be power of two = 128 (2 warps) or 256 (4 warps) or
-    // 512 (8 warps) gridDim.x <= blockDim.y * blockDim.y
-    //
-    // TODO check best option
-
-    unsigned n = Experimental::Impl::HIPTraits::WarpSize * 4;
-    while (n && static_cast<unsigned>(m_policy.space()
-                                          .impl_internal_space_instance()
-                                          ->m_maxShmemPerBlock) <
-                    hip_single_inter_block_reduce_scan_shmem<false, FunctorType,
-                                                             WorkTag>(f, n)) {
-      n >>= 1;
-    }
-    return n;
-  }
+  virtual inline unsigned local_block_size(const FunctorType& f) = 0;
 
   inline void impl_execute() {
     const index_type nwork = m_policy.end() - m_policy.begin();
@@ -649,7 +637,11 @@ class ParallelScanHIPBase {
       const int gridMaxComputeCapability_2x = 0x01fff;
 
       const int block_size = static_cast<int>(local_block_size(m_functor));
-      KOKKOS_ASSERT(block_size > 0);
+      if (block_size == 0) {
+        Kokkos::Impl::throw_runtime_exception(
+            std::string("Kokkos::Impl::ParallelScan< HIP > could not find a "
+                        "valid execution configuration."));
+      }
 
       const int grid_max =
           std::min(block_size * block_size, gridMaxComputeCapability_2x);
@@ -674,15 +666,16 @@ class ParallelScanHIPBase {
       const int shmem = ValueTraits::value_size(m_functor) * (block_size + 2);
 
       m_final = false;
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelScanHIPBase,
-                                                    LaunchBounds>(
+      // these ones are OK to be just the base because the specializations
+      // do not modify the kernel at all
+      using DriverType = ParallelScanHIPBase<FunctorType, Traits...>;
+      Kokkos::Experimental::Impl::hip_parallel_launch<DriverType, LaunchBounds>(
           *this, grid, block, shmem,
           m_policy.space().impl_internal_space_instance(),
           false);  // copy to device and execute
 
       m_final = true;
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelScanHIPBase,
-                                                    LaunchBounds>(
+      Kokkos::Experimental::Impl::hip_parallel_launch<DriverType, LaunchBounds>(
           *this, grid, block, shmem,
           m_policy.space().impl_internal_space_instance(),
           false);  // copy to device and execute
@@ -690,13 +683,17 @@ class ParallelScanHIPBase {
   }
 
   ParallelScanHIPBase(const FunctorType& arg_functor, const Policy& arg_policy)
-      : m_functor(arg_functor), m_policy(arg_policy) {}
+      : m_functor(arg_functor),
+        m_policy(arg_policy),
+        m_shared_memory_lock(m_policy.space()
+                                 .impl_internal_space_instance()
+                                 ->m_mutexSharedMemory) {}
 };
 
 template <class FunctorType, class... Traits>
 class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
                    Kokkos::Experimental::HIP>
-    : private ParallelScanHIPBase<FunctorType, Traits...> {
+    : public ParallelScanHIPBase<FunctorType, Traits...> {
  public:
   using Base = ParallelScanHIPBase<FunctorType, Traits...>;
   using Base::operator();
@@ -706,6 +703,23 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
   ParallelScan(const FunctorType& arg_functor,
                const typename Base::Policy& arg_policy)
       : Base(arg_functor, arg_policy) {}
+
+  inline unsigned local_block_size(const FunctorType& f) {
+    // blockDim.y must be power of two = 128 (2 warps) or 256 (4 warps) or
+    // 512 (8 warps) gridDim.x <= blockDim.y * blockDim.y
+
+    const auto& instance =
+        Base::m_policy.space().impl_internal_space_instance();
+    auto shmem_functor = [&f](unsigned n) {
+      return hip_single_inter_block_reduce_scan_shmem<false, FunctorType,
+                                                      typename Base::WorkTag>(
+          f, n);
+    };
+    using DriverType = ParallelScan<FunctorType, typename Base::Policy,
+                                    Kokkos::Experimental::HIP>;
+    return Kokkos::Experimental::Impl::hip_get_preferred_blocksize<
+        DriverType, typename Base::LaunchBounds>(instance, shmem_functor);
+  }
 };
 
 //----------------------------------------------------------------------------
@@ -713,7 +727,7 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
 template <class FunctorType, class ReturnType, class... Traits>
 class ParallelScanWithTotal<FunctorType, Kokkos::RangePolicy<Traits...>,
                             ReturnType, Kokkos::Experimental::HIP>
-    : private ParallelScanHIPBase<FunctorType, Traits...> {
+    : public ParallelScanHIPBase<FunctorType, Traits...> {
  public:
   using Base = ParallelScanHIPBase<FunctorType, Traits...>;
   using Base::operator();
@@ -737,6 +751,24 @@ class ParallelScanWithTotal<FunctorType, Kokkos::RangePolicy<Traits...>,
                         const typename Base::Policy& arg_policy,
                         ReturnType& arg_returnvalue)
       : Base(arg_functor, arg_policy), m_returnvalue(arg_returnvalue) {}
+
+  inline unsigned local_block_size(const FunctorType& f) {
+    // blockDim.y must be power of two = 128 (2 warps) or 256 (4 warps) or
+    // 512 (8 warps) gridDim.x <= blockDim.y * blockDim.y
+
+    const auto& instance =
+        Base::m_policy.space().impl_internal_space_instance();
+    auto shmem_functor = [&f](unsigned n) {
+      return hip_single_inter_block_reduce_scan_shmem<false, FunctorType,
+                                                      typename Base::WorkTag>(
+          f, n);
+    };
+    using DriverType =
+        ParallelScanWithTotal<FunctorType, typename Base::Policy, ReturnType,
+                              Kokkos::Experimental::HIP>;
+    return Kokkos::Experimental::Impl::hip_get_preferred_blocksize<
+        DriverType, typename Base::LaunchBounds>(instance, shmem_functor);
+  }
 };
 
 }  // namespace Impl
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_Team.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_Team.hpp
index 96c3ff2a751027a4eb05b03c99487207c9acf708..b794f5bc037111a8774ed23d1181326d3fa23b51 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_Team.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Parallel_Team.hpp
@@ -56,20 +56,20 @@
 
 namespace Kokkos {
 namespace Impl {
+
 template <typename... Properties>
 class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
     : public PolicyTraits<Properties...> {
  public:
   using execution_policy = TeamPolicyInternal;
 
-  using traits = PolicyTraits<Properties...>;
+  using traits    = PolicyTraits<Properties...>;
+  using BlockType = Kokkos::Experimental::Impl::BlockType;
 
   template <typename ExecSpace, typename... OtherProperties>
   friend class TeamPolicyInternal;
 
  private:
-  static int constexpr MAX_WARP = 8;
-
   typename traits::execution_space m_space;
   int m_league_size;
   int m_team_size;
@@ -101,17 +101,9 @@ class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
   template <typename FunctorType>
   int team_size_max(FunctorType const& f, ParallelForTag const&) const {
     using closure_type =
-        Impl::ParallelFor<FunctorType, TeamPolicy<Properties...> >;
-    hipFuncAttributes attr = ::Kokkos::Experimental::Impl::HIPParallelLaunch<
-        closure_type,
-        typename traits::launch_bounds>::get_hip_func_attributes();
-    int const block_size = ::Kokkos::Experimental::Impl::hip_get_max_block_size<
-        FunctorType, typename traits::launch_bounds>(
-        space().impl_internal_space_instance(), attr, f,
-        static_cast<size_t>(impl_vector_length()),
-        static_cast<size_t>(team_scratch_size(0)) + 2 * sizeof(double),
-        static_cast<size_t>(thread_scratch_size(0)) + sizeof(double));
-    return block_size / impl_vector_length();
+        Impl::ParallelFor<FunctorType, TeamPolicy<Properties...>>;
+
+    return internal_team_size_common<BlockType::Max, closure_type>(f);
   }
 
   template <class FunctorType>
@@ -129,8 +121,8 @@ class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
     return internal_team_size_max<closure_type>(f);
   }
 
-  template <class FunctorType, class ReducerType>
-  inline int team_size_max(const FunctorType& f, const ReducerType& /*r*/,
+  template <typename FunctorType, typename ReducerType>
+  inline int team_size_max(const FunctorType& f, const ReducerType&,
                            const ParallelReduceTag&) const {
     using closure_type =
         Impl::ParallelReduce<FunctorType, TeamPolicy<Properties...>,
@@ -141,17 +133,9 @@ class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
   template <typename FunctorType>
   int team_size_recommended(FunctorType const& f, ParallelForTag const&) const {
     using closure_type =
-        Impl::ParallelFor<FunctorType, TeamPolicy<Properties...> >;
-    hipFuncAttributes attr = ::Kokkos::Experimental::Impl::HIPParallelLaunch<
-        closure_type,
-        typename traits::launch_bounds>::get_hip_func_attributes();
-    int const block_size = ::Kokkos::Experimental::Impl::hip_get_opt_block_size<
-        FunctorType, typename traits::launch_bounds>(
-        space().impl_internal_space_instance(), attr, f,
-        static_cast<size_t>(impl_vector_length()),
-        static_cast<size_t>(team_scratch_size(0)) + 2 * sizeof(double),
-        static_cast<size_t>(thread_scratch_size(0)) + sizeof(double));
-    return block_size / impl_vector_length();
+        Impl::ParallelFor<FunctorType, TeamPolicy<Properties...>>;
+
+    return internal_team_size_common<BlockType::Preferred, closure_type>(f);
   }
 
   template <typename FunctorType>
@@ -169,7 +153,7 @@ class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
     return internal_team_size_recommended<closure_type>(f);
   }
 
-  template <class FunctorType, class ReducerType>
+  template <typename FunctorType, typename ReducerType>
   int team_size_recommended(FunctorType const& f, ReducerType const&,
                             ParallelReduceTag const&) const {
     using closure_type =
@@ -177,6 +161,7 @@ class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
                              ReducerType>;
     return internal_team_size_recommended<closure_type>(f);
   }
+
   inline bool impl_auto_vector_length() const { return m_tune_vector_length; }
   inline bool impl_auto_team_size() const { return m_tune_team_size; }
   static int vector_length_max() {
@@ -211,7 +196,10 @@ class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
   inline void impl_set_vector_length(size_t size) { m_vector_length = size; }
   inline void impl_set_team_size(size_t size) { m_team_size = size; }
   int impl_vector_length() const { return m_vector_length; }
+
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
   KOKKOS_DEPRECATED int vector_length() const { return impl_vector_length(); }
+#endif
 
   int team_size() const { return m_team_size; }
 
@@ -266,7 +254,8 @@ class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
           "space.");
 
     // Make sure total block size is permissible
-    if (m_team_size * m_vector_length > 1024) {
+    if (m_team_size * m_vector_length >
+        ::Kokkos::Experimental::Impl::HIPTraits::MaxThreadsPerBlock) {
       Impl::throw_runtime_exception(
           std::string("Kokkos::TeamPolicy< HIP > the team size is too large. "
                       "Team size x vector length must be smaller than 1024."));
@@ -363,26 +352,84 @@ class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
   using member_type = Kokkos::Impl::HIPTeamMember;
 
  protected:
-  template <class ClosureType, class FunctorType, class BlockSizeCallable>
-  int internal_team_size_common(const FunctorType& f,
-                                BlockSizeCallable&& block_size_callable) const {
-    using closure_type = ClosureType;
+  template <BlockType BlockSize, class ClosureType, class FunctorType>
+  int internal_team_size_common(const FunctorType& f) const {
+    // FIXME_HIP: this could be unified with the
+    // internal_team_size_common_reduce
+    //            once we can turn c++17 constexpr on by default.
+    //            The problem right now is that we can't turn off the evaluation
+    //            of the functor_value_traits's valuesize / StaticValueSize
+
+    const unsigned shmem_block  = team_scratch_size(0) + 2 * sizeof(double);
+    const unsigned shmem_thread = thread_scratch_size(0) + sizeof(double);
+    const int vector_length     = impl_vector_length();
+
+    const auto functor = [&f, shmem_block, shmem_thread, vector_length](
+                             const hipFuncAttributes& attr, int block_size) {
+      int functor_shmem =
+          ::Kokkos::Impl::FunctorTeamShmemSize<FunctorType>::value(
+              f, block_size / vector_length);
+      return shmem_block + shmem_thread * (block_size / vector_length) +
+             functor_shmem + attr.sharedSizeBytes;
+    };
+    int block_size;
+    // FIXME_HIP - could be if constexpr for c++17
+    if (BlockSize == BlockType::Max) {
+      block_size = ::Kokkos::Experimental::Impl::hip_get_max_team_blocksize<
+          ClosureType, typename traits::launch_bounds>(
+          space().impl_internal_space_instance(), functor);
+    } else {
+      block_size =
+          ::Kokkos::Experimental::Impl::hip_get_preferred_team_blocksize<
+              ClosureType, typename traits::launch_bounds>(
+              space().impl_internal_space_instance(), functor);
+    }
+    if (block_size == 0) {
+      Kokkos::Impl::throw_runtime_exception(
+          std::string("Kokkos::Impl::ParallelFor< HIP > could not find a valid "
+                      "team size."));
+    }
+    return block_size / impl_vector_length();
+  }
+
+  template <BlockType BlockSize, class ClosureType, class FunctorType>
+  int internal_team_size_common_reduce(const FunctorType& f) const {
     using functor_value_traits =
         Impl::FunctorValueTraits<FunctorType, typename traits::work_tag>;
 
-    hipFuncAttributes attr = ::Kokkos::Experimental::Impl::HIPParallelLaunch<
-        closure_type,
-        typename traits::launch_bounds>::get_hip_func_attributes();
-    const int block_size = std::forward<BlockSizeCallable>(block_size_callable)(
-        space().impl_internal_space_instance(), attr, f,
-        static_cast<size_t>(impl_vector_length()),
-        static_cast<size_t>(team_scratch_size(0)) + 2 * sizeof(double),
-        static_cast<size_t>(thread_scratch_size(0)) + sizeof(double) +
-            ((functor_value_traits::StaticValueSize != 0)
-                 ? 0
-                 : functor_value_traits::value_size(f)));
-    KOKKOS_ASSERT(block_size > 0);
+    const unsigned shmem_block  = team_scratch_size(0) + 2 * sizeof(double);
+    const unsigned shmem_thread = thread_scratch_size(0) + sizeof(double) +
+                                  ((functor_value_traits::StaticValueSize != 0)
+                                       ? 0
+                                       : functor_value_traits::value_size(f));
+    const int vector_length = impl_vector_length();
+
+    const auto functor = [&f, shmem_block, shmem_thread, vector_length](
+                             const hipFuncAttributes& attr, int block_size) {
+      int functor_shmem =
+          ::Kokkos::Impl::FunctorTeamShmemSize<FunctorType>::value(
+              f, block_size / vector_length);
+      return shmem_block + shmem_thread * (block_size / vector_length) +
+             functor_shmem + attr.sharedSizeBytes;
+    };
+    int block_size;
+    // FIXME_HIP - could be if constexpr for c++17
+    if (BlockSize == BlockType::Max) {
+      block_size = ::Kokkos::Experimental::Impl::hip_get_max_team_blocksize<
+          ClosureType, typename traits::launch_bounds>(
+          space().impl_internal_space_instance(), functor);
+    } else {
+      block_size =
+          ::Kokkos::Experimental::Impl::hip_get_preferred_team_blocksize<
+              ClosureType, typename traits::launch_bounds>(
+              space().impl_internal_space_instance(), functor);
+    }
 
+    if (block_size == 0) {
+      Kokkos::Impl::throw_runtime_exception(
+          std::string("Kokkos::Impl::ParallelReduce< HIP > could not find a "
+                      "valid team size."));
+    }
     // Currently we require Power-of-2 team size for reductions.
     int p2 = 1;
     while (p2 <= block_size) p2 *= 2;
@@ -392,16 +439,13 @@ class TeamPolicyInternal<Kokkos::Experimental::HIP, Properties...>
 
   template <class ClosureType, class FunctorType>
   int internal_team_size_max(const FunctorType& f) const {
-    return internal_team_size_common<ClosureType>(
-        f, ::Kokkos::Experimental::Impl::hip_get_max_block_size<
-               FunctorType, typename traits::launch_bounds>);
+    return internal_team_size_common_reduce<BlockType::Max, ClosureType>(f);
   }
 
   template <class ClosureType, class FunctorType>
   int internal_team_size_recommended(const FunctorType& f) const {
-    return internal_team_size_common<ClosureType>(
-        f, ::Kokkos::Experimental::Impl::hip_get_opt_block_size<
-               FunctorType, typename traits::launch_bounds>);
+    return internal_team_size_common_reduce<BlockType::Preferred, ClosureType>(
+        f);
   }
 };
 
@@ -505,7 +549,11 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
     dim3 const block(static_cast<int>(m_vector_size),
                      static_cast<int>(m_team_size), 1);
 
-    ::Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelFor, launch_bounds>(
+    using closure_type =
+        ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
+                    Kokkos::Experimental::HIP>;
+    ::Kokkos::Experimental::Impl::hip_parallel_launch<closure_type,
+                                                      launch_bounds>(
         *this, grid, block, shmem_size_total,
         m_policy.space().impl_internal_space_instance(),
         true);  // copy to device and execute
@@ -520,17 +568,9 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
         m_scratch_lock(m_policy.space()
                            .impl_internal_space_instance()
                            ->m_team_scratch_mutex) {
-    hipFuncAttributes attr = ::Kokkos::Experimental::Impl::HIPParallelLaunch<
-        ParallelFor, launch_bounds>::get_hip_func_attributes();
-    m_team_size =
-        m_team_size >= 0
-            ? m_team_size
-            : ::Kokkos::Experimental::Impl::hip_get_opt_block_size<
-                  FunctorType, launch_bounds>(
-                  m_policy.space().impl_internal_space_instance(), attr,
-                  m_functor, m_vector_size, m_policy.team_scratch_size(0),
-                  m_policy.thread_scratch_size(0)) /
-                  m_vector_size;
+    m_team_size = m_team_size >= 0 ? m_team_size
+                                   : arg_policy.team_size_recommended(
+                                         arg_functor, ParallelForTag());
 
     m_shmem_begin = (sizeof(double) * (m_team_size + 2));
     m_shmem_size =
@@ -556,23 +596,12 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
     int const shmem_size_total = m_shmem_begin + m_shmem_size;
     if (m_policy.space().impl_internal_space_instance()->m_maxShmemPerBlock <
         shmem_size_total) {
-      printf(
-          "%i %i\n",
-          m_policy.space().impl_internal_space_instance()->m_maxShmemPerBlock,
-          shmem_size_total);
       Kokkos::Impl::throw_runtime_exception(std::string(
           "Kokkos::Impl::ParallelFor< HIP > insufficient shared memory"));
     }
 
-    if (static_cast<int>(m_team_size) >
-        static_cast<int>(
-            ::Kokkos::Experimental::Impl::hip_get_max_block_size<FunctorType,
-                                                                 launch_bounds>(
-                m_policy.space().impl_internal_space_instance(), attr,
-                arg_functor, arg_policy.impl_vector_length(),
-                arg_policy.team_scratch_size(0),
-                arg_policy.thread_scratch_size(0)) /
-            arg_policy.impl_vector_length())) {
+    size_t max_size = arg_policy.team_size_max(arg_functor, ParallelForTag());
+    if (static_cast<int>(m_team_size) > static_cast<int>(max_size)) {
       Kokkos::Impl::throw_runtime_exception(std::string(
           "Kokkos::Impl::ParallelFor< HIP > requested too large team size."));
     }
@@ -839,8 +868,11 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
       }
       const int shmem_size_total = m_team_begin + m_shmem_begin + m_shmem_size;
 
-      Kokkos::Experimental::Impl::HIPParallelLaunch<ParallelReduce,
-                                                    launch_bounds>(
+      using closure_type =
+          ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
+                         ReducerType, Kokkos::Experimental::HIP>;
+      Kokkos::Experimental::Impl::hip_parallel_launch<closure_type,
+                                                      launch_bounds>(
           *this, grid, block, shmem_size_total,
           m_policy.space().impl_internal_space_instance(),
           true);  // copy to device and execute
@@ -890,17 +922,9 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
         m_scratch_lock(m_policy.space()
                            .impl_internal_space_instance()
                            ->m_team_scratch_mutex) {
-    hipFuncAttributes attr = Kokkos::Experimental::Impl::HIPParallelLaunch<
-        ParallelReduce, launch_bounds>::get_hip_func_attributes();
-    m_team_size =
-        m_team_size >= 0
-            ? m_team_size
-            : Kokkos::Experimental::Impl::hip_get_opt_block_size<FunctorType,
-                                                                 launch_bounds>(
-                  m_policy.space().impl_internal_space_instance(), attr,
-                  m_functor, m_vector_size, m_policy.team_scratch_size(0),
-                  m_policy.thread_scratch_size(0)) /
-                  m_vector_size;
+    m_team_size = m_team_size >= 0 ? m_team_size
+                                   : arg_policy.team_size_recommended(
+                                         arg_functor, ParallelReduceTag());
 
     m_team_begin =
         UseShflReduction
@@ -958,8 +982,9 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
                       "L0 scratch memory"));
     }
 
-    if (static_cast<int>(m_team_size) >
-        arg_policy.team_size_max(m_functor, m_reducer, ParallelReduceTag())) {
+    size_t max_size =
+        arg_policy.team_size_max(arg_functor, ParallelReduceTag());
+    if (static_cast<int>(m_team_size) > static_cast<int>(max_size)) {
       Kokkos::Impl::throw_runtime_exception(
           std::string("Kokkos::Impl::ParallelReduce< HIP > requested too "
                       "large team size."));
@@ -992,18 +1017,10 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
         m_scratch_lock(m_policy.space()
                            .impl_internal_space_instance()
                            ->m_team_scratch_mutex) {
-    hipFuncAttributes attr = Kokkos::Experimental::Impl::HIPParallelLaunch<
-        ParallelReduce, launch_bounds>::get_hip_func_attributes();
-    m_team_size =
-        m_team_size >= 0
-            ? m_team_size
-            : Kokkos::Experimental::Impl::hip_get_opt_block_size<FunctorType,
-                                                                 launch_bounds>(
-                  m_policy.space().impl_internal_space_instance(), attr,
-                  m_functor, m_vector_size, m_policy.team_scratch_size(0),
-                  m_policy.thread_scratch_size(0)) /
-                  m_vector_size;
-
+    m_team_size = m_team_size >= 0
+                      ? m_team_size
+                      : arg_policy.team_size_recommended(arg_functor, reducer,
+                                                         ParallelReduceTag());
     m_team_begin =
         UseShflReduction
             ? 0
@@ -1046,7 +1063,6 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
     // upon team size.
 
     const int shmem_size_total = m_team_begin + m_shmem_begin + m_shmem_size;
-
     if ((!Kokkos::Impl::is_integral_power_of_two(m_team_size) &&
          !UseShflReduction) ||
         m_policy.space().impl_internal_space_instance()->m_maxShmemPerBlock <
@@ -1054,8 +1070,10 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
       Kokkos::Impl::throw_runtime_exception(
           std::string("Kokkos::Impl::ParallelReduce< HIP > bad team size"));
     }
-    if (static_cast<int>(m_team_size) >
-        arg_policy.team_size_max(m_functor, m_reducer, ParallelReduceTag())) {
+
+    size_t max_size =
+        arg_policy.team_size_max(arg_functor, reducer, ParallelReduceTag());
+    if (static_cast<int>(m_team_size) > static_cast<int>(max_size)) {
       Kokkos::Impl::throw_runtime_exception(
           std::string("Kokkos::Impl::ParallelReduce< HIP > requested too "
                       "large team size."));
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp
index 15ca089d14740b6a2c42c69945a17a0c7bfa1bcc..e25ebe2ab355e626273aeff34615db45aa3465c7 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Space.cpp
@@ -67,102 +67,32 @@ namespace {
 hipStream_t get_deep_copy_stream() {
   static hipStream_t s = nullptr;
   if (s == nullptr) {
-    HIP_SAFE_CALL(hipStreamCreate(&s));
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipStreamCreate(&s));
   }
   return s;
 }
 }  // namespace
 
-DeepCopy<Kokkos::Experimental::HIPSpace, Kokkos::Experimental::HIPSpace,
-         Kokkos::Experimental::HIP>::DeepCopy(void* dst, const void* src,
-                                              size_t n) {
-  HIP_SAFE_CALL(hipMemcpy(dst, src, n, hipMemcpyDefault));
+void DeepCopyHIP(void* dst, void const* src, size_t n) {
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipMemcpy(dst, src, n, hipMemcpyDefault));
 }
 
-DeepCopy<HostSpace, Kokkos::Experimental::HIPSpace,
-         Kokkos::Experimental::HIP>::DeepCopy(void* dst, const void* src,
-                                              size_t n) {
-  HIP_SAFE_CALL(hipMemcpy(dst, src, n, hipMemcpyDefault));
-}
-
-DeepCopy<Kokkos::Experimental::HIPSpace, HostSpace,
-         Kokkos::Experimental::HIP>::DeepCopy(void* dst, const void* src,
-                                              size_t n) {
-  HIP_SAFE_CALL(hipMemcpy(dst, src, n, hipMemcpyDefault));
-}
-
-DeepCopy<Kokkos::Experimental::HIPSpace, Kokkos::Experimental::HIPSpace,
-         Kokkos::Experimental::HIP>::DeepCopy(const Kokkos::Experimental::HIP&
-                                                  instance,
-                                              void* dst, const void* src,
-                                              size_t n) {
-  HIP_SAFE_CALL(
-      hipMemcpyAsync(dst, src, n, hipMemcpyDefault, instance.hip_stream()));
-}
-
-DeepCopy<HostSpace, Kokkos::Experimental::HIPSpace, Kokkos::Experimental::HIP>::
-    DeepCopy(const Kokkos::Experimental::HIP& instance, void* dst,
-             const void* src, size_t n) {
-  HIP_SAFE_CALL(
-      hipMemcpyAsync(dst, src, n, hipMemcpyDefault, instance.hip_stream()));
-}
-
-DeepCopy<Kokkos::Experimental::HIPSpace, HostSpace, Kokkos::Experimental::HIP>::
-    DeepCopy(const Kokkos::Experimental::HIP& instance, void* dst,
-             const void* src, size_t n) {
-  HIP_SAFE_CALL(
-      hipMemcpyAsync(dst, src, n, hipMemcpyDefault, instance.hip_stream()));
-}
-
-DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace,
-         Kokkos::Experimental::HIPHostPinnedSpace,
-         Kokkos::Experimental::HIP>::DeepCopy(void* dst, const void* src,
-                                              size_t n) {
-  HIP_SAFE_CALL(hipMemcpy(dst, src, n, hipMemcpyDefault));
-}
-
-DeepCopy<HostSpace, Kokkos::Experimental::HIPHostPinnedSpace,
-         Kokkos::Experimental::HIP>::DeepCopy(void* dst, const void* src,
-                                              size_t n) {
-  HIP_SAFE_CALL(hipMemcpy(dst, src, n, hipMemcpyDefault));
-}
-
-DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace, HostSpace,
-         Kokkos::Experimental::HIP>::DeepCopy(void* dst, const void* src,
-                                              size_t n) {
-  HIP_SAFE_CALL(hipMemcpy(dst, src, n, hipMemcpyDefault));
-}
-
-DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace,
-         Kokkos::Experimental::HIPHostPinnedSpace, Kokkos::Experimental::HIP>::
-    DeepCopy(const Kokkos::Experimental::HIP& instance, void* dst,
-             const void* src, size_t n) {
-  HIP_SAFE_CALL(
-      hipMemcpyAsync(dst, src, n, hipMemcpyDefault, instance.hip_stream()));
-}
-
-DeepCopy<HostSpace, Kokkos::Experimental::HIPHostPinnedSpace,
-         Kokkos::Experimental::HIP>::DeepCopy(const Kokkos::Experimental::HIP&
-                                                  instance,
-                                              void* dst, const void* src,
-                                              size_t n) {
-  HIP_SAFE_CALL(
-      hipMemcpyAsync(dst, src, n, hipMemcpyDefault, instance.hip_stream()));
-}
-
-DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace, HostSpace,
-         Kokkos::Experimental::HIP>::DeepCopy(const Kokkos::Experimental::HIP&
-                                                  instance,
-                                              void* dst, const void* src,
-                                              size_t n) {
-  HIP_SAFE_CALL(
+void DeepCopyAsyncHIP(const Kokkos::Experimental::HIP& instance, void* dst,
+                      void const* src, size_t n) {
+  KOKKOS_IMPL_HIP_SAFE_CALL(
       hipMemcpyAsync(dst, src, n, hipMemcpyDefault, instance.hip_stream()));
 }
 
 void DeepCopyAsyncHIP(void* dst, void const* src, size_t n) {
   hipStream_t s = get_deep_copy_stream();
-  HIP_SAFE_CALL(hipMemcpyAsync(dst, src, n, hipMemcpyDefault, s));
-  HIP_SAFE_CALL(hipStreamSynchronize(s));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipMemcpyAsync(dst, src, n, hipMemcpyDefault, s));
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<
+      Kokkos::Experimental::HIP>(
+      "Kokkos::Impl::DeepCopyAsyncHIP: Post Deep Copy Fence on Deep-Copy "
+      "stream",
+      Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+          DeepCopyResourceSynchronization,
+      [&]() { KOKKOS_IMPL_HIP_SAFE_CALL(hipStreamSynchronize(s)); });
 }
 
 }  // namespace Impl
@@ -171,6 +101,7 @@ void DeepCopyAsyncHIP(void* dst, void const* src, size_t n) {
 /*--------------------------------------------------------------------------*/
 /*--------------------------------------------------------------------------*/
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 namespace Kokkos {
 
 KOKKOS_DEPRECATED void Experimental::HIPSpace::access_error() {
@@ -188,6 +119,7 @@ KOKKOS_DEPRECATED void Experimental::HIPSpace::access_error(const void* const) {
 }
 
 }  // namespace Kokkos
+#endif
 
 /*--------------------------------------------------------------------------*/
 /*--------------------------------------------------------------------------*/
@@ -283,7 +215,7 @@ void HIPSpace::impl_deallocate(
     Kokkos::Profiling::deallocateData(arg_handle, arg_label, arg_alloc_ptr,
                                       reported_size);
   }
-  HIP_SAFE_CALL(hipFree(arg_alloc_ptr));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipFree(arg_alloc_ptr));
 }
 
 void HIPHostPinnedSpace::deallocate(void* const arg_alloc_ptr,
@@ -307,7 +239,7 @@ void HIPHostPinnedSpace::impl_deallocate(
     Kokkos::Profiling::deallocateData(arg_handle, arg_label, arg_alloc_ptr,
                                       reported_size);
   }
-  HIP_SAFE_CALL(hipHostFree(arg_alloc_ptr));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipHostFree(arg_alloc_ptr));
 }
 
 }  // namespace Experimental
@@ -427,23 +359,42 @@ HIP::HIP()
       "HIP instance constructor");
 }
 
-HIP::HIP(hipStream_t const stream)
+HIP::HIP(hipStream_t const stream, bool manage_stream)
     : m_space_instance(new Impl::HIPInternal, [](Impl::HIPInternal* ptr) {
         ptr->finalize();
         delete ptr;
       }) {
   Impl::HIPInternal::singleton().verify_is_initialized(
       "HIP instance constructor");
-  m_space_instance->initialize(Impl::HIPInternal::singleton().m_hipDev, stream);
+  m_space_instance->initialize(Impl::HIPInternal::singleton().m_hipDev, stream,
+                               manage_stream);
 }
 
 void HIP::print_configuration(std::ostream& s, const bool) {
   Impl::HIPInternal::singleton().print_configuration(s);
 }
 
-void HIP::impl_static_fence() { HIP_SAFE_CALL(hipDeviceSynchronize()); }
+uint32_t HIP::impl_instance_id() const noexcept {
+  return m_space_instance->impl_get_instance_id();
+}
+void HIP::impl_static_fence(const std::string& name) {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<
+      Kokkos::Experimental::HIP>(
+      name,
+      Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+          GlobalDeviceSynchronization,
+      [&]() { KOKKOS_IMPL_HIP_SAFE_CALL(hipDeviceSynchronize()); });
+}
+void HIP::impl_static_fence() {
+  impl_static_fence("Kokkos::HIP::impl_static_fence: Unnamed Static Fence");
+}
 
-void HIP::fence() const { m_space_instance->fence(); }
+void HIP::fence(const std::string& name) const {
+  m_space_instance->fence(name);
+}
+void HIP::fence() const {
+  fence("Kokkos::HIP::fence(): Unnamed Instance Fence");
+}
 
 hipStream_t HIP::hip_stream() const { return m_space_instance->m_stream; }
 
@@ -489,6 +440,9 @@ void HIPSpaceInitializer::finalize(const bool all_spaces) {
 void HIPSpaceInitializer::fence() {
   Kokkos::Experimental::HIP::impl_static_fence();
 }
+void HIPSpaceInitializer::fence(const std::string& name) {
+  Kokkos::Experimental::HIP::impl_static_fence(name);
+}
 
 void HIPSpaceInitializer::print_configuration(std::ostream& msg,
                                               const bool detail) {
diff --git a/packages/kokkos/core/src/HIP/Kokkos_HIP_Team.hpp b/packages/kokkos/core/src/HIP/Kokkos_HIP_Team.hpp
index fe52886ced7c7a72454f9e731b3b5b4778f90073..fb67a25c5e7f5e3b0a48118ffe14372f0b1cd2dc 100644
--- a/packages/kokkos/core/src/HIP/Kokkos_HIP_Team.hpp
+++ b/packages/kokkos/core/src/HIP/Kokkos_HIP_Team.hpp
@@ -316,198 +316,6 @@ class HIPTeamMember {
 #endif
   }
 
-  //--------------------------------------------------------------------------
-  /**\brief  Global reduction across all blocks
-   *
-   *  Return !0 if reducer contains the final value
-   */
-  template <typename ReducerType>
-  KOKKOS_INLINE_FUNCTION static
-      typename std::enable_if<is_reducer<ReducerType>::value, int>::type
-      global_reduce(ReducerType const& reducer, int* const global_scratch_flags,
-                    void* const global_scratch_space, void* const shmem,
-                    int const shmem_size) {
-#ifdef __HIP_DEVICE_COMPILE__
-    using value_type   = typename ReducerType::value_type;
-    using pointer_type = value_type volatile*;
-
-    // Number of shared memory entries for the reduction:
-    const int nsh = shmem_size / sizeof(value_type);
-
-    // Number of HIP threads in the block, rank within the block
-    const int nid = blockDim.x * blockDim.y * blockDim.z;
-    const int tid =
-        threadIdx.x + blockDim.x * (threadIdx.y + blockDim.y * threadIdx.z);
-
-    // Reduces within block using all available shared memory
-    // Contributes if it is the root "vector lane"
-
-    // wn == number of warps in the block
-    // wx == which lane within the warp
-    // wy == which warp within the block
-
-    const int wn = (nid + Experimental::Impl::HIPTraits::WarpIndexMask) >>
-                   Experimental::Impl::HIPTraits::WarpIndexShift;
-    const int wx = tid & Experimental::Impl::HIPTraits::WarpIndexMask;
-    const int wy = tid >> Experimental::Impl::HIPTraits::WarpIndexShift;
-
-    //------------------------
-    {  // Intra warp shuffle reduction from contributing HIP threads
-
-      value_type tmp(reducer.reference());
-
-      int constexpr warp_size =
-          ::Kokkos::Experimental::Impl::HIPTraits::WarpSize;
-      for (int i = warp_size; static_cast<int>(blockDim.x) <= (i >>= 1);) {
-        Experimental::Impl::in_place_shfl_down(reducer.reference(), tmp, i,
-                                               warp_size);
-
-        // Root of each vector lane reduces "thread" contribution
-        if (0 == threadIdx.x && wx < i) {
-          reducer.join(&tmp, reducer.data());
-        }
-      }
-
-      // Reduce across warps using shared memory.
-      // Number of warps may not be power of two.
-
-      __syncthreads();  // Wait before shared data write
-
-      // Number of shared memory entries for the reduction
-      // is at most one per warp
-      const int nentry = wn < nsh ? wn : nsh;
-
-      if (0 == wx && wy < nentry) {
-        // Root thread of warp 'wy' has warp's value to contribute
-        (reinterpret_cast<value_type*>(shmem))[wy] = tmp;
-      }
-
-      __syncthreads();  // Wait for write to be visible to block
-
-      // When more warps than shared entries
-      // then warps must take turns joining their contribution
-      // to the designated shared memory entry.
-      for (int i = nentry; i < wn; i += nentry) {
-        const int k = wy - i;
-
-        if (0 == wx && i <= wy && k < nentry) {
-          // Root thread of warp 'wy' has warp's value to contribute
-          reducer.join((reinterpret_cast<value_type*>(shmem)) + k, &tmp);
-        }
-
-        __syncthreads();  // Wait for write to be visible to block
-      }
-
-      // One warp performs the inter-warp reduction:
-
-      if (0 == wy) {
-        // Start fan-in at power of two covering nentry
-
-        for (int i = (1 << (warp_size - __clz(nentry - 1))); (i >>= 1);) {
-          const int k = wx + i;
-          if (wx < i && k < nentry) {
-            reducer.join((reinterpret_cast<pointer_type>(shmem)) + wx,
-                         (reinterpret_cast<pointer_type>(shmem)) + k);
-            __threadfence_block();  // Wait for write to be visible to warp
-          }
-        }
-      }
-    }
-    //------------------------
-    {  // Write block's value to global_scratch_memory
-
-      int last_block = 0;
-
-      if (0 == wx) {
-        reducer.copy((reinterpret_cast<pointer_type>(global_scratch_space)) +
-                         blockIdx.x * reducer.length(),
-                     reducer.data());
-
-        __threadfence();  // Wait until global write is visible.
-
-        last_block = static_cast<int>(gridDim.x) ==
-                     1 + Kokkos::atomic_fetch_add(global_scratch_flags, 1);
-
-        // If last block then reset count
-        if (last_block) *global_scratch_flags = 0;
-      }
-
-      // FIXME hip does not support __syncthreads_or so we need to do it by hand
-      // last_block = __syncthreads_or(last_block);
-
-      __shared__ int last_block_shared;
-      if (last_block) last_block_shared = last_block;
-      __threadfence_block();
-
-      if (!last_block_shared) return 0;
-    }
-    //------------------------
-    // Last block reads global_scratch_memory into shared memory.
-
-    const int nentry = nid < gridDim.x ? (nid < nsh ? nid : nsh)
-                                       : (gridDim.x < nsh ? gridDim.x : nsh);
-
-    // nentry = min( nid , nsh , gridDim.x )
-
-    // whole block reads global memory into shared memory:
-
-    if (tid < nentry) {
-      const int offset = tid * reducer.length();
-
-      reducer.copy(
-          (reinterpret_cast<pointer_type>(shmem)) + offset,
-          (reinterpret_cast<pointer_type>(global_scratch_space)) + offset);
-
-      for (int i = nentry + tid; i < static_cast<int>(gridDim.x); i += nentry) {
-        reducer.join((reinterpret_cast<pointer_type>(shmem)) + offset,
-                     (reinterpret_cast<pointer_type>(global_scratch_space)) +
-                         i * reducer.length());
-      }
-    }
-
-    __syncthreads();  // Wait for writes to be visible to block
-
-    if (0 == wy) {
-      // Iterate to reduce shared memory to single warp fan-in size
-
-      int constexpr warp_size =
-          ::Kokkos::Experimental::Impl::HIPTraits::WarpSize;
-      const int nreduce = warp_size < nentry ? warp_size : nentry;
-
-      if (wx < nreduce && nreduce < nentry) {
-        for (int i = nreduce + wx; i < nentry; i += nreduce) {
-          reducer.join(((pointer_type)shmem) + wx, ((pointer_type)shmem) + i);
-        }
-        __threadfence_block();  // Wait for writes to be visible to warp
-      }
-
-      // Start fan-in at power of two covering nentry
-
-      for (int i = (1 << (warp_size - __clz(nreduce - 1))); (i >>= 1);) {
-        const int k = wx + i;
-        if (wx < i && k < nreduce) {
-          reducer.join((reinterpret_cast<pointer_type>(shmem)) + wx,
-                       (reinterpret_cast<pointer_type>(shmem)) + k);
-          __threadfence_block();  // Wait for writes to be visible to warp
-        }
-      }
-
-      if (0 == wx) {
-        reducer.copy(reducer.data(), reinterpret_cast<pointer_type>(shmem));
-        return 1;
-      }
-    }
-    return 0;
-#else
-    (void)reducer;
-    (void)global_scratch_flags;
-    (void)global_scratch_space;
-    (void)shmem;
-    (void)shmem_size;
-    return 0;
-#endif
-  }
-
   //----------------------------------------
   // Private for the driver
 
diff --git a/packages/kokkos/core/src/HPX/Kokkos_HPX.cpp b/packages/kokkos/core/src/HPX/Kokkos_HPX.cpp
index 910d5e52e6ac62e290f4d7c918217aa518ec3ec7..d9cb66e11f4638c8635d0e9e33d7cbda67e1cada 100644
--- a/packages/kokkos/core/src/HPX/Kokkos_HPX.cpp
+++ b/packages/kokkos/core/src/HPX/Kokkos_HPX.cpp
@@ -191,6 +191,9 @@ void HPXSpaceInitializer::finalize(const bool all_spaces) {
 }
 
 void HPXSpaceInitializer::fence() { Kokkos::Experimental::HPX().fence(); }
+void HPXSpaceInitializer::fence(const std::string &name) {
+  Kokkos::Experimental::HPX().fence(name);
+}
 
 void HPXSpaceInitializer::print_configuration(std::ostream &msg,
                                               const bool detail) {
diff --git a/packages/kokkos/core/src/HPX/Kokkos_HPX_Task.hpp b/packages/kokkos/core/src/HPX/Kokkos_HPX_Task.hpp
index df09e026fd9b45bc1c4f7d0c55e5ae10d336ad72..7bb3ca5d007023d99314c8d45de748da7836136d 100644
--- a/packages/kokkos/core/src/HPX/Kokkos_HPX_Task.hpp
+++ b/packages/kokkos/core/src/HPX/Kokkos_HPX_Task.hpp
@@ -82,7 +82,9 @@ class TaskQueueSpecialization<
     task_queue.scheduler = &scheduler;
     Kokkos::Impl::dispatch_execute_task(&task_queue,
                                         Kokkos::Experimental::HPX());
-    Kokkos::Experimental::HPX().fence();
+    Kokkos::Experimental::HPX().fence(
+        "Kokkos::Impl::TaskQueueSpecialization<SimpleTask>::execute: fence "
+        "after task execution");
   }
 
   // Must provide task queue execution function
@@ -214,7 +216,7 @@ class TaskQueueSpecializationConstrained<
     task_queue.scheduler = &scheduler;
     Kokkos::Impl::dispatch_execute_task(&task_queue,
                                         Kokkos::Experimental::HPX());
-    Kokkos::Experimental::HPX().fence();
+    Kokkos::Experimental::HPX().fence()"Kokkos::Impl::TaskQueueSpecializationConstrained::execute: fence after task execution";
   }
 
   // Must provide task queue execution function
diff --git a/packages/kokkos/core/src/HPX/Kokkos_HPX_WorkGraphPolicy.hpp b/packages/kokkos/core/src/HPX/Kokkos_HPX_WorkGraphPolicy.hpp
index 527fe12ad937f9b89029f12d5c64044f40671572..d7e13e28f054569926382933232b7119ca96a192 100644
--- a/packages/kokkos/core/src/HPX/Kokkos_HPX_WorkGraphPolicy.hpp
+++ b/packages/kokkos/core/src/HPX/Kokkos_HPX_WorkGraphPolicy.hpp
@@ -79,7 +79,9 @@ class ParallelFor<FunctorType, Kokkos::WorkGraphPolicy<Traits...>,
  public:
   void execute() const {
     dispatch_execute_task(this, m_policy.space());
-    m_policy.space().fence();
+    m_policy.space().fence(
+        "Kokkos::Experimental::Impl::HPX::ParallelFor<WorkGraphPolicy>: fence "
+        "after kernel execution");
   }
 
   void execute_task() const {
diff --git a/packages/kokkos/core/src/KokkosExp_InterOp.hpp b/packages/kokkos/core/src/KokkosExp_InterOp.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..37c2088f88f08758f5f1585b7138f43dd73d54eb
--- /dev/null
+++ b/packages/kokkos/core/src/KokkosExp_InterOp.hpp
@@ -0,0 +1,147 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#ifndef KOKKOS_CORE_EXP_INTEROP_HPP
+#define KOKKOS_CORE_EXP_INTEROP_HPP
+
+#include <Kokkos_Core_fwd.hpp>
+#include <Kokkos_Layout.hpp>
+#include <Kokkos_MemoryTraits.hpp>
+#include <Kokkos_View.hpp>
+#include <impl/Kokkos_Utilities.hpp>
+#include <type_traits>
+
+namespace Kokkos {
+namespace Impl {
+
+// ------------------------------------------------------------------ //
+//  this is used to convert
+//      Kokkos::Device<ExecSpace, MemSpace> to MemSpace
+//
+template <typename Tp>
+struct device_memory_space {
+  using type = Tp;
+};
+
+template <typename ExecT, typename MemT>
+struct device_memory_space<Kokkos::Device<ExecT, MemT>> {
+  using type = MemT;
+};
+
+template <typename Tp>
+using device_memory_space_t = typename device_memory_space<Tp>::type;
+
+// ------------------------------------------------------------------ //
+//  this is the impl version which takes a view and converts to python
+//  view type
+//
+template <typename, typename...>
+struct python_view_type_impl;
+
+template <template <typename...> class ViewT, typename ValueT,
+          typename... Types>
+struct python_view_type_impl<ViewT<ValueT>, type_list<Types...>> {
+  using type = ViewT<ValueT, device_memory_space_t<Types>...>;
+};
+
+template <template <typename...> class ViewT, typename ValueT,
+          typename... Types>
+struct python_view_type_impl<ViewT<ValueT, Types...>>
+    : python_view_type_impl<ViewT<ValueT>,
+                            filter_type_list_t<is_default_memory_trait,
+                                               type_list<Types...>, false>> {};
+
+template <typename... T>
+using python_view_type_impl_t = typename python_view_type_impl<T...>::type;
+
+}  // namespace Impl
+}  // namespace Kokkos
+
+namespace Kokkos {
+
+template <typename DataType, class... Properties>
+class DynRankView;
+
+namespace Impl {
+
+// Duplicate from the header file for DynRankView to avoid core depending on
+// containers.
+template <class>
+struct is_dyn_rank_view_dup : public std::false_type {};
+
+template <class D, class... P>
+struct is_dyn_rank_view_dup<Kokkos::DynRankView<D, P...>>
+    : public std::true_type {};
+
+}  // namespace Impl
+
+namespace Experimental {
+
+// ------------------------------------------------------------------ //
+//  this is used to extract the uniform type of a view
+//
+template <typename ViewT>
+struct python_view_type {
+  static_assert(
+      Kokkos::is_view<std::decay_t<ViewT>>::value ||
+          Kokkos::Impl::is_dyn_rank_view_dup<std::decay_t<ViewT>>::value,
+      "Error! python_view_type only supports Kokkos::View and "
+      "Kokkos::DynRankView");
+
+  using type =
+      Kokkos::Impl::python_view_type_impl_t<typename ViewT::array_type>;
+};
+
+template <typename ViewT>
+using python_view_type_t = typename python_view_type<ViewT>::type;
+
+template <typename Tp>
+auto as_python_type(Tp&& _v) {
+  using cast_type = python_view_type_t<Tp>;
+  return static_cast<cast_type>(std::forward<Tp>(_v));
+}
+}  // namespace Experimental
+}  // namespace Kokkos
+
+#endif
diff --git a/packages/kokkos/core/src/KokkosExp_MDRangePolicy.hpp b/packages/kokkos/core/src/KokkosExp_MDRangePolicy.hpp
index b7d8e62f696073bfa4794b362401aaca288de021..dfae7451fc302362743c9349485ee574a15a2d76 100644
--- a/packages/kokkos/core/src/KokkosExp_MDRangePolicy.hpp
+++ b/packages/kokkos/core/src/KokkosExp_MDRangePolicy.hpp
@@ -48,6 +48,7 @@
 #include <initializer_list>
 
 #include <Kokkos_Layout.hpp>
+#include <Kokkos_Rank.hpp>
 #include <Kokkos_Array.hpp>
 #include <impl/KokkosExp_Host_IterateTile.hpp>
 #include <Kokkos_ExecPolicy.hpp>
@@ -78,22 +79,6 @@ struct default_inner_direction {
   static constexpr Iterate value = Iterate::Right;
 };
 
-// Iteration Pattern
-template <unsigned N, Iterate OuterDir = Iterate::Default,
-          Iterate InnerDir = Iterate::Default>
-struct Rank {
-  static_assert(N != 0u, "Kokkos Error: rank 0 undefined");
-  static_assert(N != 1u,
-                "Kokkos Error: rank 1 is not a multi-dimensional range");
-  static_assert(N < 7u, "Kokkos Error: Unsupported rank...");
-
-  using iteration_pattern = Rank<N, OuterDir, InnerDir>;
-
-  static constexpr int rank                = N;
-  static constexpr Iterate outer_direction = OuterDir;
-  static constexpr Iterate inner_direction = InnerDir;
-};
-
 namespace Impl {
 // NOTE the comparison below is encapsulated to silent warnings about pointless
 // comparison of unsigned integer with zero
@@ -397,13 +382,18 @@ struct MDRangePolicy : public Kokkos::Impl::PolicyTraits<Properties...> {
 
 }  // namespace Kokkos
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 // For backward compatibility
 namespace Kokkos {
 namespace Experimental {
-using Kokkos::Iterate;
-using Kokkos::MDRangePolicy;
-using Kokkos::Rank;
+using Iterate KOKKOS_DEPRECATED = Kokkos::Iterate;
+template <typename... Properties>
+using MDRangePolicy KOKKOS_DEPRECATED = Kokkos::MDRangePolicy<Properties...>;
+template <unsigned N, Kokkos::Iterate OuterDir = Kokkos::Iterate::Default,
+          Kokkos::Iterate InnerDir = Kokkos::Iterate::Default>
+using Rank KOKKOS_DEPRECATED = Kokkos::Rank<N, OuterDir, InnerDir>;
 }  // namespace Experimental
 }  // namespace Kokkos
+#endif
 
 #endif  // KOKKOS_CORE_EXP_MD_RANGE_POLICY_HPP
diff --git a/packages/kokkos/core/src/Kokkos_Atomic.hpp b/packages/kokkos/core/src/Kokkos_Atomic.hpp
index 8cd60fa6bae993895ac901fbbab8eb532a6a0ded..a47208e97782ec424a2a96b5ec4de0c58fe2fef2 100644
--- a/packages/kokkos/core/src/Kokkos_Atomic.hpp
+++ b/packages/kokkos/core/src/Kokkos_Atomic.hpp
@@ -69,6 +69,60 @@
 #define KOKKOS_ATOMIC_HPP
 
 #include <Kokkos_Macros.hpp>
+
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+#ifdef KOKKOS_ENABLE_OPENMPTARGET
+#define DESUL_HAVE_OPENMP_ATOMICS
+#endif
+#include <Kokkos_Atomics_Desul_Wrapper.hpp>
+#include <Kokkos_Atomics_Desul_Volatile_Wrapper.hpp>
+#include <impl/Kokkos_Utilities.hpp>
+
+// Helper functions for places where we really should have called SeqCst atomics
+// anyway These can go away when we call desul unconditionally Non-Desul
+// versions are below
+namespace Kokkos {
+namespace Impl {
+using desul::MemoryOrderSeqCst;
+using desul::MemoryScopeDevice;
+
+template <class T>
+KOKKOS_INLINE_FUNCTION void desul_atomic_dec(T* dest, MemoryOrderSeqCst,
+                                             MemoryScopeDevice) {
+  return desul::atomic_dec(const_cast<T*>(dest), desul::MemoryOrderSeqCst(),
+                           desul::MemoryScopeDevice());
+}
+
+template <class T>
+KOKKOS_INLINE_FUNCTION void desul_atomic_inc(T* dest, MemoryOrderSeqCst,
+                                             MemoryScopeDevice) {
+  return desul::atomic_inc(const_cast<T*>(dest), desul::MemoryOrderSeqCst(),
+                           desul::MemoryScopeDevice());
+}
+
+template <class T>
+KOKKOS_INLINE_FUNCTION T
+desul_atomic_exchange(T* dest, const Kokkos::Impl::identity_t<T> val,
+                      MemoryOrderSeqCst, MemoryScopeDevice) {
+  return desul::atomic_exchange(const_cast<T*>(dest), val,
+                                desul::MemoryOrderSeqCst(),
+                                desul::MemoryScopeDevice());
+}
+
+template <class T>
+KOKKOS_INLINE_FUNCTION T desul_atomic_compare_exchange(
+    T* dest, Kokkos::Impl::identity_t<const T> compare,
+    Kokkos::Impl::identity_t<const T> val, MemoryOrderSeqCst,
+    MemoryScopeDevice) {
+  return desul::atomic_compare_exchange(dest, compare, val,
+                                        desul::MemoryOrderSeqCst(),
+                                        desul::MemoryScopeDevice());
+}
+
+}  // namespace Impl
+}  // namespace Kokkos
+#else
+
 #include <Kokkos_HostSpace.hpp>
 #include <impl/Kokkos_Traits.hpp>
 
@@ -326,4 +380,42 @@ inline const char* atomic_query_version() {
 
 //----------------------------------------------------------------------------
 
+// Helper functions for places where we really should have called SeqCst atomics
+// anyway These can go away when we call desul unconditionally
+namespace Kokkos {
+namespace Impl {
+struct MemoryOrderSeqCst {};
+struct MemoryScopeDevice {};
+
+template <class T>
+KOKKOS_INLINE_FUNCTION void desul_atomic_dec(T* dest, MemoryOrderSeqCst,
+                                             MemoryScopeDevice) {
+  return Kokkos::atomic_decrement(dest);
+}
+
+template <class T>
+KOKKOS_INLINE_FUNCTION void desul_atomic_inc(T* dest, MemoryOrderSeqCst,
+                                             MemoryScopeDevice) {
+  return Kokkos::atomic_increment(dest);
+}
+
+template <class T>
+KOKKOS_INLINE_FUNCTION T
+desul_atomic_exchange(T* dest, Kokkos::Impl::identity_t<const T> val,
+                      MemoryOrderSeqCst, MemoryScopeDevice) {
+  return Kokkos::atomic_exchange(dest, val);
+}
+
+template <class T>
+KOKKOS_INLINE_FUNCTION T desul_atomic_compare_exchange(
+    T* dest, Kokkos::Impl::identity_t<const T> compare,
+    Kokkos::Impl::identity_t<const T> val, MemoryOrderSeqCst,
+    MemoryScopeDevice) {
+  return Kokkos::atomic_compare_exchange(dest, compare, val);
+}
+
+}  // namespace Impl
+}  // namespace Kokkos
+
+#endif /* !KOKKOS_ENABLE_IMPL_DESUL_ATOMICS */
 #endif /* KOKKOS_ATOMIC_HPP */
diff --git a/packages/kokkos/core/src/Kokkos_Atomics_Desul_Volatile_Wrapper.hpp b/packages/kokkos/core/src/Kokkos_Atomics_Desul_Volatile_Wrapper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0bcb3ea388beeaf72c862f9519572e5d9e13a530
--- /dev/null
+++ b/packages/kokkos/core/src/Kokkos_Atomics_Desul_Volatile_Wrapper.hpp
@@ -0,0 +1,189 @@
+#ifndef KOKKOS_DESUL_ATOMICS_VOLATILE_WRAPPER_HPP_
+#define KOKKOS_DESUL_ATOMICS_VOLATILE_WRAPPER_HPP_
+#include <Kokkos_Macros.hpp>
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+#include <desul/atomics.hpp>
+
+// clang-format off
+namespace Kokkos { 
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_load(volatile T* const dest) { return desul::atomic_load(const_cast<T*>(dest), desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_store(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_store(const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+// atomic_fetch_op
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_add (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_add (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+#ifdef DESUL_IMPL_ATOMIC_CUDA_USE_DOUBLE_ATOMICADD
+KOKKOS_INLINE_FUNCTION
+double atomic_fetch_add(volatile double* const dest, double val) {
+  #ifdef __CUDA_ARCH__
+  return atomicAdd(const_cast<double*>(dest),val);
+  #else
+  return desul::atomic_fetch_add (const_cast<double*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice());
+  #endif
+};
+
+KOKKOS_INLINE_FUNCTION
+double atomic_fetch_sub(volatile double* const dest, double val) {
+  #ifdef __CUDA_ARCH__
+  return atomicAdd(const_cast<double*>(dest),-val);
+  #else
+  return desul::atomic_fetch_sub (const_cast<double*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice());
+  #endif
+};
+#endif
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_sub (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_sub (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_max (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_max (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_min (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_min (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_mul (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_mul (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_div (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_div (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_mod (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_mod (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_and (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_and (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_or  (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_or  (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_xor (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_xor (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_nand(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_nand(const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_lshift(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_lshift(const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_rshift(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_rshift(const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_inc(volatile T* const dest) { return desul::atomic_fetch_inc(const_cast<T*>(dest),desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_dec(volatile T* const dest) { return desul::atomic_fetch_dec(const_cast<T*>(dest),desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+
+// atomic_op_fetch
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_add_fetch (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_add_fetch (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_sub_fetch (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_sub_fetch (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_max_fetch (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_max_fetch (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_min_fetch (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_min_fetch (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_mul_fetch (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_mul_fetch (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_div_fetch (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_div_fetch (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_mod_fetch (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_mod_fetch (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_and_fetch (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_and_fetch (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_or_fetch  (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_or_fetch  (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_xor_fetch (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_xor_fetch (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_nand_fetch(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_nand_fetch(const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_lshift_fetch(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_lshift_fetch(const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_rshift_fetch(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_rshift_fetch(const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_inc_fetch(volatile T* const dest) { return desul::atomic_inc_fetch(const_cast<T*>(dest),desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_dec_fetch(volatile T* const dest) { return desul::atomic_dec_fetch(const_cast<T*>(dest),desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+
+// atomic_op
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_add(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_add (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_sub(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_sub (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_mul(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_mul (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_div(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_div (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_min(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_min (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_max(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_max (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+// FIXME: Desul doesn't have atomic_and yet so call fetch_and
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_and(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { (void) desul::atomic_fetch_and (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+// FIXME: Desul doesn't have atomic_or yet so call fetch_or
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_or (volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { (void) desul::atomic_fetch_or  (const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_inc(volatile T* const dest) { return desul::atomic_inc(const_cast<T*>(dest),desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_dec(volatile T* const dest) { return desul::atomic_dec(const_cast<T*>(dest),desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_increment(volatile T* const dest) { return desul::atomic_inc(const_cast<T*>(dest),desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_decrement(volatile T* const dest) { return desul::atomic_dec(const_cast<T*>(dest),desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+// Exchange
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_exchange(volatile T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_exchange(const_cast<T*>(dest), val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+bool atomic_compare_exchange_strong(volatile T* const dest, T& expected, const T desired) {
+  return desul::atomic_compare_exchange_strong(const_cast<T*>(dest),expected, desired,
+                  desul::MemoryOrderRelaxed(), desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice());
+}
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_compare_exchange(volatile T* const dest, const T compare, const T desired) {
+  return desul::atomic_compare_exchange(const_cast<T*>(dest),compare, desired,
+                  desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice());
+}
+
+}
+// clang-format on
+#endif  // KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+#endif
diff --git a/packages/kokkos/core/src/Kokkos_Atomics_Desul_Wrapper.hpp b/packages/kokkos/core/src/Kokkos_Atomics_Desul_Wrapper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3a182a6a22b56ca424d13e3d9f0835070f1cb2f6
--- /dev/null
+++ b/packages/kokkos/core/src/Kokkos_Atomics_Desul_Wrapper.hpp
@@ -0,0 +1,271 @@
+#ifndef KOKKOS_DESUL_ATOMICS_WRAPPER_HPP_
+#define KOKKOS_DESUL_ATOMICS_WRAPPER_HPP_
+#include <Kokkos_Macros.hpp>
+
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+#include <desul/atomics.hpp>
+
+#include <impl/Kokkos_Atomic_Memory_Order.hpp>
+#include <impl/Kokkos_Volatile_Load.hpp>
+
+// clang-format off
+namespace Kokkos {
+
+// FIXME: These functions don't have any use/test in unit tests ...
+// ==========================================================
+inline const char* atomic_query_version() { return "KOKKOS_DESUL_ATOMICS"; }
+
+#if defined(KOKKOS_COMPILER_GNU) && !defined(__PGIC__) && \
+    !defined(__CUDA_ARCH__)
+
+#define KOKKOS_NONTEMPORAL_PREFETCH_LOAD(addr) __builtin_prefetch(addr, 0, 0)
+#define KOKKOS_NONTEMPORAL_PREFETCH_STORE(addr) __builtin_prefetch(addr, 1, 0)
+
+#else
+
+#define KOKKOS_NONTEMPORAL_PREFETCH_LOAD(addr) ((void)0)
+#define KOKKOS_NONTEMPORAL_PREFETCH_STORE(addr) ((void)0)
+
+#endif
+// ============================================================
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_load(T* const dest) { return desul::atomic_load(dest, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_store(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_store(dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_assign(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { atomic_store(dest,val); }
+
+KOKKOS_INLINE_FUNCTION
+void memory_fence() {
+  desul::atomic_thread_fence(desul::MemoryOrderSeqCst(), desul::MemoryScopeDevice());
+}
+
+KOKKOS_INLINE_FUNCTION
+void load_fence() { return desul::atomic_thread_fence(desul::MemoryOrderAcquire(), desul::MemoryScopeDevice()); }
+
+KOKKOS_INLINE_FUNCTION
+void store_fence() { return desul::atomic_thread_fence(desul::MemoryOrderRelease(), desul::MemoryScopeDevice()); }
+
+// atomic_fetch_op
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_add (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_add (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+#ifdef DESUL_IMPL_ATOMIC_CUDA_USE_DOUBLE_ATOMICADD
+KOKKOS_INLINE_FUNCTION
+double atomic_fetch_add(double* const dest, double val) {
+  #ifdef __CUDA_ARCH__
+  return atomicAdd(dest,val);
+  #else
+  return desul::atomic_fetch_add (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice());
+  #endif
+};
+
+KOKKOS_INLINE_FUNCTION
+double atomic_fetch_sub(double* const dest, double val) {
+  #ifdef __CUDA_ARCH__
+  return atomicAdd(dest,-val);
+  #else
+  return desul::atomic_fetch_sub (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice());
+  #endif
+};
+#endif
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_sub (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_sub (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_max (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_max (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_min (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_min (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_mul (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_mul (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_div (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_div (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_mod (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_mod (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_and (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_and (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_or  (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_or  (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_xor (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_xor (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_nand(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_nand(dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_lshift(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_lshift(dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_rshift(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_fetch_rshift(dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_inc(T* const dest) { return desul::atomic_fetch_inc(dest, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_fetch_dec(T* const dest) { return desul::atomic_fetch_dec(dest, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+
+// atomic_op_fetch
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_add_fetch (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_add_fetch (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_sub_fetch (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_sub_fetch (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_max_fetch (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_max_fetch (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_min_fetch (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_min_fetch (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_mul_fetch (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_mul_fetch (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_div_fetch (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_div_fetch (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_mod_fetch (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_mod_fetch (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_and_fetch (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_and_fetch (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_or_fetch  (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_or_fetch  (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_xor_fetch (T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_xor_fetch (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_nand_fetch(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_nand_fetch(dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_lshift_fetch(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_lshift_fetch(dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_rshift_fetch(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_rshift_fetch(dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_inc_fetch(T* const dest) { return desul::atomic_inc_fetch(dest, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_dec_fetch(T* const dest) { return desul::atomic_dec_fetch(dest, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+
+// atomic_op
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_add(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_add (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_sub(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_sub (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_mul(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_mul (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_div(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_div (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_min(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_min (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_max(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_max (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+// FIXME: Desul doesn't have atomic_and yet so call fetch_and
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_and(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { (void) desul::atomic_fetch_and (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+// FIXME: Desul doesn't have atomic_or yet so call fetch_or
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_or(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val)  { (void) desul::atomic_fetch_or (dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_inc(T* const dest) { return desul::atomic_inc(dest, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_dec(T* const dest) { return desul::atomic_dec(dest, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_increment(T* const dest) { return desul::atomic_inc(dest, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+void atomic_decrement(T* const dest) { return desul::atomic_dec(dest, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+// Exchange
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_exchange(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> val) { return desul::atomic_exchange(dest, val, desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice()); }
+
+template<class T> KOKKOS_INLINE_FUNCTION
+bool atomic_compare_exchange_strong(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> expected, desul::Impl::dont_deduce_this_parameter_t<const T> desired) {
+  T expected_ref = expected;
+  return desul::atomic_compare_exchange_strong(dest, expected_ref, desired,
+                  desul::MemoryOrderRelaxed(), desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice());
+}
+
+template<class T> KOKKOS_INLINE_FUNCTION
+T atomic_compare_exchange(T* const dest, desul::Impl::dont_deduce_this_parameter_t<const T> compare, desul::Impl::dont_deduce_this_parameter_t<const T> desired) {
+  return desul::atomic_compare_exchange(dest, compare, desired,
+                  desul::MemoryOrderRelaxed(), desul::MemoryScopeDevice());
+}
+
+namespace Impl {
+
+  template<class MemoryOrder>
+  struct KokkosToDesulMemoryOrder;
+
+  template<>
+  struct KokkosToDesulMemoryOrder<memory_order_seq_cst_t> {
+    using type = desul::MemoryOrderSeqCst;
+  };
+  template<>
+  struct KokkosToDesulMemoryOrder<memory_order_acquire_t> {
+    using type = desul::MemoryOrderAcquire;
+  };
+  template<>
+  struct KokkosToDesulMemoryOrder<memory_order_release_t> {
+    using type = desul::MemoryOrderRelease;
+  };
+  template<>
+  struct KokkosToDesulMemoryOrder<memory_order_acq_rel_t> {
+    using type = desul::MemoryOrderAcqRel;
+  };
+  template<>
+  struct KokkosToDesulMemoryOrder<memory_order_relaxed_t> {
+    using type = desul::MemoryOrderRelaxed;
+  };
+  template<class T, class MemOrderSuccess, class MemOrderFailure> KOKKOS_INLINE_FUNCTION
+  bool atomic_compare_exchange_strong(T* const dest, T& expected, const T desired, MemOrderSuccess, MemOrderFailure) {
+    return desul::atomic_compare_exchange_strong(dest, expected, desired,
+                  typename KokkosToDesulMemoryOrder<MemOrderSuccess>::type(),
+                  typename KokkosToDesulMemoryOrder<MemOrderFailure>::type(),
+                  desul::MemoryScopeDevice());
+
+  }
+  template<class T, class MemoryOrder>
+  KOKKOS_INLINE_FUNCTION
+  T atomic_load(const T* const src, MemoryOrder) {
+    return desul::atomic_load(src, typename KokkosToDesulMemoryOrder<MemoryOrder>::type(), desul::MemoryScopeDevice());
+  }
+  template<class T, class MemoryOrder>
+  KOKKOS_INLINE_FUNCTION
+  void atomic_store(T* const src, const T val, MemoryOrder) {
+    return desul::atomic_store(src, val, typename KokkosToDesulMemoryOrder<MemoryOrder>::type(), desul::MemoryScopeDevice());
+  }
+}
+
+}
+// clang-format on
+#endif  // KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+#endif
diff --git a/packages/kokkos/core/src/Kokkos_Complex.hpp b/packages/kokkos/core/src/Kokkos_Complex.hpp
index 6578723fc8e5dab1e605b1a5dc80f1daf4b2ebfb..466903ab7d6626c0cd7ff97754594cc17933367e 100644
--- a/packages/kokkos/core/src/Kokkos_Complex.hpp
+++ b/packages/kokkos/core/src/Kokkos_Complex.hpp
@@ -77,7 +77,7 @@ class
 
   //! Default constructor (initializes both real and imaginary parts to zero).
   KOKKOS_DEFAULTED_FUNCTION
-  complex() noexcept = default;
+  complex() = default;
 
   //! Copy constructor.
   KOKKOS_DEFAULTED_FUNCTION
@@ -150,11 +150,11 @@ class
 
   //! The imaginary part of this complex number.
   KOKKOS_INLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14 RealType& imag() noexcept { return im_; }
+  constexpr RealType& imag() noexcept { return im_; }
 
   //! The real part of this complex number.
   KOKKOS_INLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14 RealType& real() noexcept { return re_; }
+  constexpr RealType& real() noexcept { return re_; }
 
   //! The imaginary part of this complex number.
   KOKKOS_INLINE_FUNCTION
@@ -166,41 +166,39 @@ class
 
   //! Set the imaginary part of this complex number.
   KOKKOS_INLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  void imag(RealType v) noexcept { im_ = v; }
+  constexpr void imag(RealType v) noexcept { im_ = v; }
 
   //! Set the real part of this complex number.
   KOKKOS_INLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  void real(RealType v) noexcept { re_ = v; }
+  constexpr void real(RealType v) noexcept { re_ = v; }
 
-  KOKKOS_CONSTEXPR_14 KOKKOS_INLINE_FUNCTION complex& operator+=(
+  constexpr KOKKOS_INLINE_FUNCTION complex& operator+=(
       const complex<RealType>& src) noexcept {
     re_ += src.re_;
     im_ += src.im_;
     return *this;
   }
 
-  KOKKOS_CONSTEXPR_14 KOKKOS_INLINE_FUNCTION complex& operator+=(
+  constexpr KOKKOS_INLINE_FUNCTION complex& operator+=(
       const RealType& src) noexcept {
     re_ += src;
     return *this;
   }
 
-  KOKKOS_CONSTEXPR_14 KOKKOS_INLINE_FUNCTION complex& operator-=(
+  constexpr KOKKOS_INLINE_FUNCTION complex& operator-=(
       const complex<RealType>& src) noexcept {
     re_ -= src.re_;
     im_ -= src.im_;
     return *this;
   }
 
-  KOKKOS_CONSTEXPR_14 KOKKOS_INLINE_FUNCTION complex& operator-=(
+  constexpr KOKKOS_INLINE_FUNCTION complex& operator-=(
       const RealType& src) noexcept {
     re_ -= src;
     return *this;
   }
 
-  KOKKOS_CONSTEXPR_14 KOKKOS_INLINE_FUNCTION complex& operator*=(
+  constexpr KOKKOS_INLINE_FUNCTION complex& operator*=(
       const complex<RealType>& src) noexcept {
     const RealType realPart = re_ * src.re_ - im_ * src.im_;
     const RealType imagPart = re_ * src.im_ + im_ * src.re_;
@@ -209,7 +207,7 @@ class
     return *this;
   }
 
-  KOKKOS_CONSTEXPR_14 KOKKOS_INLINE_FUNCTION complex& operator*=(
+  constexpr KOKKOS_INLINE_FUNCTION complex& operator*=(
       const RealType& src) noexcept {
     re_ *= src;
     im_ *= src;
@@ -217,7 +215,7 @@ class
   }
 
   // Conditional noexcept, just in case RType throws on divide-by-zero
-  KOKKOS_CONSTEXPR_14 KOKKOS_INLINE_FUNCTION complex& operator/=(
+  constexpr KOKKOS_INLINE_FUNCTION complex& operator/=(
       const complex<RealType>& y) noexcept(noexcept(RealType{} / RealType{})) {
     using Kokkos::Experimental::fabs;
     // Scale (by the "1-norm" of y) to avoid unwarranted overflow.
@@ -244,8 +242,7 @@ class
     return *this;
   }
 
-  KOKKOS_CONSTEXPR_14
-  KOKKOS_INLINE_FUNCTION complex& operator/=(
+  constexpr KOKKOS_INLINE_FUNCTION complex& operator/=(
       const std::complex<RealType>& y) noexcept(noexcept(RealType{} /
                                                          RealType{})) {
     using Kokkos::Experimental::fabs;
@@ -272,7 +269,7 @@ class
     return *this;
   }
 
-  KOKKOS_CONSTEXPR_14 KOKKOS_INLINE_FUNCTION complex& operator/=(
+  constexpr KOKKOS_INLINE_FUNCTION complex& operator/=(
       const RealType& src) noexcept(noexcept(RealType{} / RealType{})) {
     re_ /= src;
     im_ /= src;
@@ -688,12 +685,24 @@ KOKKOS_INLINE_FUNCTION RealType imag(const complex<RealType>& x) noexcept {
   return x.imag();
 }
 
+template <class ArithmeticType>
+KOKKOS_INLINE_FUNCTION constexpr Impl::promote_t<ArithmeticType> imag(
+    ArithmeticType) {
+  return ArithmeticType();
+}
+
 //! Real part of a complex number.
 template <class RealType>
 KOKKOS_INLINE_FUNCTION RealType real(const complex<RealType>& x) noexcept {
   return x.real();
 }
 
+template <class ArithmeticType>
+KOKKOS_INLINE_FUNCTION constexpr Impl::promote_t<ArithmeticType> real(
+    ArithmeticType x) {
+  return x;
+}
+
 //! Constructs a complex number from magnitude and phase angle
 template <class T>
 KOKKOS_INLINE_FUNCTION complex<T> polar(const T& r, const T& theta = T()) {
@@ -733,36 +742,6 @@ KOKKOS_INLINE_FUNCTION complex<T> pow(const complex<T>& x,
   return x == T() ? T() : exp(y * log(x));
 }
 
-namespace Impl {
-// NOTE promote would also be useful for math functions
-template <class T, bool = std::is_integral<T>::value>
-struct promote {
-  using type = double;
-};
-template <class T>
-struct promote<T, false> {};
-template <>
-struct promote<long double> {
-  using type = long double;
-};
-template <>
-struct promote<double> {
-  using type = double;
-};
-template <>
-struct promote<float> {
-  using type = float;
-};
-template <class T>
-using promote_t = typename promote<T>::type;
-template <class T, class U>
-struct promote_2 {
-  using type = decltype(promote_t<T>() + promote_t<U>());
-};
-template <class T, class U>
-using promote_2_t = typename promote_2<T, U>::type;
-}  // namespace Impl
-
 template <class T, class U,
           class = std::enable_if_t<std::is_arithmetic<T>::value>>
 KOKKOS_INLINE_FUNCTION complex<Impl::promote_2_t<T, U>> pow(
@@ -816,6 +795,13 @@ KOKKOS_INLINE_FUNCTION complex<RealType> conj(
   return complex<RealType>(real(x), -imag(x));
 }
 
+template <class ArithmeticType>
+KOKKOS_INLINE_FUNCTION constexpr complex<Impl::promote_t<ArithmeticType>> conj(
+    ArithmeticType x) {
+  using type = Impl::promote_t<ArithmeticType>;
+  return complex<type>(x, -type());
+}
+
 //! Exponential of a complex number.
 template <class RealType>
 KOKKOS_INLINE_FUNCTION complex<RealType> exp(const complex<RealType>& x) {
diff --git a/packages/kokkos/core/src/Kokkos_Concepts.hpp b/packages/kokkos/core/src/Kokkos_Concepts.hpp
index 2aba189487490d4f870cec407ec1d1f3b9ed001e..97137387f264a869dacb6b3b5abdd7fa5a9ba5ff 100644
--- a/packages/kokkos/core/src/Kokkos_Concepts.hpp
+++ b/packages/kokkos/core/src/Kokkos_Concepts.hpp
@@ -180,20 +180,23 @@ KOKKOS_IMPL_IS_CONCEPT(work_item_property)
 
 namespace Impl {
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 // For backward compatibility:
 
-using Kokkos::is_array_layout;
-using Kokkos::is_execution_policy;
-using Kokkos::is_execution_space;
-using Kokkos::is_memory_space;
-using Kokkos::is_memory_traits;
+template <typename T>
+using is_array_layout KOKKOS_DEPRECATED = Kokkos::is_array_layout<T>;
+template <typename T>
+using is_execution_policy KOKKOS_DEPRECATED = Kokkos::is_execution_policy<T>;
+template <typename T>
+using is_execution_space KOKKOS_DEPRECATED = Kokkos::is_execution_space<T>;
+template <typename T>
+using is_memory_space KOKKOS_DEPRECATED = Kokkos::is_memory_space<T>;
+template <typename T>
+using is_memory_traits KOKKOS_DEPRECATED = Kokkos::is_memory_traits<T>;
+#endif
 
 // Implementation concept:
 
-KOKKOS_IMPL_IS_CONCEPT(iteration_pattern)
-KOKKOS_IMPL_IS_CONCEPT(schedule_type)
-KOKKOS_IMPL_IS_CONCEPT(index_type)
-KOKKOS_IMPL_IS_CONCEPT(launch_bounds)
 KOKKOS_IMPL_IS_CONCEPT(thread_team_member)
 KOKKOS_IMPL_IS_CONCEPT(host_thread_team_member)
 KOKKOS_IMPL_IS_CONCEPT(graph_kernel)
@@ -330,42 +333,65 @@ struct is_space {
   // For backward compatibility, deprecated in favor of
   // Kokkos::Impl::HostMirror<S>::host_mirror_space
 
-  using host_memory_space = typename std::conditional<
+ private:
+  // The actual definitions for host_memory_space and host_execution_spaces are
+  // in do_not_use_host_memory_space and do_not_use_host_execution_space to be
+  // able to use them within this class without deprecation warnings.
+  using do_not_use_host_memory_space = std::conditional_t<
       std::is_same<memory_space, Kokkos::HostSpace>::value
 #if defined(KOKKOS_ENABLE_CUDA)
           || std::is_same<memory_space, Kokkos::CudaUVMSpace>::value ||
           std::is_same<memory_space, Kokkos::CudaHostPinnedSpace>::value
-#endif /* #if defined( KOKKOS_ENABLE_CUDA ) */
+#elif defined(KOKKOS_ENABLE_HIP)
+          || std::is_same<memory_space,
+                          Kokkos::Experimental::HIPHostPinnedSpace>::value
+#elif defined(KOKKOS_ENABLE_SYCL)
+          || std::is_same<memory_space,
+                          Kokkos::Experimental::SYCLSharedUSMSpace>::value ||
+          std::is_same<memory_space,
+                       Kokkos::Experimental::SYCLHostUSMSpace>::value
+#endif
       ,
-      memory_space, Kokkos::HostSpace>::type;
+      memory_space, Kokkos::HostSpace>;
 
+  using do_not_use_host_execution_space = std::conditional_t<
 #if defined(KOKKOS_ENABLE_CUDA)
-  using host_execution_space = typename std::conditional<
-      std::is_same<execution_space, Kokkos::Cuda>::value,
-      Kokkos::DefaultHostExecutionSpace, execution_space>::type;
-#else
-#if defined(KOKKOS_ENABLE_OPENMPTARGET)
-  using host_execution_space = typename std::conditional<
-      std::is_same<execution_space, Kokkos::Experimental::OpenMPTarget>::value,
-      Kokkos::DefaultHostExecutionSpace, execution_space>::type;
-#else
-  using host_execution_space = execution_space;
-#endif
+      std::is_same<execution_space, Kokkos::Cuda>::value ||
+#elif defined(KOKKOS_ENABLE_HIP)
+      std::is_same<execution_space, Kokkos::Experimental::HIP>::value ||
+#elif defined(KOKKOS_ENABLE_SYCL)
+      std::is_same<execution_space, Kokkos::Experimental::SYCL>::value ||
+#elif defined(KOKKOS_ENABLE_OPENMPTARGET)
+      std::is_same<execution_space,
+                   Kokkos::Experimental::OpenMPTarget>::value ||
 #endif
+          false,
+      Kokkos::DefaultHostExecutionSpace, execution_space>;
 
-  using host_mirror_space = typename std::conditional<
-      std::is_same<execution_space, host_execution_space>::value &&
-          std::is_same<memory_space, host_memory_space>::value,
-      T, Kokkos::Device<host_execution_space, host_memory_space>>::type;
+ public:
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
+  using host_memory_space KOKKOS_DEPRECATED = do_not_use_host_memory_space;
+  using host_execution_space KOKKOS_DEPRECATED =
+      do_not_use_host_execution_space;
+  using host_mirror_space KOKKOS_DEPRECATED = std::conditional_t<
+      std::is_same<execution_space, do_not_use_host_execution_space>::value &&
+          std::is_same<memory_space, do_not_use_host_memory_space>::value,
+      T,
+      Kokkos::Device<do_not_use_host_execution_space,
+                     do_not_use_host_memory_space>>;
+#endif
 };
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 // For backward compatibility
 
 namespace Impl {
 
-using Kokkos::is_space;
+template <typename T>
+using is_space KOKKOS_DEPRECATED = Kokkos::is_space<T>;
 
 }
+#endif
 
 }  // namespace Kokkos
 
@@ -485,13 +511,18 @@ struct SpaceAccessibility {
 
 }  // namespace Kokkos
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 namespace Kokkos {
 namespace Impl {
 
-using Kokkos::SpaceAccessibility;  // For backward compatibility
+// For backward compatibility
+template <typename AccessSpace, typename MemorySpace>
+using SpaceAccessibility KOKKOS_DEPRECATED =
+    Kokkos::SpaceAccessibility<AccessSpace, MemorySpace>;
 
-}
+}  // namespace Impl
 }  // namespace Kokkos
+#endif
 
 //----------------------------------------------------------------------------
 
diff --git a/packages/kokkos/core/src/Kokkos_CopyViews.hpp b/packages/kokkos/core/src/Kokkos_CopyViews.hpp
index a27d5f0e47284f7d06b3d9218d1f02bfb679468e..16946dd602b536793f8746165ebd4bb82631742b 100644
--- a/packages/kokkos/core/src/Kokkos_CopyViews.hpp
+++ b/packages/kokkos/core/src/Kokkos_CopyViews.hpp
@@ -47,6 +47,7 @@
 #include <string>
 #include <Kokkos_Parallel.hpp>
 #include <KokkosExp_MDRangePolicy.hpp>
+#include <Kokkos_Layout.hpp>
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
@@ -544,13 +545,11 @@ void view_copy(const ExecutionSpace& space, const DstType& dst,
 
   enum {
     ExecCanAccessSrc =
-        Kokkos::Impl::SpaceAccessibility<ExecutionSpace,
-                                         src_memory_space>::accessible
+        Kokkos::SpaceAccessibility<ExecutionSpace, src_memory_space>::accessible
   };
   enum {
     ExecCanAccessDst =
-        Kokkos::Impl::SpaceAccessibility<ExecutionSpace,
-                                         dst_memory_space>::accessible
+        Kokkos::SpaceAccessibility<ExecutionSpace, dst_memory_space>::accessible
   };
 
   if (!(ExecCanAccessSrc && ExecCanAccessDst)) {
@@ -624,14 +623,14 @@ void view_copy(const DstType& dst, const SrcType& src) {
 
   enum {
     DstExecCanAccessSrc =
-        Kokkos::Impl::SpaceAccessibility<dst_execution_space,
-                                         src_memory_space>::accessible
+        Kokkos::SpaceAccessibility<dst_execution_space,
+                                   src_memory_space>::accessible
   };
 
   enum {
     SrcExecCanAccessDst =
-        Kokkos::Impl::SpaceAccessibility<src_execution_space,
-                                         dst_memory_space>::accessible
+        Kokkos::SpaceAccessibility<src_execution_space,
+                                   dst_memory_space>::accessible
   };
 
   if (!DstExecCanAccessSrc && !SrcExecCanAccessDst) {
@@ -1254,6 +1253,98 @@ struct ViewRemap<DstType, SrcType, ExecSpace, 8> {
   }
 };
 
+template <typename ExecutionSpace, class DT, class... DP>
+inline void contiguous_fill(
+    const ExecutionSpace& exec_space, const View<DT, DP...>& dst,
+    typename ViewTraits<DT, DP...>::const_value_type& value) {
+  using ViewType     = View<DT, DP...>;
+  using ViewTypeFlat = Kokkos::View<
+      typename ViewType::value_type*, Kokkos::LayoutRight,
+      Kokkos::Device<typename ViewType::execution_space,
+                     typename std::conditional<ViewType::Rank == 0,
+                                               typename ViewType::memory_space,
+                                               Kokkos::AnonymousSpace>::type>,
+      Kokkos::MemoryTraits<0>>;
+
+  ViewTypeFlat dst_flat(dst.data(), dst.size());
+  if (dst.span() < static_cast<size_t>(std::numeric_limits<int>::max())) {
+    Kokkos::Impl::ViewFill<ViewTypeFlat, Kokkos::LayoutRight, ExecutionSpace,
+                           ViewTypeFlat::Rank, int>(dst_flat, value,
+                                                    exec_space);
+  } else
+    Kokkos::Impl::ViewFill<ViewTypeFlat, Kokkos::LayoutRight, ExecutionSpace,
+                           ViewTypeFlat::Rank, int64_t>(dst_flat, value,
+                                                        exec_space);
+}
+
+template <typename ExecutionSpace, class DT, class... DP>
+struct ZeroMemset {
+  ZeroMemset(const ExecutionSpace& exec_space, const View<DT, DP...>& dst,
+             typename ViewTraits<DT, DP...>::const_value_type& value) {
+    contiguous_fill(exec_space, dst, value);
+  }
+
+  ZeroMemset(const View<DT, DP...>& dst,
+             typename ViewTraits<DT, DP...>::const_value_type& value) {
+    contiguous_fill(ExecutionSpace(), dst, value);
+  }
+};
+
+template <typename ExecutionSpace, class DT, class... DP>
+inline std::enable_if_t<
+    std::is_trivial<typename ViewTraits<DT, DP...>::const_value_type>::value &&
+    std::is_trivially_copy_assignable<
+        typename ViewTraits<DT, DP...>::const_value_type>::value>
+contiguous_fill_or_memset(
+    const ExecutionSpace& exec_space, const View<DT, DP...>& dst,
+    typename ViewTraits<DT, DP...>::const_value_type& value) {
+  if (Impl::is_zero_byte(value))
+    ZeroMemset<ExecutionSpace, DT, DP...>(exec_space, dst, value);
+  else
+    contiguous_fill(exec_space, dst, value);
+}
+
+template <typename ExecutionSpace, class DT, class... DP>
+inline std::enable_if_t<!(
+    std::is_trivial<typename ViewTraits<DT, DP...>::const_value_type>::value &&
+    std::is_trivially_copy_assignable<
+        typename ViewTraits<DT, DP...>::const_value_type>::value)>
+contiguous_fill_or_memset(
+    const ExecutionSpace& exec_space, const View<DT, DP...>& dst,
+    typename ViewTraits<DT, DP...>::const_value_type& value) {
+  contiguous_fill(exec_space, dst, value);
+}
+
+template <class DT, class... DP>
+inline std::enable_if_t<
+    std::is_trivial<typename ViewTraits<DT, DP...>::const_value_type>::value &&
+    std::is_trivially_copy_assignable<
+        typename ViewTraits<DT, DP...>::const_value_type>::value>
+contiguous_fill_or_memset(
+    const View<DT, DP...>& dst,
+    typename ViewTraits<DT, DP...>::const_value_type& value) {
+  using ViewType        = View<DT, DP...>;
+  using exec_space_type = typename ViewType::execution_space;
+
+  if (Impl::is_zero_byte(value))
+    ZeroMemset<exec_space_type, DT, DP...>(dst, value);
+  else
+    contiguous_fill(exec_space_type(), dst, value);
+}
+
+template <class DT, class... DP>
+inline std::enable_if_t<!(
+    std::is_trivial<typename ViewTraits<DT, DP...>::const_value_type>::value &&
+    std::is_trivially_copy_assignable<
+        typename ViewTraits<DT, DP...>::const_value_type>::value)>
+contiguous_fill_or_memset(
+    const View<DT, DP...>& dst,
+    typename ViewTraits<DT, DP...>::const_value_type& value) {
+  using ViewType        = View<DT, DP...>;
+  using exec_space_type = typename ViewType::execution_space;
+
+  contiguous_fill(exec_space_type(), dst, value);
+}
 }  // namespace Impl
 
 /** \brief  Deep copy a value from Host memory into a view.  */
@@ -1276,38 +1367,23 @@ inline void deep_copy(
   }
 
   if (dst.data() == nullptr) {
-    Kokkos::fence();
+    Kokkos::fence(
+        "Kokkos::deep_copy: scalar copy, fence because destination is null");
     if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
       Kokkos::Profiling::endDeepCopy();
     }
     return;
   }
 
-  Kokkos::fence();
+  Kokkos::fence("Kokkos::deep_copy: scalar copy, pre copy fence");
   static_assert(std::is_same<typename ViewType::non_const_value_type,
                              typename ViewType::value_type>::value,
                 "deep_copy requires non-const type");
 
-  // If contiguous we can simply do a 1D flat loop
+  // If contiguous we can simply do a 1D flat loop or use memset
   if (dst.span_is_contiguous()) {
-    using ViewTypeFlat = Kokkos::View<
-        typename ViewType::value_type*, Kokkos::LayoutRight,
-        Kokkos::Device<typename ViewType::execution_space,
-                       typename std::conditional<
-                           ViewType::Rank == 0, typename ViewType::memory_space,
-                           Kokkos::AnonymousSpace>::type>,
-        Kokkos::MemoryTraits<0>>;
-
-    ViewTypeFlat dst_flat(dst.data(), dst.size());
-    if (dst.span() < static_cast<size_t>(std::numeric_limits<int>::max())) {
-      Kokkos::Impl::ViewFill<ViewTypeFlat, Kokkos::LayoutRight, exec_space_type,
-                             ViewTypeFlat::Rank, int>(dst_flat, value,
-                                                      exec_space_type());
-    } else
-      Kokkos::Impl::ViewFill<ViewTypeFlat, Kokkos::LayoutRight, exec_space_type,
-                             ViewTypeFlat::Rank, int64_t>(dst_flat, value,
-                                                          exec_space_type());
-    Kokkos::fence();
+    Impl::contiguous_fill_or_memset(dst, value);
+    Kokkos::fence("Kokkos::deep_copy: scalar copy, post copy fence");
     if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
       Kokkos::Profiling::endDeepCopy();
     }
@@ -1362,7 +1438,7 @@ inline void deep_copy(
                              exec_space_type, ViewType::Rank, int>(
           dst, value, exec_space_type());
   }
-  Kokkos::fence();
+  Kokkos::fence("Kokkos::deep_copy: scalar copy, post copy fence");
 
   if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
     Kokkos::Profiling::endDeepCopy();
@@ -1393,7 +1469,7 @@ inline void deep_copy(
   }
 
   if (src.data() == nullptr) {
-    Kokkos::fence();
+    Kokkos::fence("Kokkos::deep_copy: copy into scalar, src is null");
     if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
       Kokkos::Profiling::endDeepCopy();
     }
@@ -1439,18 +1515,19 @@ inline void deep_copy(
   }
 
   if (dst.data() == nullptr && src.data() == nullptr) {
-    Kokkos::fence();
+    Kokkos::fence(
+        "Kokkos::deep_copy: scalar to scalar copy, both pointers null");
     if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
       Kokkos::Profiling::endDeepCopy();
     }
     return;
   }
 
-  Kokkos::fence();
+  Kokkos::fence("Kokkos::deep_copy: scalar to scalar copy, pre copy fence");
   if (dst.data() != src.data()) {
     Kokkos::Impl::DeepCopy<dst_memory_space, src_memory_space>(
         dst.data(), src.data(), sizeof(value_type));
-    Kokkos::fence();
+    Kokkos::fence("Kokkos::deep_copy: scalar to scalar copy, post copy fence");
   }
   if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
     Kokkos::Profiling::endDeepCopy();
@@ -1522,7 +1599,9 @@ inline void deep_copy(
 
       Kokkos::Impl::throw_runtime_exception(message);
     }
-    Kokkos::fence();
+    Kokkos::fence(
+        "Kokkos::deep_copy: copy between contiguous views, fence due to null "
+        "argument");
     if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
       Kokkos::Profiling::endDeepCopy();
     }
@@ -1531,14 +1610,14 @@ inline void deep_copy(
 
   enum {
     DstExecCanAccessSrc =
-        Kokkos::Impl::SpaceAccessibility<dst_execution_space,
-                                         src_memory_space>::accessible
+        Kokkos::SpaceAccessibility<dst_execution_space,
+                                   src_memory_space>::accessible
   };
 
   enum {
     SrcExecCanAccessDst =
-        Kokkos::Impl::SpaceAccessibility<src_execution_space,
-                                         dst_memory_space>::accessible
+        Kokkos::SpaceAccessibility<src_execution_space,
+                                   dst_memory_space>::accessible
   };
 
   // Checking for Overlapping Views.
@@ -1549,7 +1628,9 @@ inline void deep_copy(
   if (((std::ptrdiff_t)dst_start == (std::ptrdiff_t)src_start) &&
       ((std::ptrdiff_t)dst_end == (std::ptrdiff_t)src_end) &&
       (dst.span_is_contiguous() && src.span_is_contiguous())) {
-    Kokkos::fence();
+    Kokkos::fence(
+        "Kokkos::deep_copy: copy between contiguous views, fence due to same "
+        "spans");
     if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
       Kokkos::Profiling::endDeepCopy();
     }
@@ -1620,16 +1701,22 @@ inline void deep_copy(
       ((dst_type::rank < 7) || (dst.stride_6() == src.stride_6())) &&
       ((dst_type::rank < 8) || (dst.stride_7() == src.stride_7()))) {
     const size_t nbytes = sizeof(typename dst_type::value_type) * dst.span();
-    Kokkos::fence();
+    Kokkos::fence(
+        "Kokkos::deep_copy: copy between contiguous views, pre view equality "
+        "check");
     if ((void*)dst.data() != (void*)src.data()) {
       Kokkos::Impl::DeepCopy<dst_memory_space, src_memory_space>(
           dst.data(), src.data(), nbytes);
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::deep_copy: copy between contiguous views, post deep copy "
+          "fence");
     }
   } else {
-    Kokkos::fence();
+    Kokkos::fence(
+        "Kokkos::deep_copy: copy between contiguous views, pre copy fence");
     Impl::view_copy(dst, src);
-    Kokkos::fence();
+    Kokkos::fence(
+        "Kokkos::deep_copy: copy between contiguous views, post copy fence");
   }
   if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
     Kokkos::Profiling::endDeepCopy();
@@ -2418,9 +2505,9 @@ inline void deep_copy(
     const ExecSpace& space, const View<DT, DP...>& dst,
     typename ViewTraits<DT, DP...>::const_value_type& value,
     typename std::enable_if<
-        Kokkos::Impl::is_execution_space<ExecSpace>::value &&
+        Kokkos::is_execution_space<ExecSpace>::value &&
         std::is_same<typename ViewTraits<DT, DP...>::specialize, void>::value &&
-        Kokkos::Impl::SpaceAccessibility<
+        Kokkos::SpaceAccessibility<
             ExecSpace,
             typename ViewTraits<DT, DP...>::memory_space>::accessible>::type* =
         nullptr) {
@@ -2437,7 +2524,9 @@ inline void deep_copy(
         "(none)", &value, dst.span() * sizeof(typename dst_traits::value_type));
   }
   if (dst.data() == nullptr) {
-    space.fence();
+    space.fence("Kokkos::deep_copy: scalar copy on space, dst data is null");
+  } else if (dst.span_is_contiguous()) {
+    Impl::contiguous_fill_or_memset(space, dst, value);
   } else {
     using ViewTypeUniform = typename std::conditional<
         View<DT, DP...>::Rank == 0,
@@ -2458,9 +2547,9 @@ inline void deep_copy(
     const ExecSpace& space, const View<DT, DP...>& dst,
     typename ViewTraits<DT, DP...>::const_value_type& value,
     typename std::enable_if<
-        Kokkos::Impl::is_execution_space<ExecSpace>::value &&
+        Kokkos::is_execution_space<ExecSpace>::value &&
         std::is_same<typename ViewTraits<DT, DP...>::specialize, void>::value &&
-        !Kokkos::Impl::SpaceAccessibility<
+        !Kokkos::SpaceAccessibility<
             ExecSpace,
             typename ViewTraits<DT, DP...>::memory_space>::accessible>::type* =
         nullptr) {
@@ -2477,17 +2566,23 @@ inline void deep_copy(
         "(none)", &value, dst.span() * sizeof(typename dst_traits::value_type));
   }
   if (dst.data() == nullptr) {
-    space.fence();
+    space.fence(
+        "Kokkos::deep_copy: scalar-to-view copy on space, dst data is null");
   } else {
-    space.fence();
-    using ViewTypeUniform = typename std::conditional<
-        View<DT, DP...>::Rank == 0,
-        typename View<DT, DP...>::uniform_runtime_type,
-        typename View<DT, DP...>::uniform_runtime_nomemspace_type>::type;
+    space.fence("Kokkos::deep_copy: scalar-to-view copy on space, pre copy");
     using fill_exec_space = typename dst_traits::memory_space::execution_space;
-    Kokkos::Impl::ViewFill<ViewTypeUniform, typename dst_traits::array_layout,
-                           fill_exec_space>(dst, value, fill_exec_space());
-    fill_exec_space().fence();
+    if (dst.span_is_contiguous()) {
+      Impl::contiguous_fill_or_memset(fill_exec_space(), dst, value);
+    } else {
+      using ViewTypeUniform = typename std::conditional<
+          View<DT, DP...>::Rank == 0,
+          typename View<DT, DP...>::uniform_runtime_type,
+          typename View<DT, DP...>::uniform_runtime_nomemspace_type>::type;
+      Kokkos::Impl::ViewFill<ViewTypeUniform, typename dst_traits::array_layout,
+                             fill_exec_space>(dst, value, fill_exec_space());
+    }
+    fill_exec_space().fence(
+        "Kokkos::deep_copy: scalar-to-view copy on space, fence after fill");
   }
   if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
     Kokkos::Profiling::endDeepCopy();
@@ -2501,7 +2596,7 @@ inline void deep_copy(
     typename ViewTraits<ST, SP...>::non_const_value_type& dst,
     const View<ST, SP...>& src,
     typename std::enable_if<
-        Kokkos::Impl::is_execution_space<ExecSpace>::value &&
+        Kokkos::is_execution_space<ExecSpace>::value &&
         std::is_same<typename ViewTraits<ST, SP...>::specialize,
                      void>::value>::type* = nullptr) {
   using src_traits       = ViewTraits<ST, SP...>;
@@ -2517,7 +2612,8 @@ inline void deep_copy(
   }
 
   if (src.data() == nullptr) {
-    exec_space.fence();
+    exec_space.fence(
+        "Kokkos::deep_copy: view-to-scalar copy on space, src data is null");
     if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
       Kokkos::Profiling::endDeepCopy();
     }
@@ -2538,7 +2634,7 @@ inline void deep_copy(
     const ExecSpace& exec_space, const View<DT, DP...>& dst,
     const View<ST, SP...>& src,
     typename std::enable_if<(
-        Kokkos::Impl::is_execution_space<ExecSpace>::value &&
+        Kokkos::is_execution_space<ExecSpace>::value &&
         std::is_same<typename ViewTraits<DT, DP...>::specialize, void>::value &&
         std::is_same<typename ViewTraits<ST, SP...>::specialize, void>::value &&
         (unsigned(ViewTraits<DT, DP...>::rank) == unsigned(0) &&
@@ -2562,7 +2658,8 @@ inline void deep_copy(
   }
 
   if (dst.data() == nullptr && src.data() == nullptr) {
-    exec_space.fence();
+    exec_space.fence(
+        "Kokkos::deep_copy: view-to-view copy on space, data is null");
     if (Kokkos::Tools::Experimental::get_callbacks().end_deep_copy != nullptr) {
       Kokkos::Profiling::endDeepCopy();
     }
@@ -2588,7 +2685,7 @@ inline void deep_copy(
     const ExecSpace& exec_space, const View<DT, DP...>& dst,
     const View<ST, SP...>& src,
     typename std::enable_if<(
-        Kokkos::Impl::is_execution_space<ExecSpace>::value &&
+        Kokkos::is_execution_space<ExecSpace>::value &&
         std::is_same<typename ViewTraits<DT, DP...>::specialize, void>::value &&
         std::is_same<typename ViewTraits<ST, SP...>::specialize, void>::value &&
         (unsigned(ViewTraits<DT, DP...>::rank) != 0 ||
@@ -2662,21 +2759,19 @@ inline void deep_copy(
 
   enum {
     ExecCanAccessSrcDst =
-        Kokkos::Impl::SpaceAccessibility<ExecSpace,
-                                         dst_memory_space>::accessible &&
-        Kokkos::Impl::SpaceAccessibility<ExecSpace,
-                                         src_memory_space>::accessible
+        Kokkos::SpaceAccessibility<ExecSpace, dst_memory_space>::accessible &&
+        Kokkos::SpaceAccessibility<ExecSpace, src_memory_space>::accessible
   };
   enum {
     DstExecCanAccessSrc =
-        Kokkos::Impl::SpaceAccessibility<dst_execution_space,
-                                         src_memory_space>::accessible
+        Kokkos::SpaceAccessibility<dst_execution_space,
+                                   src_memory_space>::accessible
   };
 
   enum {
     SrcExecCanAccessDst =
-        Kokkos::Impl::SpaceAccessibility<src_execution_space,
-                                         dst_memory_space>::accessible
+        Kokkos::SpaceAccessibility<src_execution_space,
+                                   dst_memory_space>::accessible
   };
 
   // Error out for non-identical overlapping views.
@@ -2757,9 +2852,13 @@ inline void deep_copy(
       using cpy_exec_space =
           typename std::conditional<DstExecCanAccessSrc, dst_execution_space,
                                     src_execution_space>::type;
-      exec_space.fence();
+      exec_space.fence(
+          "Kokkos::deep_copy: view-to-view noncontiguous copy on space, pre "
+          "copy");
       Impl::view_copy(cpy_exec_space(), dst, src);
-      cpy_exec_space().fence();
+      cpy_exec_space().fence(
+          "Kokkos::deep_copy: view-to-view noncontiguous copy on space, post "
+          "copy");
     } else {
       Kokkos::Impl::throw_runtime_exception(
           "deep_copy given views that would require a temporary allocation");
@@ -2777,6 +2876,19 @@ inline void deep_copy(
 
 namespace Kokkos {
 
+namespace Impl {
+template <typename ViewType>
+bool size_mismatch(const ViewType& view, unsigned int max_extent,
+                   const size_t new_extents[8]) {
+  for (unsigned int dim = 0; dim < max_extent; ++dim)
+    if (new_extents[dim] != view.extent(dim)) {
+      return true;
+    }
+  return false;
+}
+
+}  // namespace Impl
+
 /** \brief  Resize a view with copying old data to new data at the corresponding
  * indices. */
 template <class T, class... P>
@@ -2798,67 +2910,6 @@ resize(Kokkos::View<T, P...>& v, const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
   static_assert(Kokkos::ViewTraits<T, P...>::is_managed,
                 "Can only resize managed views");
 
-  // Fix #904 by checking dimensions before actually resizing.
-  //
-  // Rank is known at compile time, so hopefully the compiler will
-  // remove branches that are compile-time false.  The upcoming "if
-  // constexpr" language feature would make this certain.
-  if (view_type::Rank == 1 && n0 == static_cast<size_t>(v.extent(0))) {
-    return;
-  }
-  if (view_type::Rank == 2 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1))) {
-    return;
-  }
-  if (view_type::Rank == 3 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2))) {
-    return;
-  }
-  if (view_type::Rank == 4 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3))) {
-    return;
-  }
-  if (view_type::Rank == 5 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3)) &&
-      n4 == static_cast<size_t>(v.extent(4))) {
-    return;
-  }
-  if (view_type::Rank == 6 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3)) &&
-      n4 == static_cast<size_t>(v.extent(4)) &&
-      n5 == static_cast<size_t>(v.extent(5))) {
-    return;
-  }
-  if (view_type::Rank == 7 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3)) &&
-      n4 == static_cast<size_t>(v.extent(4)) &&
-      n5 == static_cast<size_t>(v.extent(5)) &&
-      n6 == static_cast<size_t>(v.extent(6))) {
-    return;
-  }
-  if (view_type::Rank == 8 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3)) &&
-      n4 == static_cast<size_t>(v.extent(4)) &&
-      n5 == static_cast<size_t>(v.extent(5)) &&
-      n6 == static_cast<size_t>(v.extent(6)) &&
-      n7 == static_cast<size_t>(v.extent(7))) {
-    return;
-  }
-  // If Kokkos ever supports Views of rank > 8, the above code won't
-  // be incorrect, because avoiding reallocation in resize() is just
-  // an optimization.
-
   // TODO (mfh 27 Jun 2017) If the old View has enough space but just
   // different dimensions (e.g., if the product of the dimensions,
   // including extra space for alignment, will not change), then
@@ -2866,11 +2917,17 @@ resize(Kokkos::View<T, P...>& v, const size_t n0 = KOKKOS_IMPL_CTOR_DEFAULT_ARG,
   // reallocates if any of the dimensions change, even if the old View
   // has enough space.
 
-  view_type v_resized(v.label(), n0, n1, n2, n3, n4, n5, n6, n7);
+  const size_t new_extents[8] = {n0, n1, n2, n3, n4, n5, n6, n7};
+  const bool sizeMismatch = Impl::size_mismatch(v, v.rank_dynamic, new_extents);
 
-  Kokkos::Impl::ViewRemap<view_type, view_type>(v_resized, v);
+  if (sizeMismatch) {
+    view_type v_resized(v.label(), n0, n1, n2, n3, n4, n5, n6, n7);
 
-  v = v_resized;
+    Kokkos::Impl::ViewRemap<view_type, view_type>(v_resized, v);
+    Kokkos::fence("Kokkos::resize(View)");
+
+    v = v_resized;
+  }
 }
 
 /** \brief  Resize a view with copying old data to new data at the corresponding
@@ -2895,67 +2952,6 @@ resize(const I& arg_prop, Kokkos::View<T, P...>& v,
   static_assert(Kokkos::ViewTraits<T, P...>::is_managed,
                 "Can only resize managed views");
 
-  // Fix #904 by checking dimensions before actually resizing.
-  //
-  // Rank is known at compile time, so hopefully the compiler will
-  // remove branches that are compile-time false.  The upcoming "if
-  // constexpr" language feature would make this certain.
-  if (view_type::Rank == 1 && n0 == static_cast<size_t>(v.extent(0))) {
-    return;
-  }
-  if (view_type::Rank == 2 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1))) {
-    return;
-  }
-  if (view_type::Rank == 3 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2))) {
-    return;
-  }
-  if (view_type::Rank == 4 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3))) {
-    return;
-  }
-  if (view_type::Rank == 5 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3)) &&
-      n4 == static_cast<size_t>(v.extent(4))) {
-    return;
-  }
-  if (view_type::Rank == 6 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3)) &&
-      n4 == static_cast<size_t>(v.extent(4)) &&
-      n5 == static_cast<size_t>(v.extent(5))) {
-    return;
-  }
-  if (view_type::Rank == 7 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3)) &&
-      n4 == static_cast<size_t>(v.extent(4)) &&
-      n5 == static_cast<size_t>(v.extent(5)) &&
-      n6 == static_cast<size_t>(v.extent(6))) {
-    return;
-  }
-  if (view_type::Rank == 8 && n0 == static_cast<size_t>(v.extent(0)) &&
-      n1 == static_cast<size_t>(v.extent(1)) &&
-      n2 == static_cast<size_t>(v.extent(2)) &&
-      n3 == static_cast<size_t>(v.extent(3)) &&
-      n4 == static_cast<size_t>(v.extent(4)) &&
-      n5 == static_cast<size_t>(v.extent(5)) &&
-      n6 == static_cast<size_t>(v.extent(6)) &&
-      n7 == static_cast<size_t>(v.extent(7))) {
-    return;
-  }
-  // If Kokkos ever supports Views of rank > 8, the above code won't
-  // be incorrect, because avoiding reallocation in resize() is just
-  // an optimization.
-
   // TODO (mfh 27 Jun 2017) If the old View has enough space but just
   // different dimensions (e.g., if the product of the dimensions,
   // including extra space for alignment, will not change), then
@@ -2963,19 +2959,64 @@ resize(const I& arg_prop, Kokkos::View<T, P...>& v,
   // reallocates if any of the dimensions change, even if the old View
   // has enough space.
 
-  view_type v_resized(view_alloc(v.label(), std::forward<const I>(arg_prop)),
-                      n0, n1, n2, n3, n4, n5, n6, n7);
+  const size_t new_extents[8] = {n0, n1, n2, n3, n4, n5, n6, n7};
+  const bool sizeMismatch = Impl::size_mismatch(v, v.rank_dynamic, new_extents);
 
-  Kokkos::Impl::ViewRemap<view_type, view_type>(v_resized, v);
+  if (sizeMismatch) {
+    view_type v_resized(view_alloc(v.label(), std::forward<const I>(arg_prop)),
+                        n0, n1, n2, n3, n4, n5, n6, n7);
 
-  v = v_resized;
+    Kokkos::Impl::ViewRemap<view_type, view_type>(v_resized, v);
+    // This fence really ought to look for an execution space in
+    // arg_prop, and just fence that if there is one
+    Kokkos::fence("Kokkos::resize(View)");
+
+    v = v_resized;
+  }
 }
 
 /** \brief  Resize a view with copying old data to new data at the corresponding
  * indices. */
 template <class T, class... P>
-inline void resize(Kokkos::View<T, P...>& v,
-                   const typename Kokkos::View<T, P...>::array_layout& layout) {
+inline std::enable_if_t<
+    std::is_same<typename Kokkos::View<T, P...>::array_layout,
+                 Kokkos::LayoutLeft>::value ||
+    std::is_same<typename Kokkos::View<T, P...>::array_layout,
+                 Kokkos::LayoutRight>::value ||
+    std::is_same<typename Kokkos::View<T, P...>::array_layout,
+                 Kokkos::LayoutStride>::value ||
+    is_layouttiled<typename Kokkos::View<T, P...>::array_layout>::value>
+resize(Kokkos::View<T, P...>& v,
+       const typename Kokkos::View<T, P...>::array_layout& layout) {
+  using view_type = Kokkos::View<T, P...>;
+
+  static_assert(Kokkos::ViewTraits<T, P...>::is_managed,
+                "Can only resize managed views");
+
+  if (v.layout() != layout) {
+    view_type v_resized(v.label(), layout);
+
+    Kokkos::Impl::ViewRemap<view_type, view_type>(v_resized, v);
+    Kokkos::fence("Kokkos::resize(View)");
+
+    v = v_resized;
+  }
+}
+
+// FIXME User-provided (custom) layouts are not required to have a comparison
+// operator. Hence, there is no way to check if the requested layout is actually
+// the same as the existing one.
+template <class T, class... P>
+inline std::enable_if_t<
+    !(std::is_same<typename Kokkos::View<T, P...>::array_layout,
+                   Kokkos::LayoutLeft>::value ||
+      std::is_same<typename Kokkos::View<T, P...>::array_layout,
+                   Kokkos::LayoutRight>::value ||
+      std::is_same<typename Kokkos::View<T, P...>::array_layout,
+                   Kokkos::LayoutStride>::value ||
+      is_layouttiled<typename Kokkos::View<T, P...>::array_layout>::value)>
+resize(Kokkos::View<T, P...>& v,
+       const typename Kokkos::View<T, P...>::array_layout& layout) {
   using view_type = Kokkos::View<T, P...>;
 
   static_assert(Kokkos::ViewTraits<T, P...>::is_managed,
@@ -3009,10 +3050,16 @@ realloc(Kokkos::View<T, P...>& v,
   static_assert(Kokkos::ViewTraits<T, P...>::is_managed,
                 "Can only realloc managed views");
 
-  const std::string label = v.label();
+  const size_t new_extents[8] = {n0, n1, n2, n3, n4, n5, n6, n7};
+  const bool sizeMismatch = Impl::size_mismatch(v, v.rank_dynamic, new_extents);
 
-  v = view_type();  // Deallocate first, if the only view to allocation
-  v = view_type(label, n0, n1, n2, n3, n4, n5, n6, n7);
+  if (sizeMismatch) {
+    const std::string label = v.label();
+
+    v = view_type();  // Deallocate first, if the only view to allocation
+    v = view_type(label, n0, n1, n2, n3, n4, n5, n6, n7);
+  } else
+    Kokkos::deep_copy(v, typename view_type::value_type{});
 }
 
 /** \brief  Resize a view with discarding old data. */
@@ -3209,7 +3256,8 @@ create_mirror_view_and_copy(
         Impl::MirrorViewType<Space, T, P...>::is_same_memspace>::type* =
         nullptr) {
   (void)name;
-  fence();  // same behavior as deep_copy(src, src)
+  fence(
+      "Kokkos::create_mirror_view_and_copy: fence before returning src view");  // same behavior as deep_copy(src, src)
   return src;
 }
 
diff --git a/packages/kokkos/core/src/Kokkos_Core.hpp b/packages/kokkos/core/src/Kokkos_Core.hpp
index c3771ab393f3aaf8f77cb474056d90e867ff03da..60e748589df593dbb9e549f6433daea77b5bc6b0 100644
--- a/packages/kokkos/core/src/Kokkos_Core.hpp
+++ b/packages/kokkos/core/src/Kokkos_Core.hpp
@@ -59,6 +59,7 @@
 #include <Kokkos_LogicalSpaces.hpp>
 #include <Kokkos_Pair.hpp>
 #include <Kokkos_MathematicalFunctions.hpp>
+#include <Kokkos_MathematicalSpecialFunctions.hpp>
 #include <Kokkos_MemoryPool.hpp>
 #include <Kokkos_Array.hpp>
 #include <Kokkos_View.hpp>
@@ -74,6 +75,7 @@
 #include <iosfwd>
 #include <map>
 #include <memory>
+#include <vector>
 
 //----------------------------------------------------------------------------
 
@@ -121,6 +123,7 @@ class ExecSpaceManager {
   void initialize_spaces(const Kokkos::InitArguments& args);
   void finalize_spaces(const bool all_spaces);
   void static_fence();
+  void static_fence(const std::string&);
   void print_configuration(std::ostream& msg, const bool detail);
   static ExecSpaceManager& get_instance();
 };
@@ -184,6 +187,7 @@ void push_finalize_hook(std::function<void()> f);
 void finalize_all();
 
 void fence();
+void fence(const std::string&);
 
 /** \brief Print "Bill of Materials" */
 void print_configuration(std::ostream&, const bool detail = false);
@@ -274,6 +278,44 @@ class ScopeGuard {
 
 }  // namespace Kokkos
 
+namespace Kokkos {
+namespace Experimental {
+// Partitioning an Execution Space: expects space and integer arguments for
+// relative weight
+//   Customization point for backends
+//   Default behavior is to return the passed in instance
+template <class ExecSpace, class... Args>
+std::vector<ExecSpace> partition_space(ExecSpace space, Args...) {
+  static_assert(is_execution_space<ExecSpace>::value,
+                "Kokkos Error: partition_space expects an Execution Space as "
+                "first argument");
+#ifdef __cpp_fold_expressions
+  static_assert(
+      (... && std::is_arithmetic_v<Args>),
+      "Kokkos Error: partitioning arguments must be integers or floats");
+#endif
+  std::vector<ExecSpace> instances(sizeof...(Args));
+  for (int s = 0; s < int(sizeof...(Args)); s++) instances[s] = space;
+  return instances;
+}
+
+template <class ExecSpace, class T>
+std::vector<ExecSpace> partition_space(ExecSpace space,
+                                       std::vector<T>& weights) {
+  static_assert(is_execution_space<ExecSpace>::value,
+                "Kokkos Error: partition_space expects an Execution Space as "
+                "first argument");
+  static_assert(
+      std::is_arithmetic<T>::value,
+      "Kokkos Error: partitioning arguments must be integers or floats");
+
+  std::vector<ExecSpace> instances(weights.size());
+  for (int s = 0; s < int(weights.size()); s++) instances[s] = space;
+  return instances;
+}
+}  // namespace Experimental
+}  // namespace Kokkos
+
 #include <Kokkos_Crs.hpp>
 #include <Kokkos_WorkGraphPolicy.hpp>
 // Including this in Kokkos_Parallel_Reduce.hpp led to a circular dependency
diff --git a/packages/kokkos/core/src/Kokkos_Core_fwd.hpp b/packages/kokkos/core/src/Kokkos_Core_fwd.hpp
index fe7eba3f6ef178848d2ea832341014d6dc5d1003..a610ee76dffb6fd23fadae40437b893eaab5cc87 100644
--- a/packages/kokkos/core/src/Kokkos_Core_fwd.hpp
+++ b/packages/kokkos/core/src/Kokkos_Core_fwd.hpp
@@ -53,7 +53,9 @@
 #include <impl/Kokkos_Error.hpp>
 #include <impl/Kokkos_Utilities.hpp>
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 #include <Kokkos_MasterLock.hpp>
+#endif
 
 //----------------------------------------------------------------------------
 // Have assumed a 64bit build (8byte pointers) throughout the code base.
@@ -238,7 +240,8 @@ class LogicalMemorySpace;
 
 namespace Kokkos {
 void fence();
-}
+void fence(const std::string &);
+}  // namespace Kokkos
 
 //----------------------------------------------------------------------------
 
@@ -250,9 +253,13 @@ class View;
 namespace Impl {
 
 template <class DstSpace, class SrcSpace,
-          class ExecutionSpace = typename DstSpace::execution_space>
+          class ExecutionSpace = typename DstSpace::execution_space,
+          class Enable         = void>
 struct DeepCopy;
 
+template <typename ExecutionSpace, class DT, class... DP>
+struct ZeroMemset;
+
 template <class ViewType, class Layout = typename ViewType::array_layout,
           class ExecSpace = typename ViewType::execution_space,
           int Rank = ViewType::Rank, typename iType = int64_t>
diff --git a/packages/kokkos/core/src/Kokkos_Crs.hpp b/packages/kokkos/core/src/Kokkos_Crs.hpp
index 1a10500b19a55f4f963807dd2cf1a28e6062f98c..897402d37643bf8876360b3e828c685c6251fe19 100644
--- a/packages/kokkos/core/src/Kokkos_Crs.hpp
+++ b/packages/kokkos/core/src/Kokkos_Crs.hpp
@@ -179,7 +179,9 @@ class GetCrsTransposeCounts {
     const closure_type closure(*this,
                                policy_type(0, index_type(in.entries.size())));
     closure.execute();
-    execution_space().fence();
+    execution_space().fence(
+        "Kokkos::Impl::GetCrsTransposeCounts::GetCrsTransposeCounts: fence "
+        "after functor execution");
   }
 };
 
@@ -261,7 +263,9 @@ class FillCrsTransposeEntries {
     using closure_type = Kokkos::Impl::ParallelFor<self_type, policy_type>;
     const closure_type closure(*this, policy_type(0, index_type(in.numRows())));
     closure.execute();
-    execution_space().fence();
+    execution_space().fence(
+        "Kokkos::Impl::FillCrsTransposeEntries::FillCrsTransposeEntries: fence "
+        "after functor execution");
   }
 };
 
diff --git a/packages/kokkos/core/src/Kokkos_Cuda.hpp b/packages/kokkos/core/src/Kokkos_Cuda.hpp
index 7a218120bb7bb3b053335946ae25ad58c8a85e6d..c5a6b0f7d7d579e2ccad3c05f97d042d4ed63471 100644
--- a/packages/kokkos/core/src/Kokkos_Cuda.hpp
+++ b/packages/kokkos/core/src/Kokkos_Cuda.hpp
@@ -55,13 +55,13 @@
 
 #include <impl/Kokkos_AnalyzePolicy.hpp>
 #include <Kokkos_CudaSpace.hpp>
+#include <Cuda/Kokkos_Cuda_Error.hpp>  // CUDA_SAFE_CALL
 
 #include <Kokkos_Parallel.hpp>
 #include <Kokkos_TaskScheduler.hpp>
 #include <Kokkos_Layout.hpp>
 #include <Kokkos_ScratchSpace.hpp>
 #include <Kokkos_MemoryTraits.hpp>
-#include <impl/Kokkos_Tags.hpp>
 #include <impl/Kokkos_ExecSpaceInitializer.hpp>
 #include <impl/Kokkos_HostSharedPtr.hpp>
 
@@ -184,8 +184,10 @@ class Cuda {
   /// method does not return until all dispatched functors on this
   /// device have completed.
   static void impl_static_fence();
+  static void impl_static_fence(const std::string&);
 
   void fence() const;
+  void fence(const std::string&) const;
 
   /** \brief  Return the maximum amount of concurrency.  */
   static int concurrency();
@@ -199,7 +201,7 @@ class Cuda {
 
   Cuda();
 
-  Cuda(cudaStream_t stream);
+  Cuda(cudaStream_t stream, bool manage_stream = false);
 
   //--------------------------------------------------------------------------
   //! \name Device-specific functions
@@ -246,7 +248,7 @@ class Cuda {
   inline Impl::CudaInternal* impl_internal_space_instance() const {
     return m_space_instance.get();
   }
-  uint32_t impl_instance_id() const noexcept { return 0; }
+  uint32_t impl_instance_id() const noexcept;
 
  private:
   Kokkos::Impl::HostSharedPtr<Impl::CudaInternal> m_space_instance;
@@ -271,9 +273,28 @@ class CudaSpaceInitializer : public ExecSpaceInitializerBase {
   void initialize(const InitArguments& args) final;
   void finalize(const bool all_spaces) final;
   void fence() final;
+  void fence(const std::string&) final;
   void print_configuration(std::ostream& msg, const bool detail) final;
 };
 
+template <class DT, class... DP>
+struct ZeroMemset<Kokkos::Cuda, DT, DP...> {
+  ZeroMemset(const Kokkos::Cuda& exec_space_instance,
+             const View<DT, DP...>& dst,
+             typename View<DT, DP...>::const_value_type&) {
+    KOKKOS_IMPL_CUDA_SAFE_CALL(cudaMemsetAsync(
+        dst.data(), 0,
+        dst.size() * sizeof(typename View<DT, DP...>::value_type),
+        exec_space_instance.cuda_stream()));
+  }
+
+  ZeroMemset(const View<DT, DP...>& dst,
+             typename View<DT, DP...>::const_value_type&) {
+    KOKKOS_IMPL_CUDA_SAFE_CALL(
+        cudaMemset(dst.data(), 0,
+                   dst.size() * sizeof(typename View<DT, DP...>::value_type)));
+  }
+};
 }  // namespace Impl
 }  // namespace Kokkos
 
diff --git a/packages/kokkos/core/src/Kokkos_CudaSpace.hpp b/packages/kokkos/core/src/Kokkos_CudaSpace.hpp
index e10fae93c7ca01ce90f31b5d22ca9bff7d113884..910a8b2d7470b65b66d397a2507eb96723be447c 100644
--- a/packages/kokkos/core/src/Kokkos_CudaSpace.hpp
+++ b/packages/kokkos/core/src/Kokkos_CudaSpace.hpp
@@ -70,6 +70,12 @@ extern "C" void kokkos_impl_cuda_set_pin_uvm_to_host(bool);
 /*--------------------------------------------------------------------------*/
 
 namespace Kokkos {
+namespace Impl {
+
+template <typename T>
+struct is_cuda_type_space : public std::false_type {};
+
+}  // namespace Impl
 
 /** \brief  Cuda on-device memory management */
 
@@ -119,10 +125,12 @@ class CudaSpace {
   /**\brief Return Name of the MemorySpace */
   static constexpr const char* name() { return m_name; }
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
   /*--------------------------------*/
   /** \brief  Error reporting for HostSpace attempt to access CudaSpace */
   KOKKOS_DEPRECATED static void access_error();
   KOKKOS_DEPRECATED static void access_error(const void* const);
+#endif
 
  private:
   int m_device;  ///< Which Cuda device
@@ -130,6 +138,10 @@ class CudaSpace {
   static constexpr const char* m_name = "Cuda";
   friend class Kokkos::Impl::SharedAllocationRecord<Kokkos::CudaSpace, void>;
 };
+
+template <>
+struct Impl::is_cuda_type_space<CudaSpace> : public std::true_type {};
+
 }  // namespace Kokkos
 
 /*--------------------------------------------------------------------------*/
@@ -151,9 +163,11 @@ class CudaUVMSpace {
   /** \brief  If UVM capability is available */
   static bool available();
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
   /*--------------------------------*/
   /** \brief  CudaUVMSpace specific routine */
   KOKKOS_DEPRECATED static int number_of_allocations();
+#endif
 
   /*--------------------------------*/
 
@@ -209,6 +223,9 @@ class CudaUVMSpace {
   static constexpr const char* m_name = "CudaUVM";
 };
 
+template <>
+struct Impl::is_cuda_type_space<CudaUVMSpace> : public std::true_type {};
+
 }  // namespace Kokkos
 
 /*--------------------------------------------------------------------------*/
@@ -271,6 +288,9 @@ class CudaHostPinnedSpace {
   /*--------------------------------*/
 };
 
+template <>
+struct Impl::is_cuda_type_space<CudaHostPinnedSpace> : public std::true_type {};
+
 }  // namespace Kokkos
 
 /*--------------------------------------------------------------------------*/
@@ -411,338 +431,107 @@ struct MemorySpaceAccess<Kokkos::CudaHostPinnedSpace, Kokkos::CudaUVMSpace> {
 namespace Kokkos {
 namespace Impl {
 
+void DeepCopyCuda(void* dst, const void* src, size_t n);
+void DeepCopyAsyncCuda(const Cuda& instance, void* dst, const void* src,
+                       size_t n);
 void DeepCopyAsyncCuda(void* dst, const void* src, size_t n);
 
-template <>
-struct DeepCopy<CudaSpace, CudaSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Cuda&, void* dst, const void* src, size_t);
-};
-
-template <>
-struct DeepCopy<CudaSpace, HostSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Cuda&, void* dst, const void* src, size_t);
-};
-
-template <>
-struct DeepCopy<HostSpace, CudaSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Cuda&, void* dst, const void* src, size_t);
-};
-
-template <>
-struct DeepCopy<CudaUVMSpace, CudaUVMSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-  DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(instance, dst, src, n);
-  }
-};
-
-template <>
-struct DeepCopy<CudaUVMSpace, HostSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, HostSpace, Cuda>(dst, src, n);
-  }
-  DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, HostSpace, Cuda>(instance, dst, src, n);
-  }
-};
-
-template <>
-struct DeepCopy<HostSpace, CudaUVMSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-  DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, CudaSpace, Cuda>(instance, dst, src, n);
-  }
-};
-
-template <>
-struct DeepCopy<CudaHostPinnedSpace, CudaHostPinnedSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-  DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(instance, dst, src, n);
-  }
-};
-
-template <>
-struct DeepCopy<CudaHostPinnedSpace, HostSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, HostSpace, Cuda>(dst, src, n);
-  }
-  DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, HostSpace, Cuda>(instance, dst, src, n);
-  }
-};
-
-template <>
-struct DeepCopy<HostSpace, CudaHostPinnedSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-  DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, CudaSpace, Cuda>(instance, dst, src, n);
-  }
-};
-
-template <>
-struct DeepCopy<CudaUVMSpace, CudaSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-  DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(instance, dst, src, n);
-  }
-};
-
-template <>
-struct DeepCopy<CudaSpace, CudaUVMSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
+template <class MemSpace>
+struct DeepCopy<MemSpace, HostSpace, Cuda,
+                std::enable_if_t<is_cuda_type_space<MemSpace>::value>> {
+  DeepCopy(void* dst, const void* src, size_t n) { DeepCopyCuda(dst, src, n); }
   DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(instance, dst, src, n);
+    DeepCopyAsyncCuda(instance, dst, src, n);
   }
 };
 
-template <>
-struct DeepCopy<CudaUVMSpace, CudaHostPinnedSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
+template <class MemSpace>
+struct DeepCopy<HostSpace, MemSpace, Cuda,
+                std::enable_if_t<is_cuda_type_space<MemSpace>::value>> {
+  DeepCopy(void* dst, const void* src, size_t n) { DeepCopyCuda(dst, src, n); }
   DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(instance, dst, src, n);
+    DeepCopyAsyncCuda(instance, dst, src, n);
   }
 };
 
-template <>
-struct DeepCopy<CudaHostPinnedSpace, CudaUVMSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
+template <class MemSpace1, class MemSpace2>
+struct DeepCopy<MemSpace1, MemSpace2, Cuda,
+                std::enable_if_t<is_cuda_type_space<MemSpace1>::value &&
+                                 is_cuda_type_space<MemSpace2>::value>> {
+  DeepCopy(void* dst, const void* src, size_t n) { DeepCopyCuda(dst, src, n); }
   DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(instance, dst, src, n);
+    DeepCopyAsyncCuda(instance, dst, src, n);
   }
 };
 
-template <>
-struct DeepCopy<CudaSpace, CudaHostPinnedSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-  DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(instance, dst, src, n);
-  }
-};
-
-template <>
-struct DeepCopy<CudaHostPinnedSpace, CudaSpace, Cuda> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-  DeepCopy(const Cuda& instance, void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(instance, dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaSpace, CudaSpace, ExecutionSpace> {
+template <class MemSpace1, class MemSpace2, class ExecutionSpace>
+struct DeepCopy<MemSpace1, MemSpace2, ExecutionSpace,
+                std::enable_if_t<is_cuda_type_space<MemSpace1>::value &&
+                                 is_cuda_type_space<MemSpace2>::value &&
+                                 !std::is_same<ExecutionSpace, Cuda>::value>> {
   inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
+    DeepCopyCuda(dst, src, n);
   }
 
   inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
                   size_t n) {
-    exec.fence();
+    exec.fence(fence_string());
     DeepCopyAsyncCuda(dst, src, n);
   }
-};
 
-template <class ExecutionSpace>
-struct DeepCopy<CudaSpace, HostSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, HostSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<HostSpace, CudaSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaSpace, CudaUVMSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaSpace, CudaHostPinnedSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, HostSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaUVMSpace, CudaSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaUVMSpace, CudaUVMSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaUVMSpace, CudaHostPinnedSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, HostSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaUVMSpace, HostSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<CudaSpace, HostSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaHostPinnedSpace, CudaSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
-  }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaHostPinnedSpace, CudaUVMSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, CudaSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
+ private:
+  static const std::string& fence_string() {
+    static const std::string string =
+        std::string("Kokkos::Impl::DeepCopy<") + MemSpace1::name() + "Space, " +
+        MemSpace2::name() +
+        "Space, ExecutionSpace>::DeepCopy: fence before copy";
+    return string;
   }
 };
 
-template <class ExecutionSpace>
-struct DeepCopy<CudaHostPinnedSpace, CudaHostPinnedSpace, ExecutionSpace> {
+template <class MemSpace, class ExecutionSpace>
+struct DeepCopy<MemSpace, HostSpace, ExecutionSpace,
+                std::enable_if_t<is_cuda_type_space<MemSpace>::value &&
+                                 !std::is_same<ExecutionSpace, Cuda>::value>> {
   inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, HostSpace, Cuda>(dst, src, n);
+    DeepCopyCuda(dst, src, n);
   }
 
   inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
                   size_t n) {
-    exec.fence();
+    exec.fence(fence_string());
     DeepCopyAsyncCuda(dst, src, n);
   }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<CudaHostPinnedSpace, HostSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, HostSpace, Cuda>(dst, src, n);
-  }
 
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
+ private:
+  static const std::string& fence_string() {
+    static const std::string string =
+        std::string("Kokkos::Impl::DeepCopy<") + MemSpace::name() +
+        "Space, HostSpace, ExecutionSpace>::DeepCopy: fence before copy";
+    return string;
   }
 };
 
-template <class ExecutionSpace>
-struct DeepCopy<HostSpace, CudaUVMSpace, ExecutionSpace> {
+template <class MemSpace, class ExecutionSpace>
+struct DeepCopy<HostSpace, MemSpace, ExecutionSpace,
+                std::enable_if_t<is_cuda_type_space<MemSpace>::value &&
+                                 !std::is_same<ExecutionSpace, Cuda>::value>> {
   inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, CudaSpace, Cuda>(dst, src, n);
+    DeepCopyCuda(dst, src, n);
   }
 
   inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
                   size_t n) {
-    exec.fence();
+    exec.fence(fence_string());
     DeepCopyAsyncCuda(dst, src, n);
   }
-};
 
-template <class ExecutionSpace>
-struct DeepCopy<HostSpace, CudaHostPinnedSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, HostSpace, Cuda>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncCuda(dst, src, n);
+ private:
+  static const std::string& fence_string() {
+    static const std::string string =
+        std::string("Kokkos::Impl::DeepCopy<HostSpace, ") + MemSpace::name() +
+        "Space, ExecutionSpace>::DeepCopy: fence before copy";
+    return string;
   }
 };
 
diff --git a/packages/kokkos/core/src/Kokkos_DetectionIdiom.hpp b/packages/kokkos/core/src/Kokkos_DetectionIdiom.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9e060b343eb77ffd9783b2449def926704032334
--- /dev/null
+++ b/packages/kokkos/core/src/Kokkos_DetectionIdiom.hpp
@@ -0,0 +1,116 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+#ifndef KOKKOS_DETECTION_IDIOM_HPP
+#define KOKKOS_DETECTION_IDIOM_HPP
+
+#include <impl/Kokkos_Utilities.hpp>  // void_t
+#include <type_traits>
+
+// NOTE This header implements the detection idiom from Version 2 of the C++
+// Extensions for Library Fundamentals, ISO/IEC TS 19568:2017
+
+// I deliberately omitted detected_or which does not fit well with the rest
+// of the specification. In my opinion, it should be removed from the TS.
+
+namespace Kokkos {
+
+namespace Impl {
+// base class for nonesuch to inherit from so it is not an aggregate
+struct nonesuch_base {};
+
+// primary template handles all types not supporting the archetypal Op
+template <class Default, class /*AlwaysVoid*/, template <class...> class Op,
+          class... /*Args*/>
+struct detector {
+  using value_t = std::false_type;
+  using type    = Default;
+};
+
+// specialization recognizes and handles only types supporting Op
+template <class Default, template <class...> class Op, class... Args>
+struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
+  using value_t = std::true_type;
+  using type    = Op<Args...>;
+};
+}  // namespace Impl
+
+struct nonesuch : private Impl::nonesuch_base {
+  ~nonesuch()               = delete;
+  nonesuch(nonesuch const&) = delete;
+  void operator=(nonesuch const&) = delete;
+};
+
+template <template <class...> class Op, class... Args>
+using is_detected =
+    typename Impl::detector<nonesuch, void, Op, Args...>::value_t;
+
+template <template <class...> class Op, class... Args>
+using detected_t = typename Impl::detector<nonesuch, void, Op, Args...>::type;
+
+template <class Default, template <class...> class Op, class... Args>
+using detected_or_t = typename Impl::detector<Default, void, Op, Args...>::type;
+
+template <class Expected, template <class...> class Op, class... Args>
+using is_detected_exact = std::is_same<Expected, detected_t<Op, Args...>>;
+
+template <class To, template <class...> class Op, class... Args>
+using is_detected_convertible =
+    std::is_convertible<detected_t<Op, Args...>, To>;
+
+#ifdef KOKKOS_ENABLE_CXX17
+template <template <class...> class Op, class... Args>
+inline constexpr bool is_detected_v = is_detected<Op, Args...>::value;
+
+template <class Expected, template <class...> class Op, class... Args>
+inline constexpr bool is_detected_exact_v =
+    is_detected_exact<Expected, Op, Args...>::value;
+
+template <class Expected, template <class...> class Op, class... Args>
+inline constexpr bool is_detected_convertible_v =
+    is_detected_convertible<Expected, Op, Args...>::value;
+#endif
+
+}  // namespace Kokkos
+
+#endif
diff --git a/packages/kokkos/core/src/Kokkos_ExecPolicy.hpp b/packages/kokkos/core/src/Kokkos_ExecPolicy.hpp
index 55aed13670e69838d94fff2735d421cc49a11835..c88c1ada14e38bd4c3cf90c61fc7351cc27fc8ea 100644
--- a/packages/kokkos/core/src/Kokkos_ExecPolicy.hpp
+++ b/packages/kokkos/core/src/Kokkos_ExecPolicy.hpp
@@ -48,7 +48,6 @@
 #include <Kokkos_Core_fwd.hpp>
 #include <impl/Kokkos_Traits.hpp>
 #include <impl/Kokkos_Error.hpp>
-#include <impl/Kokkos_Tags.hpp>
 #include <impl/Kokkos_AnalyzePolicy.hpp>
 #include <Kokkos_Concepts.hpp>
 #include <typeinfo>
diff --git a/packages/kokkos/core/src/Kokkos_HBWSpace.hpp b/packages/kokkos/core/src/Kokkos_HBWSpace.hpp
index d0366b599cf8c80c92812e386ced90f6fa77eb93..f6cdb2ec46cd4b5329a987e947ec356ff4efb0e9 100644
--- a/packages/kokkos/core/src/Kokkos_HBWSpace.hpp
+++ b/packages/kokkos/core/src/Kokkos_HBWSpace.hpp
@@ -287,7 +287,10 @@ struct DeepCopy<Kokkos::Experimental::HBWSpace, Kokkos::Experimental::HBWSpace,
   DeepCopy(void* dst, const void* src, size_t n) { memcpy(dst, src, n); }
 
   DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
+    exec.fence(
+        "Kokkos::Impl::DeepCopy<Kokkos::Experimental::HBWSpace, "
+        "Kokkos::Experimental::HBWSpace,ExecutionSpace::DeepCopy: fence before "
+        "copy");
     memcpy(dst, src, n);
   }
 };
@@ -297,7 +300,9 @@ struct DeepCopy<HostSpace, Kokkos::Experimental::HBWSpace, ExecutionSpace> {
   DeepCopy(void* dst, const void* src, size_t n) { memcpy(dst, src, n); }
 
   DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
+    exec.fence(
+        "Kokkos::Impl::DeepCopy<HostSpace, Kokkos::Experimental::HBWSpace, "
+        "ExecutionSpace>::DeepCopy: fence before copy");
     memcpy(dst, src, n);
   }
 };
@@ -307,7 +312,9 @@ struct DeepCopy<Kokkos::Experimental::HBWSpace, HostSpace, ExecutionSpace> {
   DeepCopy(void* dst, const void* src, size_t n) { memcpy(dst, src, n); }
 
   DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
+    exec.fence(
+        "Kokkos::Impl::DeepCopy<Kokkos::Experimental::HBWSpace, HostSpace, "
+        "ExecutionSpace>::DeepCopy: fence before copy");
     memcpy(dst, src, n);
   }
 };
diff --git a/packages/kokkos/core/src/Kokkos_HIP.hpp b/packages/kokkos/core/src/Kokkos_HIP.hpp
index 33cf8321c80282d5346c66afb5ee9b4be589576b..09df4f2fed4d8c5499ec339391de0730474b1f80 100644
--- a/packages/kokkos/core/src/Kokkos_HIP.hpp
+++ b/packages/kokkos/core/src/Kokkos_HIP.hpp
@@ -54,7 +54,6 @@
 
 #include <Kokkos_HIP_Space.hpp>
 #include <Kokkos_Parallel.hpp>
-#include <impl/Kokkos_Tags.hpp>
 
 #include <HIP/Kokkos_HIP_Instance.hpp>
 #include <HIP/Kokkos_HIP_MDRangePolicy.hpp>
diff --git a/packages/kokkos/core/src/Kokkos_HIP_Space.hpp b/packages/kokkos/core/src/Kokkos_HIP_Space.hpp
index 17bd681aa4b7b7aa8d98bb8253c86db81de6ce05..d20d533645b2f6bfc721820cb7a43adf4434f8e8 100644
--- a/packages/kokkos/core/src/Kokkos_HIP_Space.hpp
+++ b/packages/kokkos/core/src/Kokkos_HIP_Space.hpp
@@ -58,6 +58,7 @@
 #include <Kokkos_HostSpace.hpp>
 #include <Kokkos_Layout.hpp>
 #include <Kokkos_ScratchSpace.hpp>
+#include <HIP/Kokkos_HIP_Error.hpp>  // HIP_SAFE_CALL
 
 #include <impl/Kokkos_Profiling_Interface.hpp>
 #include <impl/Kokkos_ExecSpaceInitializer.hpp>
@@ -67,6 +68,13 @@
 /*--------------------------------------------------------------------------*/
 
 namespace Kokkos {
+namespace Impl {
+
+template <typename T>
+struct is_hip_type_space : public std::false_type {};
+
+}  // namespace Impl
+
 namespace Experimental {
 /** \brief  HIP on-device memory management */
 
@@ -116,10 +124,12 @@ class HIPSpace {
   /**\brief Return Name of the MemorySpace */
   static constexpr const char* name() { return "HIP"; }
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
   /*--------------------------------*/
   /** \brief  Error reporting for HostSpace attempt to access HIPSpace */
   KOKKOS_DEPRECATED static void access_error();
   KOKKOS_DEPRECATED static void access_error(const void* const);
+#endif
 
  private:
   int m_device;  ///< Which HIP device
@@ -129,6 +139,11 @@ class HIPSpace {
 };
 
 }  // namespace Experimental
+
+template <>
+struct Impl::is_hip_type_space<Experimental::HIPSpace> : public std::true_type {
+};
+
 }  // namespace Kokkos
 
 /*--------------------------------------------------------------------------*/
@@ -188,6 +203,11 @@ class HIPHostPinnedSpace {
   /*--------------------------------*/
 };
 }  // namespace Experimental
+
+template <>
+struct Impl::is_hip_type_space<Experimental::HIPHostPinnedSpace>
+    : public std::true_type {};
+
 }  // namespace Kokkos
 
 /*--------------------------------------------------------------------------*/
@@ -268,174 +288,116 @@ struct MemorySpaceAccess<Kokkos::Experimental::HIPHostPinnedSpace,
 namespace Kokkos {
 namespace Impl {
 
+void DeepCopyHIP(void* dst, const void* src, size_t n);
+void DeepCopyAsyncHIP(const Kokkos::Experimental::HIP& instance, void* dst,
+                      const void* src, size_t n);
 void DeepCopyAsyncHIP(void* dst, const void* src, size_t n);
 
-template <>
-struct DeepCopy<Kokkos::Experimental::HIPSpace, Kokkos::Experimental::HIPSpace,
-                Kokkos::Experimental::HIP> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Kokkos::Experimental::HIP&, void* dst, const void* src,
-           size_t);
-};
-
-template <>
-struct DeepCopy<Kokkos::Experimental::HIPSpace, HostSpace,
-                Kokkos::Experimental::HIP> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Kokkos::Experimental::HIP&, void* dst, const void* src,
-           size_t);
-};
-
-template <>
-struct DeepCopy<HostSpace, Kokkos::Experimental::HIPSpace,
-                Kokkos::Experimental::HIP> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Kokkos::Experimental::HIP&, void* dst, const void* src,
-           size_t);
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<Kokkos::Experimental::HIPSpace, Kokkos::Experimental::HIPSpace,
-                ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<Kokkos::Experimental::HIPSpace,
-                   Kokkos::Experimental::HIPSpace, Kokkos::Experimental::HIP>(
-        dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncHIP(dst, src, n);
+template <class MemSpace>
+struct DeepCopy<MemSpace, HostSpace, Kokkos::Experimental::HIP,
+                std::enable_if_t<is_hip_type_space<MemSpace>::value>> {
+  DeepCopy(void* dst, const void* src, size_t n) { DeepCopyHIP(dst, src, n); }
+  DeepCopy(const Kokkos::Experimental::HIP& instance, void* dst,
+           const void* src, size_t n) {
+    DeepCopyAsyncHIP(instance, dst, src, n);
   }
 };
 
-template <class ExecutionSpace>
-struct DeepCopy<Kokkos::Experimental::HIPSpace, HostSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<Kokkos::Experimental::HIPSpace, HostSpace,
-                   Kokkos::Experimental::HIP>(dst, src, n);
+template <class MemSpace>
+struct DeepCopy<HostSpace, MemSpace, Kokkos::Experimental::HIP,
+                std::enable_if_t<is_hip_type_space<MemSpace>::value>> {
+  DeepCopy(void* dst, const void* src, size_t n) { DeepCopyHIP(dst, src, n); }
+  DeepCopy(const Kokkos::Experimental::HIP& instance, void* dst,
+           const void* src, size_t n) {
+    DeepCopyAsyncHIP(instance, dst, src, n);
   }
+};
 
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncHIP(dst, src, n);
+template <class MemSpace1, class MemSpace2>
+struct DeepCopy<MemSpace1, MemSpace2, Kokkos::Experimental::HIP,
+                std::enable_if_t<is_hip_type_space<MemSpace1>::value &&
+                                 is_hip_type_space<MemSpace2>::value>> {
+  DeepCopy(void* dst, const void* src, size_t n) { DeepCopyHIP(dst, src, n); }
+  DeepCopy(const Kokkos::Experimental::HIP& instance, void* dst,
+           const void* src, size_t n) {
+    DeepCopyAsyncHIP(instance, dst, src, n);
   }
 };
 
-template <class ExecutionSpace>
-struct DeepCopy<HostSpace, Kokkos::Experimental::HIPSpace, ExecutionSpace> {
+template <class MemSpace1, class MemSpace2, class ExecutionSpace>
+struct DeepCopy<
+    MemSpace1, MemSpace2, ExecutionSpace,
+    std::enable_if_t<
+        is_hip_type_space<MemSpace1>::value &&
+        is_hip_type_space<MemSpace2>::value &&
+        !std::is_same<ExecutionSpace, Kokkos::Experimental::HIP>::value>> {
   inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, Kokkos::Experimental::HIPSpace,
-                   Kokkos::Experimental::HIP>(dst, src, n);
+    DeepCopyHIP(dst, src, n);
   }
 
   inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
                   size_t n) {
-    exec.fence();
+    exec.fence(fence_string());
     DeepCopyAsyncHIP(dst, src, n);
   }
-};
-
-template <>
-struct DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace,
-                Kokkos::Experimental::HIPHostPinnedSpace,
-                Kokkos::Experimental::HIP> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Kokkos::Experimental::HIP&, void* dst, const void* src,
-           size_t);
-};
-
-template <>
-struct DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace, HostSpace,
-                Kokkos::Experimental::HIP> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Kokkos::Experimental::HIP&, void* dst, const void* src,
-           size_t);
-};
-
-template <>
-struct DeepCopy<HostSpace, Kokkos::Experimental::HIPHostPinnedSpace,
-                Kokkos::Experimental::HIP> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Kokkos::Experimental::HIP&, void* dst, const void* src,
-           size_t);
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<Kokkos::Experimental::HIPSpace,
-                Kokkos::Experimental::HIPHostPinnedSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<Kokkos::Experimental::HIPSpace, HostSpace,
-                   Kokkos::Experimental::HIP>(dst, src, n);
-  }
 
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncHIP(dst, src, n);
+ private:
+  static const std::string& fence_string() {
+    static const std::string string =
+        std::string("Kokkos::Impl::DeepCopy<") + MemSpace1::name() + "Space, " +
+        MemSpace2::name() +
+        "Space, ExecutionSpace>::DeepCopy: fence before copy";
+    return string;
   }
 };
 
-template <class ExecutionSpace>
-struct DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace,
-                Kokkos::Experimental::HIPSpace, ExecutionSpace> {
+template <class MemSpace, class ExecutionSpace>
+struct DeepCopy<
+    MemSpace, HostSpace, ExecutionSpace,
+    std::enable_if_t<
+        is_hip_type_space<MemSpace>::value &&
+        !std::is_same<ExecutionSpace, Kokkos::Experimental::HIP>::value>> {
   inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, Kokkos::Experimental::HIPSpace,
-                   Kokkos::Experimental::HIP>(dst, src, n);
+    DeepCopyHIP(dst, src, n);
   }
 
   inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
                   size_t n) {
-    exec.fence();
+    exec.fence(fence_string());
     DeepCopyAsyncHIP(dst, src, n);
   }
-};
 
-template <class ExecutionSpace>
-struct DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace,
-                Kokkos::Experimental::HIPHostPinnedSpace, ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace,
-                   Kokkos::Experimental::HIPHostPinnedSpace,
-                   Kokkos::Experimental::HIP>(dst, src, n);
-  }
-
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncHIP(dst, src, n);
+ private:
+  static const std::string& fence_string() {
+    static const std::string string =
+        std::string("Kokkos::Impl::DeepCopy<") + MemSpace::name() +
+        "Space, HostSpace, ExecutionSpace>::DeepCopy: fence before copy";
+    return string;
   }
 };
 
-template <class ExecutionSpace>
-struct DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace, HostSpace,
-                ExecutionSpace> {
+template <class MemSpace, class ExecutionSpace>
+struct DeepCopy<
+    HostSpace, MemSpace, ExecutionSpace,
+    std::enable_if_t<
+        is_hip_type_space<MemSpace>::value &&
+        !std::is_same<ExecutionSpace, Kokkos::Experimental::HIP>::value>> {
   inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<Kokkos::Experimental::HIPHostPinnedSpace, HostSpace,
-                   Kokkos::Experimental::HIP>(dst, src, n);
+    DeepCopyHIP(dst, src, n);
   }
 
   inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
                   size_t n) {
-    exec.fence();
+    exec.fence(fence_string());
     DeepCopyAsyncHIP(dst, src, n);
   }
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<HostSpace, Kokkos::Experimental::HIPHostPinnedSpace,
-                ExecutionSpace> {
-  inline DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<HostSpace, Kokkos::Experimental::HIPHostPinnedSpace,
-                   Kokkos::Experimental::HIP>(dst, src, n);
-  }
 
-  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
-                  size_t n) {
-    exec.fence();
-    DeepCopyAsyncHIP(dst, src, n);
+ private:
+  static const std::string& fence_string() {
+    static const std::string string =
+        std::string("Kokkos::Impl::DeepCopy<HostSpace, ") + MemSpace::name() +
+        "Space, ExecutionSpace>::DeepCopy: fence before copy";
+    return string;
   }
 };
 }  // namespace Impl
@@ -536,7 +498,7 @@ class HIP {
   using scratch_memory_space = ScratchMemorySpace<HIP>;
 
   HIP();
-  HIP(hipStream_t stream);
+  HIP(hipStream_t stream, bool manage_stream = false);
 
   //@}
   //------------------------------------
@@ -558,8 +520,10 @@ class HIP {
    * until all dispatched functors on this device have completed.
    */
   static void impl_static_fence();
+  static void impl_static_fence(const std::string&);
 
   void fence() const;
+  void fence(const std::string&) const;
 
   hipStream_t hip_stream() const;
 
@@ -596,7 +560,7 @@ class HIP {
     return m_space_instance.get();
   }
 
-  uint32_t impl_instance_id() const noexcept { return 0; }
+  uint32_t impl_instance_id() const noexcept;
 
  private:
   Kokkos::Impl::HostSharedPtr<Impl::HIPInternal> m_space_instance;
@@ -620,9 +584,28 @@ class HIPSpaceInitializer : public Kokkos::Impl::ExecSpaceInitializerBase {
   void initialize(const InitArguments& args) final;
   void finalize(const bool) final;
   void fence() final;
+  void fence(const std::string&) final;
   void print_configuration(std::ostream& msg, const bool detail) final;
 };
 
+template <class DT, class... DP>
+struct ZeroMemset<Kokkos::Experimental::HIP, DT, DP...> {
+  ZeroMemset(const Kokkos::Experimental::HIP& exec_space,
+             const View<DT, DP...>& dst,
+             typename View<DT, DP...>::const_value_type&) {
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipMemsetAsync(
+        dst.data(), 0,
+        dst.size() * sizeof(typename View<DT, DP...>::value_type),
+        exec_space.hip_stream()));
+  }
+
+  ZeroMemset(const View<DT, DP...>& dst,
+             typename View<DT, DP...>::const_value_type&) {
+    KOKKOS_IMPL_HIP_SAFE_CALL(
+        hipMemset(dst.data(), 0,
+                  dst.size() * sizeof(typename View<DT, DP...>::value_type)));
+  }
+};
 }  // namespace Impl
 }  // namespace Kokkos
 
diff --git a/packages/kokkos/core/src/Kokkos_HPX.hpp b/packages/kokkos/core/src/Kokkos_HPX.hpp
index 2100b49c116cfaecd35205aa60708ed1535578ca..236211864ee8f00f2bce884dc6a16666174bdadf 100644
--- a/packages/kokkos/core/src/Kokkos_HPX.hpp
+++ b/packages/kokkos/core/src/Kokkos_HPX.hpp
@@ -69,7 +69,6 @@
 #include <impl/Kokkos_FunctorAdapter.hpp>
 #include <impl/Kokkos_FunctorAnalysis.hpp>
 #include <impl/Kokkos_Tools.hpp>
-#include <impl/Kokkos_Tags.hpp>
 #include <impl/Kokkos_TaskQueue.hpp>
 #include <impl/Kokkos_ExecSpaceInitializer.hpp>
 
@@ -318,25 +317,50 @@ class HPX {
   }
 
   void impl_fence_instance() const {
-    if (hpx::threads::get_self_ptr() == nullptr) {
-      hpx::threads::run_as_hpx_thread([this]() { impl_get_future().wait(); });
-    } else {
-      impl_get_future().wait();
-    }
+    impl_fence_instance(
+        "Kokkos::Experimental::HPX::impl_fence_instance: Unnamed Instance "
+        "Fence");
+  }
+  void impl_fence_instance(const std::string &name) const {
+    Kokkos::Tools::Experimental::Impl::profile_fence_event(name, *this, [&]() {
+      if (hpx::threads::get_self_ptr() == nullptr) {
+        hpx::threads::run_as_hpx_thread([this]() { impl_get_future().wait(); });
+      } else {
+        impl_get_future().wait();
+      }
+    });
   }
 
   void impl_fence_all_instances() const {
-    hpx::util::yield_while(
-        []() { return m_active_parallel_region_count.load() != 0; });
+    impl_fence_instance(
+        "Kokkos::Experimental::HPX::impl_fence_all_instances: Unnamed Global "
+        "HPX Fence");
+  }
+  void impl_fence_all_instances(const std::string &namename) const {
+    Kokkos::Tools::Experimental::Impl::profile_fence_event(name, *this, [&]() {
+      hpx::util::yield_while(
+          []() { return m_active_parallel_region_count.load() != 0; });
+    });
   }
 #endif
 
   void fence() const {
 #if defined(KOKKOS_ENABLE_HPX_ASYNC_DISPATCH)
     if (m_mode == instance_mode::global) {
-      impl_fence_all_instances();
+      impl_fence_all_instances(
+          "Kokkos::Experimental::HPX::fence: Unnamed Global HPX Fence");
+    } else {
+      impl_fence_instance(
+          "Kokkos::Experimental::HPX::fence: Unnamed HPX Instance Fence");
+    }
+#endif
+  }
+  void fence(const std::string &name) const {
+#if defined(KOKKOS_ENABLE_HPX_ASYNC_DISPATCH)
+    if (m_mode == instance_mode::global) {
+      impl_fence_all_instances(name);
     } else {
-      impl_fence_instance();
+      impl_fence_instance(name);
     }
 #endif
   }
@@ -464,6 +488,7 @@ class HPXSpaceInitializer : public ExecSpaceInitializerBase {
   void initialize(const InitArguments &args) final;
   void finalize(const bool) final;
   void fence() final;
+  void fence(const std::string &) final;
   void print_configuration(std::ostream &msg, const bool detail) final;
 };
 
@@ -491,7 +516,9 @@ inline void dispatch_execute_task(Closure *closure,
   }
 
   if (force_synchronous) {
-    instance.fence();
+    instance.fence(
+        "Kokkos::Experimental::Impl::HPX::dispatch_execute_task: fence due to "
+        "forced syncronizations");
   }
 }
 #else
diff --git a/packages/kokkos/core/src/Kokkos_HostSpace.hpp b/packages/kokkos/core/src/Kokkos_HostSpace.hpp
index ba69fbad393ee391eff2b59c34d4ae526fa7af29..c96cf5fbbe1b3a07f75da83a2557d2bbe4cb38c0 100644
--- a/packages/kokkos/core/src/Kokkos_HostSpace.hpp
+++ b/packages/kokkos/core/src/Kokkos_HostSpace.hpp
@@ -299,6 +299,20 @@ namespace Kokkos {
 
 namespace Impl {
 
+template <class DT, class... DP>
+struct ZeroMemset<typename HostSpace::execution_space, DT, DP...> {
+  ZeroMemset(const typename HostSpace::execution_space&,
+             const View<DT, DP...>& dst,
+             typename View<DT, DP...>::const_value_type& value)
+      : ZeroMemset(dst, value) {}
+
+  ZeroMemset(const View<DT, DP...>& dst,
+             typename View<DT, DP...>::const_value_type&) {
+    using ValueType = typename View<DT, DP...>::value_type;
+    std::memset(dst.data(), 0, sizeof(ValueType) * dst.size());
+  }
+};
+
 template <class ExecutionSpace>
 struct DeepCopy<HostSpace, HostSpace, ExecutionSpace> {
   DeepCopy(void* dst, const void* src, size_t n) {
@@ -306,9 +320,13 @@ struct DeepCopy<HostSpace, HostSpace, ExecutionSpace> {
   }
 
   DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
+    exec.fence(
+        "Kokkos::Impl::DeepCopy<HostSpace, HostSpace, "
+        "ExecutionSpace>::DeepCopy: fence before copy");
     hostspace_parallel_deepcopy(dst, src, n);
-    exec.fence();
+    exec.fence(
+        "Kokkos::Impl::DeepCopy<HostSpace, HostSpace, "
+        "ExecutionSpace>::DeepCopy: fence after copy");
   }
 };
 
diff --git a/packages/kokkos/core/src/Kokkos_Layout.hpp b/packages/kokkos/core/src/Kokkos_Layout.hpp
index 778b4f08109a5b2d617c2ff89298c9e92dbccb61..cfd77ea50fedcb5766ace9feb488c4c0f6238e89 100644
--- a/packages/kokkos/core/src/Kokkos_Layout.hpp
+++ b/packages/kokkos/core/src/Kokkos_Layout.hpp
@@ -50,7 +50,6 @@
 
 #include <cstddef>
 #include <impl/Kokkos_Traits.hpp>
-#include <impl/Kokkos_Tags.hpp>
 
 namespace Kokkos {
 
@@ -89,6 +88,16 @@ struct LayoutLeft {
                                 size_t N3 = 0, size_t N4 = 0, size_t N5 = 0,
                                 size_t N6 = 0, size_t N7 = 0)
       : dimension{N0, N1, N2, N3, N4, N5, N6, N7} {}
+
+  friend bool operator==(const LayoutLeft& left, const LayoutLeft& right) {
+    for (unsigned int rank = 0; rank < ARRAY_LAYOUT_MAX_RANK; ++rank)
+      if (left.dimension[rank] != right.dimension[rank]) return false;
+    return true;
+  }
+
+  friend bool operator!=(const LayoutLeft& left, const LayoutLeft& right) {
+    return !(left == right);
+  }
 };
 
 //----------------------------------------------------------------------------
@@ -123,6 +132,16 @@ struct LayoutRight {
                                  size_t N3 = 0, size_t N4 = 0, size_t N5 = 0,
                                  size_t N6 = 0, size_t N7 = 0)
       : dimension{N0, N1, N2, N3, N4, N5, N6, N7} {}
+
+  friend bool operator==(const LayoutRight& left, const LayoutRight& right) {
+    for (unsigned int rank = 0; rank < ARRAY_LAYOUT_MAX_RANK; ++rank)
+      if (left.dimension[rank] != right.dimension[rank]) return false;
+    return true;
+  }
+
+  friend bool operator!=(const LayoutRight& left, const LayoutRight& right) {
+    return !(left == right);
+  }
 };
 
 //----------------------------------------------------------------------------
@@ -184,6 +203,18 @@ struct LayoutStride {
                                   size_t S7 = 0)
       : dimension{N0, N1, N2, N3, N4, N5, N6, N7}, stride{S0, S1, S2, S3,
                                                           S4, S5, S6, S7} {}
+
+  friend bool operator==(const LayoutStride& left, const LayoutStride& right) {
+    for (unsigned int rank = 0; rank < ARRAY_LAYOUT_MAX_RANK; ++rank)
+      if (left.dimension[rank] != right.dimension[rank] ||
+          left.stride[rank] != right.stride[rank])
+        return false;
+    return true;
+  }
+
+  friend bool operator!=(const LayoutStride& left, const LayoutStride& right) {
+    return !(left == right);
+  }
 };
 
 // ===================================================================================
@@ -229,18 +260,6 @@ struct LayoutTiled {
   static_assert(IsPowerOfTwo,
                 "LayoutTiled must be given power-of-two tile dimensions");
 
-#if 0
-  static_assert( (Impl::is_integral_power_of_two(ArgN0) ) &&
-                 (Impl::is_integral_power_of_two(ArgN1) ) &&
-                 (Impl::is_integral_power_of_two(ArgN2) || (ArgN2 == 0) ) &&
-                 (Impl::is_integral_power_of_two(ArgN3) || (ArgN3 == 0) ) &&
-                 (Impl::is_integral_power_of_two(ArgN4) || (ArgN4 == 0) ) &&
-                 (Impl::is_integral_power_of_two(ArgN5) || (ArgN5 == 0) ) &&
-                 (Impl::is_integral_power_of_two(ArgN6) || (ArgN6 == 0) ) &&
-                 (Impl::is_integral_power_of_two(ArgN7) || (ArgN7 == 0) )
-               , "LayoutTiled must be given power-of-two tile dimensions" );
-#endif
-
   using array_layout = LayoutTiled<OuterP, InnerP, ArgN0, ArgN1, ArgN2, ArgN3,
                                    ArgN4, ArgN5, ArgN6, ArgN7, IsPowerOfTwo>;
   static constexpr Iterate outer_pattern = OuterP;
@@ -270,6 +289,16 @@ struct LayoutTiled {
                                  size_t argN4 = 0, size_t argN5 = 0,
                                  size_t argN6 = 0, size_t argN7 = 0)
       : dimension{argN0, argN1, argN2, argN3, argN4, argN5, argN6, argN7} {}
+
+  friend bool operator==(const LayoutTiled& left, const LayoutTiled& right) {
+    for (unsigned int rank = 0; rank < ARRAY_LAYOUT_MAX_RANK; ++rank)
+      if (left.dimension[rank] != right.dimension[rank]) return false;
+    return true;
+  }
+
+  friend bool operator!=(const LayoutTiled& left, const LayoutTiled& right) {
+    return !(left == right);
+  }
 };
 
 }  // namespace Experimental
diff --git a/packages/kokkos/core/src/Kokkos_Macros.hpp b/packages/kokkos/core/src/Kokkos_Macros.hpp
index 0d0185346540bf929b4305d6ad496b2f02e39c69..8d0fd925a27070dcd97160ba25ba19f06d3842b2 100644
--- a/packages/kokkos/core/src/Kokkos_Macros.hpp
+++ b/packages/kokkos/core/src/Kokkos_Macros.hpp
@@ -53,11 +53,12 @@
  *  KOKKOS_ENABLE_HPX                 Kokkos::Experimental::HPX execution space
  *  KOKKOS_ENABLE_OPENMP              Kokkos::OpenMP execution space
  *  KOKKOS_ENABLE_OPENMPTARGET        Kokkos::Experimental::OpenMPTarget
- * execution space KOKKOS_ENABLE_HWLOC               HWLOC library is available.
+ *                                    execution space
+ *  KOKKOS_ENABLE_HIP                 Kokkos::Experimental::HIP execution space
+ *  KOKKOS_ENABLE_SYCL                Kokkos::Experimental::SYCL execution space
+ *  KOKKOS_ENABLE_HWLOC               HWLOC library is available.
  *  KOKKOS_ENABLE_DEBUG_BOUNDS_CHECK  Insert array bounds checks, is expensive!
- *  KOKKOS_ENABLE_MPI                 Negotiate MPI/execution space
- * interactions. KOKKOS_ENABLE_CUDA_UVM            Use CUDA UVM for Cuda memory
- * space.
+ *  KOKKOS_ENABLE_CUDA_UVM            Use CUDA UVM for Cuda memory space.
  */
 
 #ifndef KOKKOS_DONT_INCLUDE_CORE_CONFIG_H
@@ -211,6 +212,11 @@
 #define KOKKOS_ENABLE_PRAGMA_SIMD 1
 #endif
 
+// FIXME Workaround for ICE with intel 17,18,19 in Trilinos
+#if (KOKKOS_COMPILER_INTEL <= 1900)
+#define KOKKOS_IMPL_WORKAROUND_ICE_IN_TRILINOS_WITH_OLD_INTEL_COMPILERS
+#endif
+
 // FIXME_SYCL
 #if !defined(KOKKOS_ENABLE_SYCL)
 #define KOKKOS_ENABLE_PRAGMA_IVDEP 1
@@ -220,11 +226,19 @@
 #define KOKKOS_MEMORY_ALIGNMENT 64
 #endif
 
+#if defined(_WIN32)
+#define KOKKOS_RESTRICT __restrict
+#else
 #define KOKKOS_RESTRICT __restrict__
+#endif
 
 #ifndef KOKKOS_IMPL_ALIGN_PTR
+#if defined(_WIN32)
+#define KOKKOS_IMPL_ALIGN_PTR(size) __declspec(align_value(size))
+#else
 #define KOKKOS_IMPL_ALIGN_PTR(size) __attribute__((align_value(size)))
 #endif
+#endif
 
 #if (1700 > KOKKOS_COMPILER_INTEL)
 #error "Compiling with Intel version earlier than 17.0 is not supported."
@@ -507,24 +521,44 @@
 #if defined(KOKKOS_ENABLE_CUDA_RELOCATABLE_DEVICE_CODE)
 #define KOKKOS_ENABLE_TASKDAG
 #endif
+// FIXME_SYCL Tasks not implemented
 #elif !defined(KOKKOS_ENABLE_HIP) && !defined(KOKKOS_ENABLE_SYCL)
 #define KOKKOS_ENABLE_TASKDAG
 #endif
 
-#if defined(KOKKOS_ENABLE_CUDA)
-#define KOKKOS_IMPL_CUDA_VERSION_9_WORKAROUND
-#if (__CUDA_ARCH__)
-#define KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-#endif
-#endif
-
 #define KOKKOS_INVALID_INDEX (~std::size_t(0))
 
 #define KOKKOS_IMPL_CTOR_DEFAULT_ARG KOKKOS_INVALID_INDEX
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 #define KOKKOS_CONSTEXPR_14 constexpr
-#define KOKKOS_DEPRECATED [[deprecated]]
 #define KOKKOS_DEPRECATED_TRAILING_ATTRIBUTE
+#endif
+
+// Guard intel compiler version <= 1900
+// intel error #2651: attribute does not apply to any entity
+// using <deprecated_type> KOKKOS_DEPRECATED = ...
+#if defined(KOKKOS_ENABLE_DEPRECATION_WARNINGS) && !defined(__NVCC__) && \
+    (KOKKOS_COMPILER_INTEL > 1900)
+#define KOKKOS_DEPRECATED [[deprecated]]
+#define KOKKOS_DEPRECATED_WITH_COMMENT(comment) [[deprecated(comment)]]
+#else
+#define KOKKOS_DEPRECATED
+#define KOKKOS_DEPRECATED_WITH_COMMENT(comment)
+#endif
+
+#define KOKKOS_IMPL_STRINGIFY(x) #x
+#define KOKKOS_IMPL_TOSTRING(x) KOKKOS_IMPL_STRINGIFY(x)
+
+#ifdef _MSC_VER
+#define KOKKOS_IMPL_DO_PRAGMA(x) __pragma(x)
+#define KOKKOS_IMPL_WARNING(desc) \
+  KOKKOS_IMPL_DO_PRAGMA(message(  \
+      __FILE__ "(" KOKKOS_IMPL_TOSTRING(__LINE__) ") : warning: " #desc))
+#else
+#define KOKKOS_IMPL_DO_PRAGMA(x) _Pragma(#x)
+#define KOKKOS_IMPL_WARNING(desc) KOKKOS_IMPL_DO_PRAGMA(message(#desc))
+#endif
 
 // DJS 05/28/2019: Bugfix: Issue 2155
 // Use KOKKOS_ENABLE_CUDA_LDG_INTRINSIC to avoid memory leak in RandomAccess
@@ -541,7 +575,7 @@
 
 #if (defined(KOKKOS_COMPILER_GNU) || defined(KOKKOS_COMPILER_CLANG) ||  \
      defined(KOKKOS_COMPILER_INTEL) || defined(KOKKOS_COMPILER_PGI)) && \
-    !defined(KOKKOS_COMPILER_MSVC)
+    !defined(_WIN32)
 #define KOKKOS_IMPL_ENABLE_STACKTRACE
 #define KOKKOS_IMPL_ENABLE_CXXABI
 #endif
@@ -553,7 +587,8 @@
 #undef __CUDA_ARCH__
 #endif
 
-#if defined(KOKKOS_COMPILER_MSVC) && !defined(KOKKOS_COMPILER_CLANG)
+#if (defined(KOKKOS_COMPILER_MSVC) && !defined(KOKKOS_COMPILER_CLANG)) || \
+    (defined(KOKKOS_COMPILER_INTEL) && defined(_WIN32))
 #define KOKKOS_THREAD_LOCAL __declspec(thread)
 #else
 #define KOKKOS_THREAD_LOCAL __thread
diff --git a/packages/kokkos/core/src/Kokkos_MasterLock.hpp b/packages/kokkos/core/src/Kokkos_MasterLock.hpp
index 3c45e131a0fba6e39f3f97ef2fd67451b9aef76c..cbfbb92660ba9d75f4aadb67196d969902524a4f 100644
--- a/packages/kokkos/core/src/Kokkos_MasterLock.hpp
+++ b/packages/kokkos/core/src/Kokkos_MasterLock.hpp
@@ -47,6 +47,8 @@
 
 #include <Kokkos_Macros.hpp>
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
+
 namespace Kokkos {
 namespace Experimental {
 
@@ -72,4 +74,6 @@ class MasterLock;
 }  // namespace Experimental
 }  // namespace Kokkos
 
+#endif
+
 #endif  // KOKKOS_MASTER_LOCK_HPP
diff --git a/packages/kokkos/core/src/Kokkos_MathematicalFunctions.hpp b/packages/kokkos/core/src/Kokkos_MathematicalFunctions.hpp
index 50223651e7d189e07cd94f9bf48eb6c5dcaa62d2..50fde82d77a7c37dfa0d5f3d1a565df470f680e0 100644
--- a/packages/kokkos/core/src/Kokkos_MathematicalFunctions.hpp
+++ b/packages/kokkos/core/src/Kokkos_MathematicalFunctions.hpp
@@ -55,116 +55,224 @@
 #endif
 
 namespace Kokkos {
+
+namespace Impl {
+template <class T, bool = std::is_integral<T>::value>
+struct promote {
+  using type = double;
+};
+template <class T>
+struct promote<T, false> {};
+template <>
+struct promote<long double> {
+  using type = long double;
+};
+template <>
+struct promote<double> {
+  using type = double;
+};
+template <>
+struct promote<float> {
+  using type = float;
+};
+template <class T>
+using promote_t = typename promote<T>::type;
+template <class T, class U>
+struct promote_2 {
+  using type = decltype(promote_t<T>() + promote_t<U>());
+};
+template <class T, class U>
+using promote_2_t = typename promote_2<T, U>::type;
+}  // namespace Impl
+
 namespace Experimental {
 
 #if defined(KOKKOS_ENABLE_SYCL)
-#define NAMESPACE_MATH_FUNCTIONS sycl
+#define KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE sycl
 #else
-#define NAMESPACE_MATH_FUNCTIONS std
+#define KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE std
 #endif
 
-#define KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, RETURNTYPE, ARGTYPE) \
-  KOKKOS_INLINE_FUNCTION RETURNTYPE FUNC(ARGTYPE x) {                        \
-    using NAMESPACE_MATH_FUNCTIONS::FUNC;                                    \
-    return FUNC(x);                                                          \
+// NOTE long double overloads are not available on the device
+#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \
+    defined(KOKKOS_ENABLE_SYCL) || defined(KOKKOS_ENABLE_OPENMPTARGET)
+#else
+#define KOKKOS_IMPL_MATH_FUNCTIONS_HAVE_LONG_DOUBLE_OVERLOADS
+#endif
+
+#if defined(KOKKOS_IMPL_MATH_FUNCTIONS_HAVE_LONG_DOUBLE_OVERLOADS)
+
+#define KOKKOS_IMPL_MATH_UNARY_FUNCTION(FUNC)                                 \
+  KOKKOS_INLINE_FUNCTION float FUNC(float x) {                                \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(x);                                                           \
+  }                                                                           \
+  KOKKOS_INLINE_FUNCTION double FUNC(double x) {                              \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(x);                                                           \
+  }                                                                           \
+  KOKKOS_INLINE_FUNCTION long double FUNC(long double x) {                    \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(x);                                                           \
+  }                                                                           \
+  KOKKOS_INLINE_FUNCTION float FUNC##f(float x) {                             \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(x);                                                           \
+  }                                                                           \
+  KOKKOS_INLINE_FUNCTION long double FUNC##l(long double x) {                 \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(x);                                                           \
+  }                                                                           \
+  template <class T>                                                          \
+  KOKKOS_INLINE_FUNCTION std::enable_if_t<std::is_integral<T>::value, double> \
+  FUNC(T x) {                                                                 \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(static_cast<double>(x));                                      \
   }
 
-#define KOKKOS_IMPL_UNARY_FUNCTION_INTEGRAL(FUNC, RETURNTYPE)              \
-  template <typename Integer,                                              \
-            typename = std::enable_if_t<std::is_integral<Integer>::value>> \
-  KOKKOS_INLINE_FUNCTION RETURNTYPE FUNC(Integer x) {                      \
-    return Kokkos::Experimental::FUNC(static_cast<double>(x));             \
+#define KOKKOS_IMPL_MATH_UNARY_PREDICATE(FUNC)                              \
+  KOKKOS_INLINE_FUNCTION bool FUNC(float x) {                               \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                       \
+    return FUNC(x);                                                         \
+  }                                                                         \
+  KOKKOS_INLINE_FUNCTION bool FUNC(double x) {                              \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                       \
+    return FUNC(x);                                                         \
+  }                                                                         \
+  KOKKOS_INLINE_FUNCTION bool FUNC(long double x) {                         \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                       \
+    return FUNC(x);                                                         \
+  }                                                                         \
+  template <class T>                                                        \
+  KOKKOS_INLINE_FUNCTION std::enable_if_t<std::is_integral<T>::value, bool> \
+  FUNC(T x) {                                                               \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                       \
+    return FUNC(static_cast<double>(x));                                    \
   }
 
-#define KOKKOS_IMPL_BINARY_FUNCTION_FLOATING_POINT(FUNC, TYPE) \
-  KOKKOS_INLINE_FUNCTION TYPE FUNC(TYPE x, TYPE y) {           \
-    using NAMESPACE_MATH_FUNCTIONS::FUNC;                      \
-    return FUNC(x, y);                                         \
+#define KOKKOS_IMPL_MATH_BINARY_FUNCTION(FUNC)                               \
+  KOKKOS_INLINE_FUNCTION float FUNC(float x, float y) {                      \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                        \
+    return FUNC(x, y);                                                       \
+  }                                                                          \
+  KOKKOS_INLINE_FUNCTION double FUNC(double x, double y) {                   \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                        \
+    return FUNC(x, y);                                                       \
+  }                                                                          \
+  KOKKOS_INLINE_FUNCTION long double FUNC(long double x, long double y) {    \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                        \
+    return FUNC(x, y);                                                       \
+  }                                                                          \
+  KOKKOS_INLINE_FUNCTION float FUNC##f(float x, float y) {                   \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                        \
+    return FUNC(x, y);                                                       \
+  }                                                                          \
+  KOKKOS_INLINE_FUNCTION long double FUNC##l(long double x, long double y) { \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                        \
+    return FUNC(x, y);                                                       \
+  }                                                                          \
+  template <class T1, class T2>                                              \
+  KOKKOS_INLINE_FUNCTION std::enable_if_t<std::is_arithmetic<T1>::value &&   \
+                                              std::is_arithmetic<T2>::value, \
+                                          Kokkos::Impl::promote_2_t<T1, T2>> \
+  FUNC(T1 x, T2 y) {                                                         \
+    using Promoted = Kokkos::Impl::promote_2_t<T1, T2>;                      \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                        \
+    return FUNC(static_cast<Promoted>(x), static_cast<Promoted>(y));         \
   }
 
-// NOTE long double overloads are not available on the device
-#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \
-    defined(KOKKOS_ENABLE_SYCL) || defined(KOKKOS_ENABLE_OPENMPTARGET)
+#else  // long double overloads are not available
 
-#define KOKKOS_IMPL_BINARY_FUNCTION_ARITHMETIC(FUNC)                         \
-  template <typename Arithmetic1, typename Arithmetic2,                      \
-            typename = std::enable_if_t<                                     \
-                std::is_arithmetic<Arithmetic1>::value &&                    \
-                std::is_arithmetic<Arithmetic2>::value &&                    \
-                !std::is_same<Arithmetic1, long double>::value &&            \
-                !std::is_same<Arithmetic2, long double>::value>>             \
-  KOKKOS_INLINE_FUNCTION double FUNC(Arithmetic1 x, Arithmetic2 y) {         \
-    return Kokkos::Experimental::FUNC(                                       \
-        static_cast<std::conditional_t<std::is_integral<Arithmetic1>::value, \
-                                       double, Arithmetic1>>(x),             \
-        static_cast<std::conditional_t<std::is_integral<Arithmetic2>::value, \
-                                       double, Arithmetic2>>(y));            \
+#define KOKKOS_IMPL_MATH_UNARY_FUNCTION(FUNC)                                 \
+  KOKKOS_INLINE_FUNCTION float FUNC(float x) {                                \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(x);                                                           \
+  }                                                                           \
+  KOKKOS_INLINE_FUNCTION double FUNC(double x) {                              \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(x);                                                           \
+  }                                                                           \
+  KOKKOS_INLINE_FUNCTION float FUNC##f(float x) {                             \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(x);                                                           \
+  }                                                                           \
+  template <class T>                                                          \
+  KOKKOS_INLINE_FUNCTION std::enable_if_t<std::is_integral<T>::value, double> \
+  FUNC(T x) {                                                                 \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                         \
+    return FUNC(static_cast<double>(x));                                      \
   }
 
-#define KOKKOS_IMPL_MATH_UNARY_FUNCTION(FUNC)                     \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, float, float)   \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, double, double) \
-  KOKKOS_IMPL_UNARY_FUNCTION_INTEGRAL(FUNC, double)
-
-#define KOKKOS_IMPL_MATH_UNARY_PREDICATE(FUNC)                  \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, bool, float)  \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, bool, double) \
-  KOKKOS_IMPL_UNARY_FUNCTION_INTEGRAL(FUNC, bool)
-
-#define KOKKOS_IMPL_MATH_BINARY_FUNCTION(FUNC)             \
-  KOKKOS_IMPL_BINARY_FUNCTION_FLOATING_POINT(FUNC, float)  \
-  KOKKOS_IMPL_BINARY_FUNCTION_FLOATING_POINT(FUNC, double) \
-  KOKKOS_IMPL_BINARY_FUNCTION_ARITHMETIC(FUNC)
-
-#define KOKKOS_IMPL_MATH_NAN()                                        \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(nanf, float, char const*) \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(nan, double, char const*)
-
-#else  // long double overloads are available
-
-#define KOKKOS_IMPL_BINARY_FUNCTION_ARITHMETIC(FUNC)                         \
-  template <typename Arithmetic1, typename Arithmetic2,                      \
-            typename =                                                       \
-                std::enable_if_t<std::is_arithmetic<Arithmetic1>::value &&   \
-                                 std::is_arithmetic<Arithmetic2>::value>,    \
-            typename Promoted = std::conditional_t<                          \
-                std::is_same<Arithmetic1, long double>::value ||             \
-                    std::is_same<Arithmetic2, long double>::value,           \
-                long double, double>>                                        \
-  KOKKOS_INLINE_FUNCTION Promoted FUNC(Arithmetic1 x, Arithmetic2 y) {       \
-    return Kokkos::Experimental::FUNC(                                       \
-        static_cast<std::conditional_t<std::is_integral<Arithmetic1>::value, \
-                                       double, Arithmetic1>>(x),             \
-        static_cast<std::conditional_t<std::is_integral<Arithmetic2>::value, \
-                                       double, Arithmetic2>>(y));            \
+#define KOKKOS_IMPL_MATH_UNARY_PREDICATE(FUNC)                              \
+  KOKKOS_INLINE_FUNCTION bool FUNC(float x) {                               \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                       \
+    return FUNC(x);                                                         \
+  }                                                                         \
+  KOKKOS_INLINE_FUNCTION bool FUNC(double x) {                              \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                       \
+    return FUNC(x);                                                         \
+  }                                                                         \
+  template <class T>                                                        \
+  KOKKOS_INLINE_FUNCTION std::enable_if_t<std::is_integral<T>::value, bool> \
+  FUNC(T x) {                                                               \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                       \
+    return FUNC(static_cast<double>(x));                                    \
   }
 
-#define KOKKOS_IMPL_MATH_UNARY_FUNCTION(FUNC)                               \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, float, float)             \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, double, double)           \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, long double, long double) \
-  KOKKOS_IMPL_UNARY_FUNCTION_INTEGRAL(FUNC, double)
-
-#define KOKKOS_IMPL_MATH_UNARY_PREDICATE(FUNC)                       \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, bool, float)       \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, bool, double)      \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(FUNC, bool, long double) \
-  KOKKOS_IMPL_UNARY_FUNCTION_INTEGRAL(FUNC, bool)
-
-#define KOKKOS_IMPL_MATH_BINARY_FUNCTION(FUNC)                  \
-  KOKKOS_IMPL_BINARY_FUNCTION_FLOATING_POINT(FUNC, float)       \
-  KOKKOS_IMPL_BINARY_FUNCTION_FLOATING_POINT(FUNC, double)      \
-  KOKKOS_IMPL_BINARY_FUNCTION_FLOATING_POINT(FUNC, long double) \
-  KOKKOS_IMPL_BINARY_FUNCTION_ARITHMETIC(FUNC)
-
-#define KOKKOS_IMPL_MATH_NAN()                                        \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(nanf, float, char const*) \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(nan, double, char const*) \
-  KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT(nanl, long double, char const*)
+#define KOKKOS_IMPL_MATH_BINARY_FUNCTION(FUNC)                          \
+  KOKKOS_INLINE_FUNCTION float FUNC(float x, float y) {                 \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                   \
+    return FUNC(x, y);                                                  \
+  }                                                                     \
+  KOKKOS_INLINE_FUNCTION double FUNC(double x, double y) {              \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                   \
+    return FUNC(x, y);                                                  \
+  }                                                                     \
+  KOKKOS_INLINE_FUNCTION float FUNC##f(float x, float y) {              \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                   \
+    return FUNC(x, y);                                                  \
+  }                                                                     \
+  template <class T1, class T2>                                         \
+  KOKKOS_INLINE_FUNCTION std::enable_if_t<                              \
+      std::is_arithmetic<T1>::value && std::is_arithmetic<T2>::value && \
+          !std::is_same<T1, long double>::value &&                      \
+          !std::is_same<T2, long double>::value,                        \
+      Kokkos::Impl::promote_2_t<T1, T2>>                                \
+  FUNC(T1 x, T2 y) {                                                    \
+    using Promoted = Kokkos::Impl::promote_2_t<T1, T2>;                 \
+    using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::FUNC;                   \
+    return FUNC(static_cast<Promoted>(x), static_cast<Promoted>(y));    \
+  }
 
 #endif
 
 // Basic operations
+KOKKOS_INLINE_FUNCTION int abs(int n) {
+  using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::abs;
+  return abs(n);
+}
+KOKKOS_INLINE_FUNCTION long abs(long n) {
+  using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::abs;
+  return abs(n);
+}
+KOKKOS_INLINE_FUNCTION long long abs(long long n) {
+  using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::abs;
+  return abs(n);
+}
+KOKKOS_INLINE_FUNCTION float abs(float x) {
+  using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::abs;
+  return abs(x);
+}
+KOKKOS_INLINE_FUNCTION double abs(double x) {
+  using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::abs;
+  return abs(x);
+}
+#if defined(KOKKOS_IMPL_MATH_FUNCTIONS_HAVE_LONG_DOUBLE_OVERLOADS)
+KOKKOS_INLINE_FUNCTION long double abs(long double x) {
+  using KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE::abs;
+  return abs(x);
+}
+#endif
 KOKKOS_IMPL_MATH_UNARY_FUNCTION(fabs)
 KOKKOS_IMPL_MATH_BINARY_FUNCTION(fmod)
 KOKKOS_IMPL_MATH_BINARY_FUNCTION(remainder)
@@ -172,7 +280,18 @@ KOKKOS_IMPL_MATH_BINARY_FUNCTION(fmin)
 KOKKOS_IMPL_MATH_BINARY_FUNCTION(fmax)
 KOKKOS_IMPL_MATH_BINARY_FUNCTION(fdim)
 #ifndef KOKKOS_ENABLE_SYCL
-KOKKOS_IMPL_MATH_NAN()
+KOKKOS_INLINE_FUNCTION float nanf(char const* arg) { return ::nanf(arg); }
+KOKKOS_INLINE_FUNCTION double nan(char const* arg) { return ::nan(arg); }
+#if defined(KOKKOS_IMPL_MATH_FUNCTIONS_HAVE_LONG_DOUBLE_OVERLOADS)
+KOKKOS_INLINE_FUNCTION long double nanl(char const* arg) { return ::nanl(arg); }
+#endif
+#else
+// FIXME_SYCL
+// sycl::nan does not follow the C/C++ standard library and takes an unsigned
+// integer as argument.  The current implementation does not attempt to convert
+// the character string arg into the quiet NaN value.
+KOKKOS_INLINE_FUNCTION float nanf(char const*) { return sycl::nan(0u); }
+KOKKOS_INLINE_FUNCTION double nan(char const*) { return sycl::nan(0ul); }
 #endif
 // Power functions
 KOKKOS_IMPL_MATH_BINARY_FUNCTION(pow)
@@ -211,6 +330,7 @@ KOKKOS_IMPL_MATH_UNARY_FUNCTION(lgamma)
 KOKKOS_IMPL_MATH_UNARY_FUNCTION(ceil)
 KOKKOS_IMPL_MATH_UNARY_FUNCTION(floor)
 KOKKOS_IMPL_MATH_UNARY_FUNCTION(trunc)
+// FIXME_SYCL not available as of current SYCL specification v1.2.1
 #ifndef KOKKOS_ENABLE_SYCL
 KOKKOS_IMPL_MATH_UNARY_FUNCTION(nearbyint)
 #endif
@@ -219,14 +339,12 @@ KOKKOS_IMPL_MATH_UNARY_PREDICATE(isfinite)
 KOKKOS_IMPL_MATH_UNARY_PREDICATE(isinf)
 KOKKOS_IMPL_MATH_UNARY_PREDICATE(isnan)
 
-#undef KOKKOS_IMPL_UNARY_FUNCTION_FLOATING_POINT
-#undef KOKKOS_IMPL_UNARY_FUNCTION_INTEGRAL
-#undef KOKKOS_IMPL_BINARY_FUNCTION_FLOATING_POINT
-#undef KOKKOS_IMPL_BINARY_FUNCTION_ARITHMETIC
+#undef KOKKOS_IMPL_MATH_FUNCTIONS_NAMESPACE
+#undef KOKKOS_IMPL_MATH_FUNCTIONS_HAVE_LONG_DOUBLE_OVERLOADS
 #undef KOKKOS_IMPL_MATH_UNARY_FUNCTION
 #undef KOKKOS_IMPL_MATH_UNARY_PREDICATE
 #undef KOKKOS_IMPL_MATH_BINARY_FUNCTION
-#undef KOKKOS_IMPL_MATH_NAN
+
 }  // namespace Experimental
 }  // namespace Kokkos
 
diff --git a/packages/kokkos/core/src/Kokkos_MathematicalSpecialFunctions.hpp b/packages/kokkos/core/src/Kokkos_MathematicalSpecialFunctions.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7bcea91c86790dd52265addb1ca651adbe21a966
--- /dev/null
+++ b/packages/kokkos/core/src/Kokkos_MathematicalSpecialFunctions.hpp
@@ -0,0 +1,1280 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#ifndef KOKKOS_MATHEMATICAL_SPECIAL_FUNCTIONS_HPP
+#define KOKKOS_MATHEMATICAL_SPECIAL_FUNCTIONS_HPP
+
+#include <Kokkos_Macros.hpp>
+#include <cmath>
+#include <algorithm>
+#include <type_traits>
+#include <Kokkos_MathematicalFunctions.hpp>
+#include <Kokkos_NumericTraits.hpp>
+#include <Kokkos_Complex.hpp>
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+namespace Kokkos {
+namespace Experimental {
+
+//! Compute exponential integral E1(x) (x > 0).
+template <class RealType>
+KOKKOS_INLINE_FUNCTION RealType expint1(RealType x) {
+  // This function is a conversion of the corresponding Fortran program in
+  // S. Zhang & J. Jin "Computation of Special Functions" (Wiley, 1996).
+  using Kokkos::Experimental::epsilon;
+  using Kokkos::Experimental::exp;
+  using Kokkos::Experimental::fabs;
+  using Kokkos::Experimental::infinity;
+  using Kokkos::Experimental::log;
+  using Kokkos::Experimental::pow;
+
+  RealType e1;
+
+  if (x < 0) {
+    e1 = -infinity<RealType>::value;
+  } else if (x == 0.0) {
+    e1 = infinity<RealType>::value;
+  } else if (x <= 1.0) {
+    e1         = 1.0;
+    RealType r = 1.0;
+    for (int k = 1; k <= 25; k++) {
+      RealType k_real = static_cast<RealType>(k);
+      r               = -r * k_real * x / pow(k_real + 1.0, 2.0);
+      e1              = e1 + r;
+      if (fabs(r) <= fabs(e1) * epsilon<RealType>::value) break;
+    }
+    e1 = -0.5772156649015328 - log(x) + x * e1;
+  } else {
+    int m       = 20 + static_cast<int>(80.0 / x);
+    RealType t0 = 0.0;
+    for (int k = m; k >= 1; k--) {
+      RealType k_real = static_cast<RealType>(k);
+      t0              = k_real / (1.0 + k_real / (x + t0));
+    }
+    e1 = exp(-x) * (1.0 / (x + t0));
+  }
+  return e1;
+}
+
+//! Compute error function erf(z) for z=cmplx(x,y).
+template <class RealType>
+KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> erf(
+    const Kokkos::complex<RealType>& z) {
+  // This function is a conversion of the corresponding Fortran program written
+  // by D.E. Amos, May,1974. D.E. Amos' revisions of Jan 86 incorporated by
+  // Ken Damrau on 27-Jan-1986 14:37:13
+  //
+  // Reference: NBS HANDBOOK OF MATHEMATICAL FUNCTIONS, AMS 55, By
+  //           M. ABRAMOWITZ AND I.A. STEGUN, December,1955.
+  // Summary:
+  //  If x < 0, z is replaced by -z and all computation is done in the right
+  //  half lane, except for z inside the circle abs(z)<=2, since
+  //  erf(-z)=-erf(z). The regions for computation are divided as follows
+  //      (1)  abs(z)<=2 - Power series, NBS Handbook, p. 298
+  //      (2)  abs(z)>2 and x>1 - continued fraction, NBS Handbook, p. 298
+  //      (3)  abs(z)>2 and 0<=x<=1 and abs(y)<6 - series, NBS Handbook, p. 299
+  //      (4)  abs(z)>2 and 0<=x<=1 and abs(y)>=6 - asymtotic expansion
+  //  Error condition: abs(z^2) > 670 is a fatal overflow error
+  using Kokkos::Experimental::cos;
+  using Kokkos::Experimental::epsilon;
+  using Kokkos::Experimental::exp;
+  using Kokkos::Experimental::fabs;
+  using Kokkos::Experimental::infinity;
+  using Kokkos::Experimental::sin;
+
+  using CmplxType = Kokkos::complex<RealType>;
+
+  auto const inf = infinity<RealType>::value;
+  auto const tol = epsilon<RealType>::value;
+
+  const RealType fnorm = 1.12837916709551;
+  const RealType gnorm = 0.564189583547756;
+  const RealType eh    = 0.606530659712633;
+  const RealType ef    = 0.778800783071405;
+  // const RealType tol   = 1.0e-13;
+  const RealType pi = M_PI;
+
+  CmplxType cans;
+
+  RealType az = Kokkos::abs(z);
+  if (az <= 2.0) {  // Series for abs(z)<=2.0
+    CmplxType cz    = z * z;
+    CmplxType accum = CmplxType(1.0, 0.0);
+    CmplxType term  = accum;
+    RealType ak     = 1.5;
+    for (int i = 1; i <= 35; i++) {
+      term  = term * cz / ak;
+      accum = accum + term;
+      if (Kokkos::abs(term) <= tol) break;
+      ak = ak + 1.0;
+    }
+    cz          = -cz;
+    RealType er = cz.real();
+    RealType ei = cz.imag();
+    accum       = accum * z * fnorm;
+    cz          = exp(er) * CmplxType(cos(ei), sin(ei));
+    cans        = accum * cz;
+  }       // end (az <= 2.0)
+  else {  //(az > 2.0)
+    CmplxType zp = z;
+    if (z.real() < 0.0) zp = -z;
+    CmplxType cz = zp * zp;
+    RealType xp  = zp.real();
+    RealType yp  = zp.imag();
+    if (xp > 1.0) {
+      // continued fraction for erfc(z), abs(Z)>2
+      int n          = static_cast<int>(100.0 / az + 5.0);
+      int fn         = n;
+      CmplxType term = cz;
+      for (int i = 1; i <= n; i++) {
+        RealType fnh = fn - 0.5;
+        term         = cz + (fnh * term) / (fn + term);
+        fn           = fn - 1;
+      }
+      if (Kokkos::abs(cz) > 670.0) return CmplxType(inf, inf);
+      cz              = -cz;
+      RealType er     = cz.real();
+      RealType ei     = cz.imag();
+      cz              = exp(er) * CmplxType(cos(ei), sin(ei));
+      CmplxType accum = zp * gnorm * cz;
+      cans            = 1.0 - accum / term;
+      if (z.real() < 0.0) cans = -cans;
+    }       // end (xp > 1.0)
+    else {  //(xp <= 1.0)
+      if (fabs(yp) <
+          6.0) {  // Series (3) for abs(z)>2 and 0<=xp<=1 and abs(yp)<6
+        RealType s1   = 0.0;
+        RealType s2   = 0.0;
+        RealType x2   = xp * xp;
+        RealType fx2  = 4.0 * x2;
+        RealType tx   = xp + xp;
+        RealType xy   = xp * yp;
+        RealType sxyh = sin(xy);
+        RealType sxy  = sin(xy + xy);
+        RealType cxy  = cos(xy + xy);
+        RealType fn   = 1.0;
+        RealType fnh  = 0.5;
+        RealType ey   = exp(yp);
+        RealType en   = ey;
+        RealType ehn  = eh;
+        RealType un   = ef;
+        RealType vn   = 1.0;
+        for (int i = 1; i <= 50; i++) {
+          RealType ren = 1.0 / en;
+          RealType csh = en + ren;
+          RealType tm  = xp * csh;
+          RealType ssh = en - ren;
+          RealType tmp = fnh * ssh;
+          RealType rn  = tx - tm * cxy + tmp * sxy;
+          RealType ain = tm * sxy + tmp * cxy;
+          RealType cf  = un / (vn + fx2);
+          rn           = cf * rn;
+          ain          = cf * ain;
+          s1           = s1 + rn;
+          s2           = s2 + ain;
+          if ((fabs(rn) + fabs(ain)) < tol * (fabs(s1) + fabs(s2))) break;
+          un  = un * ehn * ef;
+          ehn = ehn * eh;
+          en  = en * ey;
+          vn  = vn + fn + fn + 1.0;
+          fnh = fnh + 0.5;
+          fn  = fn + 1.0;
+        }
+        s1 = s1 + s1;
+        s2 = s2 + s2;
+        if (z.real() == 0.0)
+          s2 = s2 + yp;
+        else {
+          s1 = s1 + sxyh * sxyh / xp;
+          s2 = s2 + sxy / tx;
+        }
+        // Power series for erf(xp), 0<=xp<=1
+        RealType w  = 1.0;
+        RealType ak = 1.5;
+        RealType tm = 1.0;
+        for (int i = 1; i <= 17; i++) {
+          tm = tm * x2 / ak;
+          w  = w + tm;
+          if (tm <= tol) break;
+          ak = ak + 1.0;
+        }
+        RealType ex = exp(-x2);
+        w           = w * xp * fnorm * ex;
+        RealType cf = ex / pi;
+        s1          = cf * s1 + w;
+        s2          = cf * s2;
+        cans        = CmplxType(s1, s2);
+        if (z.real() < 0.0) cans = -cans;
+      }       // end (abs(yp) < 6.0)
+      else {  //(abs(YP)>=6.0)
+        // Asymtotic expansion for 0<=xp<=1 and abs(yp)>=6
+        CmplxType rcz   = 0.5 / cz;
+        CmplxType accum = CmplxType(1.0, 0.0);
+        CmplxType term  = accum;
+        RealType ak     = 1.0;
+        for (int i = 1; i <= 35; i++) {
+          term  = -term * ak * rcz;
+          accum = accum + term;
+          if (Kokkos::abs(term) / Kokkos::abs(accum) <= tol) break;
+          ak = ak + 2.0;
+        }
+        accum       = accum * gnorm / zp;
+        cz          = -cz;
+        RealType er = cz.real();
+        if (fabs(er) > 670.0) return CmplxType(inf, inf);
+        RealType ei = cz.imag();
+        cz          = exp(er) * CmplxType(cos(ei), sin(ei));
+        cans        = 1.0 - accum * cz;
+        if (z.real() < 0.0) cans = -cans;
+      }  // end (abs(YP)>=6.0)
+    }    // end (xp <= 1.0)
+  }      // end (az > 2.0)
+  return cans;
+}
+
+//! Compute scaled complementary error function erfcx(z)=exp(z^2)*erfc(z)
+//! for z=cmplx(x,y).
+template <class RealType>
+KOKKOS_INLINE_FUNCTION Kokkos::complex<RealType> erfcx(
+    const Kokkos::complex<RealType>& z) {
+  // This function is a conversion of the corresponding Fortran program written
+  // by D.E. Amos, May,1974. D.E. Amos' revisions of Jan 86 incorporated by
+  // Ken Damrau on 27-Jan-1986 14:37:13
+  //
+  // Reference: NBS HANDBOOK OF MATHEMATICAL FUNCTIONS, AMS 55, By
+  //           M. ABRAMOWITZ AND I.A. STEGUN, December,1955.
+  // Summary:
+  //  If x < 0, z is replaced by -z and all computation is done in the right
+  //  half lane, except for z inside the circle abs(z)<=2, since
+  //  erfc(-z)=2-erfc(z). The regions for computation are divided as follows
+  //      (1)  abs(z)<=2 - Power series, NBS Handbook, p. 298
+  //      (2)  abs(z)>2 and x>1 - continued fraction, NBS Handbook, p. 298
+  //      (3)  abs(z)>2 and 0<=x<=1 and abs(y)<6 - series, NBS Handbook, p. 299
+  //      (4)  abs(z)>2 and 0<=x<=1 and abs(y)>=6 - asymtotic expansion
+  // Error condition: abs(z^2) > 670 is a fatal overflow error when x<0
+  using Kokkos::Experimental::cos;
+  using Kokkos::Experimental::epsilon;
+  using Kokkos::Experimental::exp;
+  using Kokkos::Experimental::fabs;
+  using Kokkos::Experimental::infinity;
+  using Kokkos::Experimental::isinf;
+  using Kokkos::Experimental::sin;
+
+  using CmplxType = Kokkos::complex<RealType>;
+
+  auto const inf = infinity<RealType>::value;
+  auto const tol = epsilon<RealType>::value;
+
+  const RealType fnorm = 1.12837916709551;
+  const RealType gnorm = 0.564189583547756;
+  const RealType eh    = 0.606530659712633;
+  const RealType ef    = 0.778800783071405;
+  // const RealType tol   = 1.0e-13;
+  const RealType pi = M_PI;
+
+  CmplxType cans;
+
+  if ((isinf(z.real())) && (z.real() > 0)) {
+    cans = CmplxType(0.0, 0.0);
+    return cans;
+  }
+  if ((isinf(z.real())) && (z.real() < 0)) {
+    cans = CmplxType(inf, inf);
+    return cans;
+  }
+
+  RealType az = Kokkos::abs(z);
+  if (az <= 2.0) {  // Series for abs(z)<=2.0
+    CmplxType cz    = z * z;
+    CmplxType accum = CmplxType(1.0, 0.0);
+    CmplxType term  = accum;
+    RealType ak     = 1.5;
+    for (int i = 1; i <= 35; i++) {
+      term  = term * cz / ak;
+      accum = accum + term;
+      if (Kokkos::abs(term) <= tol) break;
+      ak = ak + 1.0;
+    }
+    cz          = -cz;
+    RealType er = cz.real();
+    RealType ei = cz.imag();
+    accum       = accum * z * fnorm;
+    cz          = exp(er) * CmplxType(cos(ei), sin(ei));
+    cans        = 1.0 / cz - accum;
+  }       // end (az <= 2.0)
+  else {  //(az > 2.0)
+    CmplxType zp = z;
+    if (z.real() < 0.0) zp = -z;
+    CmplxType cz = zp * zp;
+    RealType xp  = zp.real();
+    RealType yp  = zp.imag();
+    if (xp > 1.0) {
+      // continued fraction for erfc(z), abs(z)>2
+      int n          = static_cast<int>(100.0 / az + 5.0);
+      int fn         = n;
+      CmplxType term = cz;
+      for (int i = 1; i <= n; i++) {
+        RealType fnh = fn - 0.5;
+        term         = cz + (fnh * term) / (fn + term);
+        fn           = fn - 1;
+      }
+      cans = zp * gnorm / term;
+      if (z.real() >= 0.0) return cans;
+      if (Kokkos::abs(cz) > 670.0) return CmplxType(inf, inf);
+      ;
+      cz          = -cz;
+      RealType er = cz.real();
+      RealType ei = cz.imag();
+      cz          = exp(er) * CmplxType(cos(ei), sin(ei));
+      cz          = 1.0 / cz;
+      cans        = cz + cz - cans;
+    }       // end (xp > 1.0)
+    else {  //(xp <= 1.0)
+      if (fabs(yp) <
+          6.0) {  // Series (3) for abs(z)>2 and 0<=xp<=1 and abs(yp)<6
+        RealType s1   = 0.0;
+        RealType s2   = 0.0;
+        RealType x2   = xp * xp;
+        RealType fx2  = 4.0 * x2;
+        RealType tx   = xp + xp;
+        RealType xy   = xp * yp;
+        RealType sxyh = sin(xy);
+        RealType sxy  = sin(xy + xy);
+        RealType cxy  = cos(xy + xy);
+        RealType fn   = 1.0;
+        RealType fnh  = 0.5;
+        RealType ey   = exp(yp);
+        RealType en   = ey;
+        RealType ehn  = eh;
+        RealType un   = ef;
+        RealType vn   = 1.0;
+        for (int i = 1; i <= 50; i++) {
+          RealType ren = 1.0 / en;
+          RealType csh = en + ren;
+          RealType tm  = xp * csh;
+          RealType ssh = en - ren;
+          RealType tmp = fnh * ssh;
+          RealType rn  = tx - tm * cxy + tmp * sxy;
+          RealType ain = tm * sxy + tmp * cxy;
+          RealType cf  = un / (vn + fx2);
+          rn           = cf * rn;
+          ain          = cf * ain;
+          s1           = s1 + rn;
+          s2           = s2 + ain;
+          if ((fabs(rn) + fabs(ain)) < tol * (fabs(s1) + fabs(s2))) break;
+          un  = un * ehn * ef;
+          ehn = ehn * eh;
+          en  = en * ey;
+          vn  = vn + fn + fn + 1.0;
+          fnh = fnh + 0.5;
+          fn  = fn + 1.0;
+        }
+        s1 = s1 + s1;
+        s2 = s2 + s2;
+        if (z.real() == 0.0)
+          s2 = s2 + yp;
+        else {
+          s1 = s1 + sxyh * sxyh / xp;
+          s2 = s2 + sxy / tx;
+        }
+        // Power series for erf(xp), 0<=xp<=1
+        RealType w  = 1.0;
+        RealType ak = 1.5;
+        RealType tm = 1.0;
+        for (int i = 1; i <= 17; i++) {
+          tm = tm * x2 / ak;
+          w  = w + tm;
+          if (tm <= tol) break;
+          ak = ak + 1.0;
+        }
+        RealType ex   = exp(-x2);
+        w             = w * xp * fnorm * ex;
+        CmplxType rcz = CmplxType(cxy, sxy);
+        RealType y2   = yp * yp;
+        cz            = exp(x2 - y2) * rcz;
+        rcz           = exp(-y2) * rcz;
+        if (z.real() >= 0.0)
+          cans = cz * (1.0 - w) - rcz * CmplxType(s1, s2) / pi;
+        else
+          cans = cz * (1.0 + w) + rcz * CmplxType(s1, s2) / pi;
+      }       // end (abs(yp) < 6.0)
+      else {  //(abs(YP)>=6.0)
+        // Asymtotic expansion for 0<=xp<=1 and abs(yp)>=6
+        CmplxType rcz   = 0.5 / cz;
+        CmplxType accum = CmplxType(1.0, 0.0);
+        CmplxType term  = accum;
+        RealType ak     = 1.0;
+        for (int i = 1; i <= 35; i++) {
+          term  = -term * ak * rcz;
+          accum = accum + term;
+          if (Kokkos::abs(term) / Kokkos::abs(accum) <= tol) break;
+          ak = ak + 2.0;
+        }
+        accum = accum * gnorm / zp;
+        if (z.real() < 0.0) accum = -accum;
+        cans = accum;
+      }  // end (abs(YP)>=6.0)
+    }    // end (xp <= 1.0)
+  }      // end (az > 2.0)
+  return cans;
+}
+
+//! Compute scaled complementary error function erfcx(x)=exp(x^2)*erfc(x)
+//! for real x
+template <class RealType>
+KOKKOS_INLINE_FUNCTION RealType erfcx(RealType x) {
+  using CmplxType = Kokkos::complex<RealType>;
+  // Note: using erfcx(complex) for now
+  // TODO: replace with an implementation of erfcx(real)
+  CmplxType zin  = CmplxType(x, 0.0);
+  CmplxType zout = erfcx(zin);
+  return zout.real();
+}
+
+//! Compute Bessel function J0(z) of the first kind of order zero
+//! for a complex argument
+template <class CmplxType, class RealType, class IntType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_j0(const CmplxType& z,
+                                               const RealType& joint_val = 25,
+                                               const IntType& bw_start   = 70) {
+  // This function is converted and modified from the corresponding Fortran
+  // program CJYNB in S. Zhang & J. Jin "Computation of Special Functions"
+  //(Wiley, 1996).
+  // Input :  z         --- Complex argument
+  //         joint_val --- Joint point of abs(z) separating small and large
+  //                       argument regions
+  //         bw_start  --- Starting point for backward recurrence
+  // Output:  cbj0      --- J0(z)
+  using Kokkos::Experimental::fabs;
+  using Kokkos::Experimental::pow;
+
+  CmplxType cbj0;
+  const RealType pi    = M_PI;
+  const RealType a[12] = {
+      -0.703125e-01,           0.112152099609375e+00,   -0.5725014209747314e+00,
+      0.6074042001273483e+01,  -0.1100171402692467e+03, 0.3038090510922384e+04,
+      -0.1188384262567832e+06, 0.6252951493434797e+07,  -0.4259392165047669e+09,
+      0.3646840080706556e+11,  -0.3833534661393944e+13, 0.4854014686852901e+15};
+  const RealType b[12] = {0.732421875e-01,        -0.2271080017089844e+00,
+                          0.1727727502584457e+01, -0.2438052969955606e+02,
+                          0.5513358961220206e+03, -0.1825775547429318e+05,
+                          0.8328593040162893e+06, -0.5006958953198893e+08,
+                          0.3836255180230433e+10, -0.3649010818849833e+12,
+                          0.4218971570284096e+14, -0.5827244631566907e+16};
+
+  RealType r2p = 2.0 / pi;
+  RealType a0  = Kokkos::abs(z);
+  RealType y0  = fabs(z.imag());
+  CmplxType z1 = z;
+
+  if (a0 < 1e-100) {  // Treat z=0 as a special case
+    cbj0 = CmplxType(1.0, 0.0);
+  } else {
+    if (z.real() < 0.0) z1 = -z;
+    if (a0 <= joint_val) {  // Using backward recurrence for |z|<=joint_val
+                            // (default:25)
+      CmplxType cbs = CmplxType(0.0, 0.0);
+      CmplxType csu = CmplxType(0.0, 0.0);
+      CmplxType csv = CmplxType(0.0, 0.0);
+      CmplxType cf2 = CmplxType(0.0, 0.0);
+      CmplxType cf1 = CmplxType(1e-100, 0.0);
+      CmplxType cf, cs0;
+      for (int k = bw_start; k >= 0; k--) {  // Backward recurrence (default:
+                                             // 70)
+        cf                    = 2.0 * (k + 1.0) / z * cf1 - cf2;
+        RealType tmp_exponent = static_cast<RealType>(k / 2);
+        if (k == 0) cbj0 = cf;
+        if ((k == 2 * (k / 2)) && (k != 0)) {
+          if (y0 <= 1.0)
+            cbs = cbs + 2.0 * cf;
+          else
+            cbs = cbs + pow(-1.0, tmp_exponent) * 2.0 * cf;
+          csu = csu + pow(-1.0, tmp_exponent) * cf / k;
+        } else if (k > 1) {
+          csv = csv + pow(-1.0, tmp_exponent) * k / (k * k - 1.0) * cf;
+        }
+        cf2 = cf1;
+        cf1 = cf;
+      }
+      if (y0 <= 1.0)
+        cs0 = cbs + cf;
+      else
+        cs0 = (cbs + cf) / Kokkos::cos(z);
+      cbj0 = cbj0 / cs0;
+    } else {  // Using asymptotic expansion (5.2.5) for |z|>joint_val
+              // (default:25)
+      CmplxType ct1 = z1 - 0.25 * pi;
+      CmplxType cp0 = CmplxType(1.0, 0.0);
+      for (int k = 1; k <= 12; k++) {  // Calculate (5.2.9)
+        cp0 = cp0 + a[k - 1] * Kokkos::pow(z1, -2.0 * k);
+      }
+      CmplxType cq0 = -0.125 / z1;
+      for (int k = 1; k <= 12; k++) {  // Calculate (5.2.10)
+        cq0 = cq0 + b[k - 1] * Kokkos::pow(z1, -2.0 * k - 1);
+      }
+      CmplxType cu = Kokkos::sqrt(r2p / z1);
+      cbj0         = cu * (cp0 * Kokkos::cos(ct1) - cq0 * Kokkos::sin(ct1));
+    }
+  }
+  return cbj0;
+}
+
+//! Compute Bessel function Y0(z) of the second kind of order zero
+//! for a complex argument
+template <class CmplxType, class RealType, class IntType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_y0(const CmplxType& z,
+                                               const RealType& joint_val = 25,
+                                               const IntType& bw_start   = 70) {
+  // This function is converted and modified from the corresponding Fortran
+  // program CJYNB in S. Zhang & J. Jin "Computation of Special Functions"
+  //(Wiley, 1996).
+  //    Input :  z         --- Complex argument
+  //             joint_val --- Joint point of abs(z) separating small and large
+  //                           argument regions
+  //             bw_start  --- Starting point for backward recurrence
+  //    Output:  cby0      --- Y0(z)
+  using Kokkos::Experimental::fabs;
+  using Kokkos::Experimental::infinity;
+  using Kokkos::Experimental::pow;
+
+  auto const inf = infinity<RealType>::value;
+
+  CmplxType cby0, cbj0;
+  const RealType pi    = M_PI;
+  const RealType el    = 0.57721566490153286060651209008240;
+  const RealType a[12] = {
+      -0.703125e-01,           0.112152099609375e+00,   -0.5725014209747314e+00,
+      0.6074042001273483e+01,  -0.1100171402692467e+03, 0.3038090510922384e+04,
+      -0.1188384262567832e+06, 0.6252951493434797e+07,  -0.4259392165047669e+09,
+      0.3646840080706556e+11,  -0.3833534661393944e+13, 0.4854014686852901e+15};
+  const RealType b[12] = {0.732421875e-01,        -0.2271080017089844e+00,
+                          0.1727727502584457e+01, -0.2438052969955606e+02,
+                          0.5513358961220206e+03, -0.1825775547429318e+05,
+                          0.8328593040162893e+06, -0.5006958953198893e+08,
+                          0.3836255180230433e+10, -0.3649010818849833e+12,
+                          0.4218971570284096e+14, -0.5827244631566907e+16};
+
+  RealType r2p = 2.0 / pi;
+  RealType a0  = Kokkos::abs(z);
+  RealType y0  = fabs(z.imag());
+  CmplxType ci = CmplxType(0.0, 1.0);
+  CmplxType z1 = z;
+
+  if (a0 < 1e-100) {  // Treat z=0 as a special case
+    cby0 = -CmplxType(inf, 0.0);
+  } else {
+    if (z.real() < 0.0) z1 = -z;
+    if (a0 <= joint_val) {  // Using backward recurrence for |z|<=joint_val
+                            // (default:25)
+      CmplxType cbs = CmplxType(0.0, 0.0);
+      CmplxType csu = CmplxType(0.0, 0.0);
+      CmplxType csv = CmplxType(0.0, 0.0);
+      CmplxType cf2 = CmplxType(0.0, 0.0);
+      CmplxType cf1 = CmplxType(1e-100, 0.0);
+      CmplxType cf, cs0, ce;
+      for (int k = bw_start; k >= 0; k--) {  // Backward recurrence (default:
+                                             // 70)
+        cf                    = 2.0 * (k + 1.0) / z * cf1 - cf2;
+        RealType tmp_exponent = static_cast<RealType>(k / 2);
+        if (k == 0) cbj0 = cf;
+        if ((k == 2 * (k / 2)) && (k != 0)) {
+          if (y0 <= 1.0)
+            cbs = cbs + 2.0 * cf;
+          else
+            cbs = cbs + pow(-1.0, tmp_exponent) * 2.0 * cf;
+          csu = csu + pow(-1.0, tmp_exponent) * cf / k;
+        } else if (k > 1) {
+          csv = csv + pow(-1.0, tmp_exponent) * k / (k * k - 1.0) * cf;
+        }
+        cf2 = cf1;
+        cf1 = cf;
+      }
+      if (y0 <= 1.0)
+        cs0 = cbs + cf;
+      else
+        cs0 = (cbs + cf) / Kokkos::cos(z);
+      cbj0 = cbj0 / cs0;
+      ce   = Kokkos::log(z / 2.0) + el;
+      cby0 = r2p * (ce * cbj0 - 4.0 * csu / cs0);
+    } else {  // Using asymptotic expansion (5.2.6) for |z|>joint_val
+              // (default:25)
+      CmplxType ct1 = z1 - 0.25 * pi;
+      CmplxType cp0 = CmplxType(1.0, 0.0);
+      for (int k = 1; k <= 12; k++) {  // Calculate (5.2.9)
+        cp0 = cp0 + a[k - 1] * Kokkos::pow(z1, -2.0 * k);
+      }
+      CmplxType cq0 = -0.125 / z1;
+      for (int k = 1; k <= 12; k++) {  // Calculate (5.2.10)
+        cq0 = cq0 + b[k - 1] * Kokkos::pow(z1, -2.0 * k - 1);
+      }
+      CmplxType cu = Kokkos::sqrt(r2p / z1);
+      cbj0         = cu * (cp0 * Kokkos::cos(ct1) - cq0 * Kokkos::sin(ct1));
+      cby0         = cu * (cp0 * Kokkos::sin(ct1) + cq0 * Kokkos::cos(ct1));
+
+      if (z.real() < 0.0) {  // Apply (5.4.2)
+        if (z.imag() < 0.0) cby0 = cby0 - 2.0 * ci * cbj0;
+        if (z.imag() >= 0.0) cby0 = cby0 + 2.0 * ci * cbj0;
+      }
+    }
+  }
+  return cby0;
+}
+
+//! Compute Bessel function J1(z) of the first kind of order one
+//! for a complex argument
+template <class CmplxType, class RealType, class IntType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_j1(const CmplxType& z,
+                                               const RealType& joint_val = 25,
+                                               const IntType& bw_start   = 70) {
+  // This function is converted and modified from the corresponding Fortran
+  // program CJYNB in S. Zhang & J. Jin "Computation of Special Functions"
+  //(Wiley, 1996).
+  //    Input :  z         --- Complex argument
+  //             joint_val --- Joint point of abs(z) separating small and large
+  //                           argument regions
+  //             bw_start  --- Starting point for backward recurrence
+  //    Output:  cbj1      --- J1(z)
+  using Kokkos::Experimental::fabs;
+  using Kokkos::Experimental::pow;
+
+  CmplxType cbj1;
+  const RealType pi     = M_PI;
+  const RealType a1[12] = {0.1171875e+00,          -0.144195556640625e+00,
+                           0.6765925884246826e+00, -0.6883914268109947e+01,
+                           0.1215978918765359e+03, -0.3302272294480852e+04,
+                           0.1276412726461746e+06, -0.6656367718817688e+07,
+                           0.4502786003050393e+09, -0.3833857520742790e+11,
+                           0.4011838599133198e+13, -0.5060568503314727e+15};
+  const RealType b1[12] = {
+      -0.1025390625e+00,       0.2775764465332031e+00,  -0.1993531733751297e+01,
+      0.2724882731126854e+02,  -0.6038440767050702e+03, 0.1971837591223663e+05,
+      -0.8902978767070678e+06, 0.5310411010968522e+08,  -0.4043620325107754e+10,
+      0.3827011346598605e+12,  -0.4406481417852278e+14, 0.6065091351222699e+16};
+
+  RealType r2p = 2.0 / pi;
+  RealType a0  = Kokkos::abs(z);
+  RealType y0  = fabs(z.imag());
+  CmplxType z1 = z;
+
+  if (a0 < 1e-100) {  // Treat z=0 as a special case
+    cbj1 = CmplxType(0.0, 0.0);
+  } else {
+    if (z.real() < 0.0) z1 = -z;
+    if (a0 <= joint_val) {  // Using backward recurrence for |z|<=joint_val
+                            // (default:25)
+      CmplxType cbs = CmplxType(0.0, 0.0);
+      CmplxType csu = CmplxType(0.0, 0.0);
+      CmplxType csv = CmplxType(0.0, 0.0);
+      CmplxType cf2 = CmplxType(0.0, 0.0);
+      CmplxType cf1 = CmplxType(1e-100, 0.0);
+      CmplxType cf, cs0;
+      for (int k = bw_start; k >= 0; k--) {  // Backward recurrence (default:
+                                             // 70)
+        cf                    = 2.0 * (k + 1.0) / z * cf1 - cf2;
+        RealType tmp_exponent = static_cast<RealType>(k / 2);
+        if (k == 1) cbj1 = cf;
+        if ((k == 2 * (k / 2)) && (k != 0)) {
+          if (y0 <= 1.0)
+            cbs = cbs + 2.0 * cf;
+          else
+            cbs = cbs + pow(-1.0, tmp_exponent) * 2.0 * cf;
+          csu = csu + pow(-1.0, tmp_exponent) * cf / k;
+        } else if (k > 1) {
+          csv = csv + pow(-1.0, tmp_exponent) * k / (k * k - 1.0) * cf;
+        }
+        cf2 = cf1;
+        cf1 = cf;
+      }
+      if (y0 <= 1.0)
+        cs0 = cbs + cf;
+      else
+        cs0 = (cbs + cf) / Kokkos::cos(z);
+      cbj1 = cbj1 / cs0;
+    } else {  // Using asymptotic expansion (5.2.5) for |z|>joint_val
+              // (default:25)
+      CmplxType ct2 = z1 - 0.75 * pi;
+      CmplxType cp1 = CmplxType(1.0, 0.0);
+      for (int k = 1; k <= 12; k++) {  // Calculate (5.2.11)
+        cp1 = cp1 + a1[k - 1] * Kokkos::pow(z1, -2.0 * k);
+      }
+      CmplxType cq1 = 0.375 / z1;
+      for (int k = 1; k <= 12; k++) {  // Calculate (5.2.12)
+        cq1 = cq1 + b1[k - 1] * Kokkos::pow(z1, -2.0 * k - 1);
+      }
+      CmplxType cu = Kokkos::sqrt(r2p / z1);
+      cbj1         = cu * (cp1 * Kokkos::cos(ct2) - cq1 * Kokkos::sin(ct2));
+
+      if (real(z) < 0.0) {  // Apply (5.4.2)
+        cbj1 = -cbj1;
+      }
+    }
+  }
+  return cbj1;
+}
+
+//! Compute Bessel function Y1(z) of the second kind of order one
+//! for a complex argument
+template <class CmplxType, class RealType, class IntType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_y1(const CmplxType& z,
+                                               const RealType& joint_val = 25,
+                                               const IntType& bw_start   = 70) {
+  // This function is converted and modified from the corresponding Fortran
+  // program CJYNB in S. Zhang & J. Jin "Computation of Special Functions"
+  //(Wiley, 1996).
+  //    Input :  z         --- Complex argument
+  //             joint_val --- Joint point of abs(z) separating small and large
+  //                           argument regions
+  //             bw_start  --- Starting point for backward recurrence
+  //    Output:  cby1      --- Y1(z)
+  using Kokkos::Experimental::fabs;
+  using Kokkos::Experimental::infinity;
+  using Kokkos::Experimental::pow;
+
+  auto const inf = infinity<RealType>::value;
+
+  CmplxType cby1, cbj0, cbj1, cby0;
+  const RealType pi     = M_PI;
+  const RealType el     = 0.57721566490153286060651209008240;
+  const RealType a1[12] = {0.1171875e+00,          -0.144195556640625e+00,
+                           0.6765925884246826e+00, -0.6883914268109947e+01,
+                           0.1215978918765359e+03, -0.3302272294480852e+04,
+                           0.1276412726461746e+06, -0.6656367718817688e+07,
+                           0.4502786003050393e+09, -0.3833857520742790e+11,
+                           0.4011838599133198e+13, -0.5060568503314727e+15};
+  const RealType b1[12] = {
+      -0.1025390625e+00,       0.2775764465332031e+00,  -0.1993531733751297e+01,
+      0.2724882731126854e+02,  -0.6038440767050702e+03, 0.1971837591223663e+05,
+      -0.8902978767070678e+06, 0.5310411010968522e+08,  -0.4043620325107754e+10,
+      0.3827011346598605e+12,  -0.4406481417852278e+14, 0.6065091351222699e+16};
+
+  RealType r2p = 2.0 / pi;
+  RealType a0  = Kokkos::abs(z);
+  RealType y0  = fabs(z.imag());
+  CmplxType ci = CmplxType(0.0, 1.0);
+  CmplxType z1 = z;
+
+  if (a0 < 1e-100) {  // Treat z=0 as a special case
+    cby1 = -CmplxType(inf, 0.0);
+  } else {
+    if (z.real() < 0.0) z1 = -z;
+    if (a0 <= joint_val) {  // Using backward recurrence for |z|<=joint_val
+                            // (default:25)
+      CmplxType cbs = CmplxType(0.0, 0.0);
+      CmplxType csu = CmplxType(0.0, 0.0);
+      CmplxType csv = CmplxType(0.0, 0.0);
+      CmplxType cf2 = CmplxType(0.0, 0.0);
+      CmplxType cf1 = CmplxType(1e-100, 0.0);
+      CmplxType cf, cs0, ce;
+      for (int k = bw_start; k >= 0; k--) {  // Backward recurrence (default:
+                                             // 70)
+        cf                    = 2.0 * (k + 1.0) / z * cf1 - cf2;
+        RealType tmp_exponent = static_cast<RealType>(k / 2);
+        if (k == 1) cbj1 = cf;
+        if (k == 0) cbj0 = cf;
+        if ((k == 2 * (k / 2)) && (k != 0)) {
+          if (y0 <= 1.0)
+            cbs = cbs + 2.0 * cf;
+          else
+            cbs = cbs + pow(-1.0, tmp_exponent) * 2.0 * cf;
+          csu = csu + pow(-1.0, tmp_exponent) * cf / k;
+        } else if (k > 1) {
+          csv = csv + pow(-1.0, tmp_exponent) * k / (k * k - 1.0) * cf;
+        }
+        cf2 = cf1;
+        cf1 = cf;
+      }
+      if (y0 <= 1.0)
+        cs0 = cbs + cf;
+      else
+        cs0 = (cbs + cf) / Kokkos::cos(z);
+      cbj0 = cbj0 / cs0;
+      ce   = Kokkos::log(z / 2.0) + el;
+      cby0 = r2p * (ce * cbj0 - 4.0 * csu / cs0);
+      cbj1 = cbj1 / cs0;
+      cby1 = (cbj1 * cby0 - 2.0 / (pi * z)) / cbj0;
+    } else {  // Using asymptotic expansion (5.2.5) for |z|>joint_val
+              // (default:25)
+      CmplxType ct2 = z1 - 0.75 * pi;
+      CmplxType cp1 = CmplxType(1.0, 0.0);
+      for (int k = 1; k <= 12; k++) {  // Calculate (5.2.11)
+        cp1 = cp1 + a1[k - 1] * Kokkos::pow(z1, -2.0 * k);
+      }
+      CmplxType cq1 = 0.375 / z1;
+      for (int k = 1; k <= 12; k++) {  // Calculate (5.2.12)
+        cq1 = cq1 + b1[k - 1] * Kokkos::pow(z1, -2.0 * k - 1);
+      }
+      CmplxType cu = Kokkos::sqrt(r2p / z1);
+      cbj1         = cu * (cp1 * Kokkos::cos(ct2) - cq1 * Kokkos::sin(ct2));
+      cby1         = cu * (cp1 * Kokkos::sin(ct2) + cq1 * Kokkos::cos(ct2));
+
+      if (z.real() < 0.0) {  // Apply (5.4.2)
+        if (z.imag() < 0.0) cby1 = -(cby1 - 2.0 * ci * cbj1);
+        if (z.imag() >= 0.0) cby1 = -(cby1 + 2.0 * ci * cbj1);
+      }
+    }
+  }
+  return cby1;
+}
+
+//! Compute modified Bessel function I0(z) of the first kind of order zero
+//! for a complex argument
+template <class CmplxType, class RealType, class IntType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_i0(const CmplxType& z,
+                                               const RealType& joint_val = 25,
+                                               const IntType& bw_start   = 70) {
+  // This function is converted and modified from the corresponding Fortran
+  // programs CIKNB and CIK01 in S. Zhang & J. Jin "Computation of Special
+  // Functions" (Wiley, 1996).
+  //    Input :  z         --- Complex argument
+  //             joint_val --- Joint point of abs(z) separating small and large
+  //                           argument regions
+  //             bw_start  --- Starting point for backward recurrence
+  //    Output:  cbi0      --- I0(z)
+  CmplxType cbi0;
+  const RealType pi    = M_PI;
+  const RealType a[12] = {0.125,
+                          7.03125e-2,
+                          7.32421875e-2,
+                          1.1215209960938e-1,
+                          2.2710800170898e-1,
+                          5.7250142097473e-1,
+                          1.7277275025845e0,
+                          6.0740420012735e0,
+                          2.4380529699556e1,
+                          1.1001714026925e2,
+                          5.5133589612202e2,
+                          3.0380905109224e3};
+
+  RealType a0  = Kokkos::abs(z);
+  CmplxType z1 = z;
+
+  if (a0 < 1e-100) {  // Treat z=0 as a special case
+    cbi0 = CmplxType(1.0, 0.0);
+  } else {
+    if (z.real() < 0.0) z1 = -z;
+    if (a0 <= joint_val) {  // Using backward recurrence for |z|<=joint_val
+                            // (default:25)
+      CmplxType cbs = CmplxType(0.0, 0.0);
+      // CmplxType csk0 = CmplxType(0.0,0.0);
+      CmplxType cf0 = CmplxType(0.0, 0.0);
+      CmplxType cf1 = CmplxType(1e-100, 0.0);
+      CmplxType cf, cs0;
+      for (int k = bw_start; k >= 0; k--) {  // Backward recurrence (default:
+                                             // 70)
+        cf = 2.0 * (k + 1.0) * cf1 / z1 + cf0;
+        if (k == 0) cbi0 = cf;
+        // if ((k == 2*(k/2)) && (k != 0)) {
+        //  csk0 = csk0+4.0*cf/static_cast<RealType>(k);
+        //}
+        cbs = cbs + 2.0 * cf;
+        cf0 = cf1;
+        cf1 = cf;
+      }
+      cs0  = Kokkos::exp(z1) / (cbs - cf);
+      cbi0 = cbi0 * cs0;
+    } else {  // Using asymptotic expansion (6.2.1) for |z|>joint_val
+              // (default:25)
+      CmplxType ca = Kokkos::exp(z1) / Kokkos::sqrt(2.0 * pi * z1);
+      cbi0         = CmplxType(1.0, 0.0);
+      CmplxType zr = 1.0 / z1;
+      for (int k = 1; k <= 12; k++) {
+        cbi0 = cbi0 + a[k - 1] * Kokkos::pow(zr, 1.0 * k);
+      }
+      cbi0 = ca * cbi0;
+    }
+  }
+  return cbi0;
+}
+
+//! Compute modified Bessel function K0(z) of the second kind of order zero
+//! for a complex argument
+template <class CmplxType, class RealType, class IntType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_k0(const CmplxType& z,
+                                               const RealType& joint_val = 9,
+                                               const IntType& bw_start   = 30) {
+  // This function is converted and modified from the corresponding Fortran
+  // programs CIKNB and CIK01 in S. Zhang & J. Jin "Computation of Special
+  // Functions" (Wiley, 1996).
+  //    Purpose: Compute modified Bessel function K0(z) of the second kind of
+  //             order zero for a complex argument
+  //    Input :  z         --- Complex argument
+  //             joint_val --- Joint point of abs(z) separating small and large
+  //                           argument regions
+  //             bw_start  --- Starting point for backward recurrence
+  //    Output:  cbk0      --- K0(z)
+  using Kokkos::Experimental::infinity;
+  using Kokkos::Experimental::pow;
+
+  auto const inf = infinity<RealType>::value;
+
+  CmplxType cbk0, cbi0;
+  const RealType pi = M_PI;
+  const RealType el = 0.57721566490153286060651209008240;
+
+  RealType a0  = Kokkos::abs(z);
+  CmplxType ci = CmplxType(0.0, 1.0);
+  CmplxType z1 = z;
+
+  if (a0 < 1e-100) {  // Treat z=0 as a special case
+    cbk0 = CmplxType(inf, 0.0);
+  } else {
+    if (z.real() < 0.0) z1 = -z;
+    if (a0 <= joint_val) {  // Using backward recurrence for |z|<=joint_val
+                            // (default:9)
+      CmplxType cbs  = CmplxType(0.0, 0.0);
+      CmplxType csk0 = CmplxType(0.0, 0.0);
+      CmplxType cf0  = CmplxType(0.0, 0.0);
+      CmplxType cf1  = CmplxType(1e-100, 0.0);
+      CmplxType cf, cs0;
+      for (int k = bw_start; k >= 0; k--) {  // Backward recurrence (default:
+                                             // 30)
+        cf = 2.0 * (k + 1.0) * cf1 / z1 + cf0;
+        if (k == 0) cbi0 = cf;
+        if ((k == 2 * (k / 2)) && (k != 0)) {
+          csk0 = csk0 + 4.0 * cf / static_cast<RealType>(k);
+        }
+        cbs = cbs + 2.0 * cf;
+        cf0 = cf1;
+        cf1 = cf;
+      }
+      cs0  = Kokkos::exp(z1) / (cbs - cf);
+      cbi0 = cbi0 * cs0;
+      cbk0 = -(Kokkos::log(0.5 * z1) + el) * cbi0 + cs0 * csk0;
+    } else {  // Using asymptotic expansion (6.2.2) for |z|>joint_val
+              // (default:9)
+      CmplxType ca0  = Kokkos::sqrt(pi / (2.0 * z1)) * Kokkos::exp(-z1);
+      CmplxType cbkl = CmplxType(1.0, 0.0);
+      CmplxType cr   = CmplxType(1.0, 0.0);
+      for (int k = 1; k <= 30; k++) {
+        cr   = 0.125 * cr * (0.0 - pow(2.0 * k - 1.0, 2.0)) / (k * z1);
+        cbkl = cbkl + cr;
+      }
+      cbk0 = ca0 * cbkl;
+    }
+    if (z.real() < 0.0) {  // Apply (6.4.4)
+      if (z.imag() < 0.0)
+        cbk0 = cbk0 + ci * pi * cyl_bessel_i0<CmplxType, RealType, IntType>(z);
+      if (z.imag() >= 0.0)
+        cbk0 = cbk0 - ci * pi * cyl_bessel_i0<CmplxType, RealType, IntType>(z);
+    }
+  }
+  return cbk0;
+}
+
+//! Compute modified Bessel function I1(z) of the first kind of order one
+//! for a complex argument
+template <class CmplxType, class RealType, class IntType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_i1(const CmplxType& z,
+                                               const RealType& joint_val = 25,
+                                               const IntType& bw_start   = 70) {
+  // This function is converted and modified from the corresponding Fortran
+  // programs CIKNB and CIK01 in S. Zhang & J. Jin "Computation of Special
+  // Functions" (Wiley, 1996).
+  //    Input :  z         --- Complex argument
+  //             joint_val --- Joint point of abs(z) separating small and large
+  //                           argument regions
+  //             bw_start  --- Starting point for backward recurrence
+  //    Output:  cbi1      --- I1(z)
+  CmplxType cbi1;
+  const RealType pi    = M_PI;
+  const RealType b[12] = {-0.375,
+                          -1.171875e-1,
+                          -1.025390625e-1,
+                          -1.4419555664063e-1,
+                          -2.7757644653320e-1,
+                          -6.7659258842468e-1,
+                          -1.9935317337513,
+                          -6.8839142681099,
+                          -2.7248827311269e1,
+                          -1.2159789187654e2,
+                          -6.0384407670507e2,
+                          -3.3022722944809e3};
+
+  RealType a0  = Kokkos::abs(z);
+  CmplxType z1 = z;
+
+  if (a0 < 1e-100) {  // Treat z=0 as a special case
+    cbi1 = CmplxType(0.0, 0.0);
+  } else {
+    if (z.real() < 0.0) z1 = -z;
+    if (a0 <= joint_val) {  // Using backward recurrence for |z|<=joint_val
+                            // (default:25)
+      CmplxType cbs = CmplxType(0.0, 0.0);
+      // CmplxType csk0 = CmplxType(0.0,0.0);
+      CmplxType cf0 = CmplxType(0.0, 0.0);
+      CmplxType cf1 = CmplxType(1e-100, 0.0);
+      CmplxType cf, cs0;
+      for (int k = bw_start; k >= 0; k--) {  // Backward recurrence (default:
+                                             // 70)
+        cf = 2.0 * (k + 1.0) * cf1 / z1 + cf0;
+        if (k == 1) cbi1 = cf;
+        // if ((k == 2*(k/2)) && (k != 0)) {
+        //  csk0 = csk0+4.0*cf/static_cast<RealType>(k);
+        //}
+        cbs = cbs + 2.0 * cf;
+        cf0 = cf1;
+        cf1 = cf;
+      }
+      cs0  = Kokkos::exp(z1) / (cbs - cf);
+      cbi1 = cbi1 * cs0;
+    } else {  // Using asymptotic expansion (6.2.1) for |z|>joint_val
+              // (default:25)
+      CmplxType ca = Kokkos::exp(z1) / Kokkos::sqrt(2.0 * pi * z1);
+      cbi1         = CmplxType(1.0, 0.0);
+      CmplxType zr = 1.0 / z1;
+      for (int k = 1; k <= 12; k++) {
+        cbi1 = cbi1 + b[k - 1] * Kokkos::pow(zr, 1.0 * k);
+      }
+      cbi1 = ca * cbi1;
+    }
+    if (z.real() < 0.0) {  // Apply (6.4.4)
+      cbi1 = -cbi1;
+    }
+  }
+  return cbi1;
+}
+
+//! Compute modified Bessel function K1(z) of the second kind of order one
+//! for a complex argument
+template <class CmplxType, class RealType, class IntType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_k1(const CmplxType& z,
+                                               const RealType& joint_val = 9,
+                                               const IntType& bw_start   = 30) {
+  // This function is converted and modified from the corresponding Fortran
+  // programs CIKNB and CIK01 in S. Zhang & J. Jin "Computation of Special
+  // Functions" (Wiley, 1996).
+  //    Input :  z         --- Complex argument
+  //             joint_val --- Joint point of abs(z) separating small and large
+  //                           argument regions
+  //             bw_start  --- Starting point for backward recurrence
+  //    Output:  cbk1      --- K1(z)
+  using Kokkos::Experimental::infinity;
+  using Kokkos::Experimental::pow;
+
+  auto const inf = infinity<RealType>::value;
+
+  CmplxType cbk0, cbi0, cbk1, cbi1;
+  const RealType pi = M_PI;
+  const RealType el = 0.57721566490153286060651209008240;
+
+  RealType a0  = Kokkos::abs(z);
+  CmplxType ci = CmplxType(0.0, 1.0);
+  CmplxType z1 = z;
+
+  if (a0 < 1e-100) {  // Treat z=0 as a special case
+    cbk1 = CmplxType(inf, 0.0);
+  } else {
+    if (z.real() < 0.0) z1 = -z;
+    if (a0 <= joint_val) {  // Using backward recurrence for |z|<=joint_val
+                            // (default:9)
+      CmplxType cbs  = CmplxType(0.0, 0.0);
+      CmplxType csk0 = CmplxType(0.0, 0.0);
+      CmplxType cf0  = CmplxType(0.0, 0.0);
+      CmplxType cf1  = CmplxType(1e-100, 0.0);
+      CmplxType cf, cs0;
+      for (int k = bw_start; k >= 0; k--) {  // Backward recurrence (default:
+                                             // 30)
+        cf = 2.0 * (k + 1.0) * cf1 / z1 + cf0;
+        if (k == 1) cbi1 = cf;
+        if (k == 0) cbi0 = cf;
+        if ((k == 2 * (k / 2)) && (k != 0)) {
+          csk0 = csk0 + 4.0 * cf / static_cast<RealType>(k);
+        }
+        cbs = cbs + 2.0 * cf;
+        cf0 = cf1;
+        cf1 = cf;
+      }
+      cs0  = Kokkos::exp(z1) / (cbs - cf);
+      cbi0 = cbi0 * cs0;
+      cbi1 = cbi1 * cs0;
+      cbk0 = -(Kokkos::log(0.5 * z1) + el) * cbi0 + cs0 * csk0;
+      cbk1 = (1.0 / z1 - cbi1 * cbk0) / cbi0;
+    } else {  // Using asymptotic expansion (6.2.2) for |z|>joint_val
+              // (default:9)
+      CmplxType ca0  = Kokkos::sqrt(pi / (2.0 * z1)) * Kokkos::exp(-z1);
+      CmplxType cbkl = CmplxType(1.0, 0.0);
+      CmplxType cr   = CmplxType(1.0, 0.0);
+      for (int k = 1; k <= 30; k++) {
+        cr   = 0.125 * cr * (4.0 - pow(2.0 * k - 1.0, 2.0)) / (k * z1);
+        cbkl = cbkl + cr;
+      }
+      cbk1 = ca0 * cbkl;
+    }
+    if (z.real() < 0.0) {  // Apply (6.4.4)
+      if (z.imag() < 0.0)
+        cbk1 = -cbk1 - ci * pi * cyl_bessel_i1<CmplxType, RealType, IntType>(z);
+      if (z.imag() >= 0.0)
+        cbk1 = -cbk1 + ci * pi * cyl_bessel_i1<CmplxType, RealType, IntType>(z);
+    }
+  }
+  return cbk1;
+}
+
+//! Compute Hankel function H10(z) of the first kind of order zero
+//! for a complex argument
+template <class CmplxType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_h10(const CmplxType& z) {
+  // This function is converted and modified from the corresponding Fortran
+  // programs CH12N in S. Zhang & J. Jin "Computation of Special Functions"
+  //(Wiley, 1996).
+  using RealType = typename CmplxType::value_type;
+  using Kokkos::Experimental::infinity;
+
+  auto const inf = infinity<RealType>::value;
+
+  CmplxType ch10, cbk0, cbj0, cby0;
+  const RealType pi = M_PI;
+  CmplxType ci      = CmplxType(0.0, 1.0);
+
+  if ((z.real() == 0.0) && (z.imag() == 0.0)) {
+    ch10 = CmplxType(1.0, -inf);
+  } else if (z.imag() <= 0.0) {
+    cbj0 = cyl_bessel_j0<CmplxType, RealType, int>(z);
+    cby0 = cyl_bessel_y0<CmplxType, RealType, int>(z);
+    ch10 = cbj0 + ci * cby0;
+  } else {  //(z.imag() > 0.0)
+    cbk0 = cyl_bessel_k0<CmplxType, RealType, int>(-ci * z, 18.0, 70);
+    ch10 = 2.0 / (pi * ci) * cbk0;
+  }
+
+  return ch10;
+}
+
+//! Compute Hankel function H11(z) of the first kind of order one
+//! for a complex argument
+template <class CmplxType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_h11(const CmplxType& z) {
+  // This function is converted and modified from the corresponding Fortran
+  // programs CH12N in S. Zhang & J. Jin "Computation of Special Functions"
+  //(Wiley, 1996).
+  using RealType = typename CmplxType::value_type;
+  using Kokkos::Experimental::infinity;
+
+  auto const inf = infinity<RealType>::value;
+
+  CmplxType ch11, cbk1, cbj1, cby1;
+  const RealType pi = M_PI;
+  CmplxType ci      = CmplxType(0.0, 1.0);
+
+  if ((z.real() == 0.0) && (z.imag() == 0.0)) {
+    ch11 = CmplxType(0.0, -inf);
+  } else if (z.imag() <= 0.0) {
+    cbj1 = cyl_bessel_j1<CmplxType, RealType, int>(z);
+    cby1 = cyl_bessel_y1<CmplxType, RealType, int>(z);
+    ch11 = cbj1 + ci * cby1;
+  } else {  //(z.imag() > 0.0)
+    cbk1 = cyl_bessel_k1<CmplxType, RealType, int>(-ci * z, 18.0, 70);
+    ch11 = -2.0 / pi * cbk1;
+  }
+
+  return ch11;
+}
+
+//! Compute Hankel function H20(z) of the second kind of order zero
+//! for a complex argument
+template <class CmplxType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_h20(const CmplxType& z) {
+  // This function is converted and modified from the corresponding Fortran
+  // programs CH12N in S. Zhang & J. Jin "Computation of Special Functions"
+  //(Wiley, 1996).
+  using RealType = typename CmplxType::value_type;
+  using Kokkos::Experimental::infinity;
+
+  auto const inf = infinity<RealType>::value;
+
+  CmplxType ch20, cbk0, cbj0, cby0;
+  const RealType pi = M_PI;
+  CmplxType ci      = CmplxType(0.0, 1.0);
+
+  if ((z.real() == 0.0) && (z.imag() == 0.0)) {
+    ch20 = CmplxType(1.0, inf);
+  } else if (z.imag() >= 0.0) {
+    cbj0 = cyl_bessel_j0<CmplxType, RealType, int>(z);
+    cby0 = cyl_bessel_y0<CmplxType, RealType, int>(z);
+    ch20 = cbj0 - ci * cby0;
+  } else {  //(z.imag() < 0.0)
+    cbk0 = cyl_bessel_k0<CmplxType, RealType, int>(ci * z, 18.0, 70);
+    ch20 = 2.0 / pi * ci * cbk0;
+  }
+
+  return ch20;
+}
+
+//! Compute Hankel function H21(z) of the second kind of order one
+//! for a complex argument
+template <class CmplxType>
+KOKKOS_INLINE_FUNCTION CmplxType cyl_bessel_h21(const CmplxType& z) {
+  // This function is converted and modified from the corresponding Fortran
+  // programs CH12N in S. Zhang & J. Jin "Computation of Special Functions"
+  //(Wiley, 1996).
+  using RealType = typename CmplxType::value_type;
+  using Kokkos::Experimental::infinity;
+
+  auto const inf = infinity<RealType>::value;
+
+  CmplxType ch21, cbk1, cbj1, cby1;
+  const RealType pi = M_PI;
+  CmplxType ci      = CmplxType(0.0, 1.0);
+
+  if ((z.real() == 0.0) && (z.imag() == 0.0)) {
+    ch21 = CmplxType(0.0, inf);
+  } else if (z.imag() >= 0.0) {
+    cbj1 = cyl_bessel_j1<CmplxType, RealType, int>(z);
+    cby1 = cyl_bessel_y1<CmplxType, RealType, int>(z);
+    ch21 = cbj1 - ci * cby1;
+  } else {  //(z.imag() < 0.0)
+    cbk1 = cyl_bessel_k1<CmplxType, RealType, int>(ci * z, 18.0, 70);
+    ch21 = -2.0 / pi * cbk1;
+  }
+
+  return ch21;
+}
+
+}  // namespace Experimental
+}  // namespace Kokkos
+
+#endif
diff --git a/packages/kokkos/core/src/Kokkos_MemoryPool.hpp b/packages/kokkos/core/src/Kokkos_MemoryPool.hpp
index 2cafac1aea462ec29fe1d1cb853cb374ea7e8109..c814e5a22a32d31e1047f52ac55438934c8194d3 100644
--- a/packages/kokkos/core/src/Kokkos_MemoryPool.hpp
+++ b/packages/kokkos/core/src/Kokkos_MemoryPool.hpp
@@ -524,7 +524,7 @@ class MemoryPool {
     // Fast query clock register 'tic' to pseudo-randomize
     // the guess for which block within a superblock should
     // be claimed.  If not available then a search occurs.
-#if defined(KOKKOS_ENABLE_SYCL) && !defined(KOKKOS_ARCH_INTEL_GEN)
+#if defined(KOKKOS_ENABLE_SYCL) && !defined(KOKKOS_ARCH_INTEL_GPU)
     const uint32_t block_id_hint = alloc_size;
 #else
     const uint32_t block_id_hint =
@@ -585,19 +585,6 @@ class MemoryPool {
               (uint64_t(sb_id) << m_sb_size_lg2)       // superblock memory
               + (uint64_t(result.first) << size_lg2);  // block memory
 
-#if 0
-  printf( "  MemoryPool(0x%lx) pointer(0x%lx) allocate(%lu) sb_id(%d) sb_state(0x%x) block_size(%d) block_capacity(%d) block_id(%d) block_claimed(%d)\n"
-        , (uintptr_t)m_sb_state_array
-        , (uintptr_t)p
-        , alloc_size
-        , sb_id
-        , sb_state 
-        , (1u << size_lg2)
-        , (1u << count_lg2)
-        , result.first 
-        , result.second );
-#endif
-
           break;  // Success
         }
       }
@@ -740,7 +727,8 @@ class MemoryPool {
 
     // Determine which superblock and block
     const ptrdiff_t d =
-        ((char *)p) - ((char *)(m_sb_state_array + m_data_offset));
+        static_cast<char *>(p) -
+        reinterpret_cast<char *>(m_sb_state_array + m_data_offset);
 
     // Verify contained within the memory pool's superblocks:
     const int ok_contains =
@@ -772,29 +760,10 @@ class MemoryPool {
         const int result = CB::release(sb_state_array, bit, block_state);
 
         ok_dealloc_once = 0 <= result;
-
-#if 0
-  printf( "  MemoryPool(0x%lx) pointer(0x%lx) deallocate sb_id(%d) block_size(%d) block_capacity(%d) block_id(%d) block_claimed(%d)\n"
-        , (uintptr_t)m_sb_state_array
-        , (uintptr_t)p
-        , sb_id
-        , (1u << block_size_lg2)
-        , (1u << (m_sb_size_lg2 - block_size_lg2))
-        , bit
-        , result );
-#endif
       }
     }
 
     if (!ok_contains || !ok_block_aligned || !ok_dealloc_once) {
-#if 0
-  printf( "  MemoryPool(0x%lx) pointer(0x%lx) deallocate ok_contains(%d) ok_block_aligned(%d) ok_dealloc_once(%d)\n"
-        , (uintptr_t)m_sb_state_array
-        , (uintptr_t)p
-        , int(ok_contains)
-        , int(ok_block_aligned)
-        , int(ok_dealloc_once) );
-#endif
       Kokkos::abort("Kokkos MemoryPool::deallocate given erroneous pointer");
     }
   }
diff --git a/packages/kokkos/core/src/Kokkos_MemoryTraits.hpp b/packages/kokkos/core/src/Kokkos_MemoryTraits.hpp
index f23442b793f5eeca8e0c1b22df6468271df96b73..e3cee93e257b154b73df1d0c40514040d7083f22 100644
--- a/packages/kokkos/core/src/Kokkos_MemoryTraits.hpp
+++ b/packages/kokkos/core/src/Kokkos_MemoryTraits.hpp
@@ -46,7 +46,6 @@
 #define KOKKOS_MEMORYTRAITS_HPP
 
 #include <impl/Kokkos_Traits.hpp>
-#include <impl/Kokkos_Tags.hpp>
 
 //----------------------------------------------------------------------------
 
@@ -119,6 +118,15 @@ enum : unsigned {
   MEMORY_ALIGNMENT_THRESHOLD = KOKKOS_MEMORY_ALIGNMENT_THRESHOLD
 };
 
+// ------------------------------------------------------------------ //
+//  this identifies the default memory trait
+//
+template <typename Tp>
+struct is_default_memory_trait : std::false_type {};
+
+template <>
+struct is_default_memory_trait<Kokkos::MemoryTraits<0>> : std::true_type {};
+
 }  // namespace Impl
 }  // namespace Kokkos
 
diff --git a/packages/kokkos/core/src/Kokkos_NumericTraits.hpp b/packages/kokkos/core/src/Kokkos_NumericTraits.hpp
index b9380cbe02b42a04c5b21b6cb8408016049d15f8..1999d46f3c4087dab1192c350da8ba844199a8fe 100644
--- a/packages/kokkos/core/src/Kokkos_NumericTraits.hpp
+++ b/packages/kokkos/core/src/Kokkos_NumericTraits.hpp
@@ -56,11 +56,11 @@ namespace Kokkos {
 namespace Experimental {
 namespace Impl {
 // clang-format off
-template <class> struct infinity_helper;
+template <class> struct infinity_helper {};
 template <> struct infinity_helper<float> { static constexpr float value = HUGE_VALF; };
 template <> struct infinity_helper<double> { static constexpr double value = HUGE_VAL; };
 template <> struct infinity_helper<long double> { static constexpr long double value = HUGE_VALL; };
-template <class> struct finite_min_helper;
+template <class> struct finite_min_helper {};
 template <> struct finite_min_helper<bool> { static constexpr bool value = false; };
 template <> struct finite_min_helper<char> { static constexpr char value = CHAR_MIN; };
 template <> struct finite_min_helper<signed char> { static constexpr signed char value = SCHAR_MIN; };
@@ -76,7 +76,7 @@ template <> struct finite_min_helper<unsigned long long int> { static constexpr
 template <> struct finite_min_helper<float> { static constexpr float value = -FLT_MAX; };
 template <> struct finite_min_helper<double> { static constexpr double value = -DBL_MAX; };
 template <> struct finite_min_helper<long double> { static constexpr long double value = -LDBL_MAX; };
-template <class> struct finite_max_helper;
+template <class> struct finite_max_helper {};
 template <> struct finite_max_helper<bool> { static constexpr bool value = true; };
 template <> struct finite_max_helper<char> { static constexpr char value = CHAR_MAX; };
 template <> struct finite_max_helper<signed char> { static constexpr signed char value = SCHAR_MAX; };
@@ -92,7 +92,7 @@ template <> struct finite_max_helper<unsigned long long int> { static constexpr
 template <> struct finite_max_helper<float> { static constexpr float value = FLT_MAX; };
 template <> struct finite_max_helper<double> { static constexpr double value = DBL_MAX; };
 template <> struct finite_max_helper<long double> { static constexpr long double value = LDBL_MAX; };
-template <class> struct epsilon_helper;
+template <class> struct epsilon_helper {};
 namespace{
   // FIXME workaround for LDL_EPSILON with XL
   template<typename T>
@@ -115,15 +115,15 @@ template <> struct epsilon_helper<long double> {
   static constexpr long double value = LDBL_EPSILON;
 #endif
 };
-template <class> struct round_error_helper;
+template <class> struct round_error_helper {};
 template <> struct round_error_helper<float> { static constexpr float value = 0.5F; };
 template <> struct round_error_helper<double> { static constexpr double value = 0.5; };
 template <> struct round_error_helper<long double> { static constexpr long double value = 0.5L; };
-template <class> struct norm_min_helper;
+template <class> struct norm_min_helper {};
 template <> struct norm_min_helper<float> { static constexpr float value = FLT_MIN; };
 template <> struct norm_min_helper<double> { static constexpr double value = DBL_MIN; };
 template <> struct norm_min_helper<long double> { static constexpr long double value = LDBL_MIN; };
-template <class> struct digits_helper;
+template <class> struct digits_helper {};
 template <> struct digits_helper<bool> { static constexpr int value = 1; };
 template <> struct digits_helper<char> { static constexpr int value = CHAR_BIT - std::is_signed<char>::value; };
 template <> struct digits_helper<signed char> { static constexpr int value = CHAR_BIT - 1; };
@@ -139,11 +139,13 @@ template <> struct digits_helper<unsigned long long int> { static constexpr int
 template <> struct digits_helper<float> { static constexpr int value = FLT_MANT_DIG; };
 template <> struct digits_helper<double> { static constexpr int value = DBL_MANT_DIG; };
 template <> struct digits_helper<long double> { static constexpr int value = LDBL_MANT_DIG; };
-template <class> struct digits10_helper;
+template <class> struct digits10_helper {};
 template <> struct digits10_helper<bool> { static constexpr int value = 0; };
-constexpr double log10_2 = 2.41;
+// The fraction 643/2136 approximates log10(2) to 7 significant digits.
+// Workaround GCC compiler bug with -frounding-math that prevented the
+// floating-point expression to be evaluated at compile time.
 #define DIGITS10_HELPER_INTEGRAL(TYPE) \
-template <> struct digits10_helper<TYPE> { static constexpr int value = digits_helper<TYPE>::value * log10_2; };
+template <> struct digits10_helper<TYPE> { static constexpr int value = digits_helper<TYPE>::value * 643L / 2136; };
 DIGITS10_HELPER_INTEGRAL(char)
 DIGITS10_HELPER_INTEGRAL(signed char)
 DIGITS10_HELPER_INTEGRAL(unsigned char)
@@ -159,15 +161,29 @@ DIGITS10_HELPER_INTEGRAL(unsigned long long int)
 template <> struct digits10_helper<float> { static constexpr int value = FLT_DIG; };
 template <> struct digits10_helper<double> { static constexpr int value = DBL_DIG; };
 template <> struct digits10_helper<long double> { static constexpr int value = LDBL_DIG; };
-template <class> struct max_digits10_helper;
-// FIXME not sure why were not defined in my <cfloat>
-//template <> struct max_digits10_helper<float> { static constexpr int value = FLT_DECIMAL_DIG; };
-//template <> struct max_digits10_helper<double> { static constexpr int value = DBL_DECIMAL_DIG; };
-//template <> struct max_digits10_helper<long double> { static constexpr int value = LDBL_DECIMAL_DIG; };
-template <> struct max_digits10_helper<float> { static constexpr int value = 9; };
-template <> struct max_digits10_helper<double> { static constexpr int value = 17; };
-template <> struct max_digits10_helper<long double> { static constexpr int value = 21; };
-template <class> struct radix_helper;
+template <class> struct max_digits10_helper {};
+// Approximate ceil(digits<T>::value * log10(2) + 1)
+#define MAX_DIGITS10_HELPER(TYPE) \
+template <> struct max_digits10_helper<TYPE> { static constexpr int value = (digits_helper<TYPE>::value * 643L + 2135) / 2136 + 1; };
+#ifdef FLT_DECIMAL_DIG
+template <> struct max_digits10_helper<float> { static constexpr int value = FLT_DECIMAL_DIG; };
+#else
+MAX_DIGITS10_HELPER(float)
+#endif
+#ifdef DBL_DECIMAL_DIG
+template <> struct max_digits10_helper<double> { static constexpr int value = DBL_DECIMAL_DIG; };
+#else
+MAX_DIGITS10_HELPER(double)
+#endif
+#ifdef DECIMAL_DIG
+template <> struct max_digits10_helper<long double> { static constexpr int value = DECIMAL_DIG; };
+#elif LDBL_DECIMAL_DIG
+template <> struct max_digits10_helper<long double> { static constexpr int value = LDBL_DECIMAL_DIG; };
+#else
+MAX_DIGITS10_HELPER(long double)
+#endif
+#undef MAX_DIGITS10_HELPER
+template <class> struct radix_helper {};
 template <> struct radix_helper<bool> { static constexpr int value = 2; };
 template <> struct radix_helper<char> { static constexpr int value = 2; };
 template <> struct radix_helper<signed char> { static constexpr int value = 2; };
@@ -183,19 +199,19 @@ template <> struct radix_helper<unsigned long long int> { static constexpr int v
 template <> struct radix_helper<float> { static constexpr int value = FLT_RADIX; };
 template <> struct radix_helper<double> { static constexpr int value = FLT_RADIX; };
 template <> struct radix_helper<long double> { static constexpr int value = FLT_RADIX; };
-template <class> struct min_exponent_helper;
+template <class> struct min_exponent_helper {};
 template <> struct min_exponent_helper<float> { static constexpr int value = FLT_MIN_EXP; };
 template <> struct min_exponent_helper<double> { static constexpr int value = DBL_MIN_EXP; };
 template <> struct min_exponent_helper<long double> { static constexpr int value = LDBL_MIN_EXP; };
-template <class> struct min_exponent10_helper;
+template <class> struct min_exponent10_helper {};
 template <> struct min_exponent10_helper<float> { static constexpr int value = FLT_MIN_10_EXP; };
 template <> struct min_exponent10_helper<double> { static constexpr int value = DBL_MIN_10_EXP; };
 template <> struct min_exponent10_helper<long double> { static constexpr int value = LDBL_MIN_10_EXP; };
-template <class> struct max_exponent_helper;
+template <class> struct max_exponent_helper {};
 template <> struct max_exponent_helper<float> { static constexpr int value = FLT_MAX_EXP; };
 template <> struct max_exponent_helper<double> { static constexpr int value = DBL_MAX_EXP; };
 template <> struct max_exponent_helper<long double> { static constexpr int value = LDBL_MAX_EXP; };
-template <class> struct max_exponent10_helper;
+template <class> struct max_exponent10_helper{};
 template <> struct max_exponent10_helper<float> { static constexpr int value = FLT_MAX_10_EXP; };
 template <> struct max_exponent10_helper<double> { static constexpr int value = DBL_MAX_10_EXP; };
 template <> struct max_exponent10_helper<long double> { static constexpr int value = LDBL_MAX_10_EXP; };
diff --git a/packages/kokkos/core/src/Kokkos_OpenMP.hpp b/packages/kokkos/core/src/Kokkos_OpenMP.hpp
index eedba38a8456117ac03d8c21e657729673017984..8f12eceb27c46946a83c64eceec0711cca6ef2b7 100644
--- a/packages/kokkos/core/src/Kokkos_OpenMP.hpp
+++ b/packages/kokkos/core/src/Kokkos_OpenMP.hpp
@@ -62,7 +62,6 @@
 #include <Kokkos_Parallel.hpp>
 #include <Kokkos_TaskScheduler.hpp>
 #include <Kokkos_Layout.hpp>
-#include <impl/Kokkos_Tags.hpp>
 #include <impl/Kokkos_Profiling_Interface.hpp>
 #include <impl/Kokkos_ExecSpaceInitializer.hpp>
 
@@ -105,9 +104,11 @@ class OpenMP {
   /// \brief Wait until all dispatched functors complete on the given instance
   ///
   ///  This is a no-op on OpenMP
-  static void impl_static_fence(OpenMP const& = OpenMP()) noexcept;
+  static void impl_static_fence(OpenMP const&           = OpenMP(),
+                                const std::string& name = "") noexcept;
 
   void fence() const;
+  void fence(const std::string& name) const;
 
   /// \brief Does the given instance return immediately after launching
   /// a parallel algorithm
@@ -167,7 +168,7 @@ class OpenMP {
   static int impl_get_current_max_threads() noexcept;
 
   static constexpr const char* name() noexcept { return "OpenMP"; }
-  uint32_t impl_instance_id() const noexcept { return 0; }
+  uint32_t impl_instance_id() const noexcept { return 1; }
 };
 
 namespace Tools {
@@ -188,6 +189,7 @@ class OpenMPSpaceInitializer : public ExecSpaceInitializerBase {
   void initialize(const InitArguments& args) final;
   void finalize(const bool) final;
   void fence() final;
+  void fence(const std::string&) final;
   void print_configuration(std::ostream& msg, const bool detail) final;
 };
 
diff --git a/packages/kokkos/core/src/Kokkos_OpenMPTarget.hpp b/packages/kokkos/core/src/Kokkos_OpenMPTarget.hpp
index 2a57a43e63b77b7f60e4cc40bb20272e0332944a..f394f3240832a67f22e4056fa27e33500b38178c 100644
--- a/packages/kokkos/core/src/Kokkos_OpenMPTarget.hpp
+++ b/packages/kokkos/core/src/Kokkos_OpenMPTarget.hpp
@@ -56,9 +56,8 @@
 #include <Kokkos_OpenMPTargetSpace.hpp>
 #include <Kokkos_ScratchSpace.hpp>
 #include <Kokkos_Parallel.hpp>
-#include <Kokkos_TaskPolicy.hpp>
+#include <Kokkos_TaskScheduler.hpp>
 #include <Kokkos_Layout.hpp>
-#include <impl/Kokkos_Tags.hpp>
 #include <impl/Kokkos_Profiling_Interface.hpp>
 #include <KokkosExp_MDRangePolicy.hpp>
 #include <impl/Kokkos_ExecSpaceInitializer.hpp>
@@ -92,7 +91,10 @@ class OpenMPTarget {
   inline static bool in_parallel() { return omp_in_parallel(); }
 
   static void fence();
+  static void fence(const std::string&);
 
+  static void impl_static_fence();
+  static void impl_static_fence(const std::string&);
   /** \brief  Return the maximum amount of concurrency.  */
   static int concurrency();
 
@@ -115,7 +117,7 @@ class OpenMPTarget {
   }
 
   OpenMPTarget();
-  uint32_t impl_instance_id() const noexcept { return 0; }
+  uint32_t impl_instance_id() const noexcept;
 
  private:
   Impl::OpenMPTargetInternal* m_space_instance;
@@ -141,6 +143,7 @@ class OpenMPTargetSpaceInitializer : public ExecSpaceInitializerBase {
   void initialize(const InitArguments& args) final;
   void finalize(const bool) final;
   void fence() final;
+  void fence(const std::string&) final;
   void print_configuration(std::ostream& msg, const bool detail) final;
 };
 
diff --git a/packages/kokkos/core/src/Kokkos_OpenMPTargetSpace.hpp b/packages/kokkos/core/src/Kokkos_OpenMPTargetSpace.hpp
index dc5e0194ab0a8bb85a29727c664a33b6c23e2c6c..c1d338331f56cd59a9eb917a2d8f72ebb06b453b 100644
--- a/packages/kokkos/core/src/Kokkos_OpenMPTargetSpace.hpp
+++ b/packages/kokkos/core/src/Kokkos_OpenMPTargetSpace.hpp
@@ -89,6 +89,41 @@ namespace Impl {
 }  // namespace Impl
 }  // namespace Kokkos
 
+namespace Kokkos {
+namespace Impl {
+
+//----------------------------------------
+
+template <>
+struct MemorySpaceAccess<Kokkos::HostSpace,
+                         Kokkos::Experimental::OpenMPTargetSpace> {
+  enum : bool { assignable = false };
+  enum : bool { accessible = false };
+  enum : bool { deepcopy = true };
+};
+
+//----------------------------------------
+
+template <>
+struct MemorySpaceAccess<Kokkos::Experimental::OpenMPTargetSpace,
+                         Kokkos::HostSpace> {
+  enum : bool { assignable = false };
+  enum : bool { accessible = false };
+  enum : bool { deepcopy = true };
+};
+
+//----------------------------------------
+
+template <>
+struct MemorySpaceAccess<Kokkos::Experimental::OpenMPTargetSpace,
+                         Kokkos::Experimental::OpenMPTargetSpace> {
+  enum : bool { assignable = true };
+  enum : bool { accessible = true };
+  enum : bool { deepcopy = false };
+};
+}  // namespace Impl
+}  // namespace Kokkos
+
 namespace Kokkos {
 namespace Experimental {
 
@@ -179,8 +214,6 @@ class SharedAllocationRecord<Kokkos::Experimental::OpenMPTargetSpace, void>
       const RecordBase::function_type arg_dealloc = &deallocate);
 
  public:
-  std::string get_label() const;
-
   KOKKOS_INLINE_FUNCTION static SharedAllocationRecord* allocate(
       const Kokkos::Experimental::OpenMPTargetSpace& arg_space,
       const std::string& arg_label, const size_t arg_alloc_size) {
@@ -190,10 +223,6 @@ class SharedAllocationRecord<Kokkos::Experimental::OpenMPTargetSpace, void>
     return nullptr;
 #endif
   }
-
-  /**\brief  Reallocate tracked memory in the space */
-  static void* reallocate_tracked(void* const arg_alloc_ptr,
-                                  const size_t arg_alloc_size);
 };
 
 }  // namespace Impl
@@ -219,7 +248,10 @@ struct DeepCopy<Kokkos::Experimental::OpenMPTargetSpace,
                                        omp_get_default_device()));
   }
   DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
+    exec.fence(
+        "Kokkos::Impl::DeepCopy<OpenMPTargetSpace, OpenMPTargetSpace>: fence "
+        "before "
+        "copy");
     if (n > 0)
       OMPT_SAFE_CALL(omp_target_memcpy(dst, const_cast<void*>(src), n, 0, 0,
                                        omp_get_default_device(),
@@ -237,7 +269,9 @@ struct DeepCopy<Kokkos::Experimental::OpenMPTargetSpace, HostSpace,
                                        omp_get_initial_device()));
   }
   DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
+    exec.fence(
+        "Kokkos::Impl::DeepCopy<OpenMPTargetSpace, HostSpace>: fence before "
+        "copy");
     if (n > 0)
       OMPT_SAFE_CALL(omp_target_memcpy(dst, const_cast<void*>(src), n, 0, 0,
                                        omp_get_default_device(),
@@ -255,7 +289,9 @@ struct DeepCopy<HostSpace, Kokkos::Experimental::OpenMPTargetSpace,
                                        omp_get_default_device()));
   }
   DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
+    exec.fence(
+        "Kokkos::Impl::DeepCopy<HostSpace, OpenMPTargetSpace>: fence before "
+        "copy");
     if (n > 0)
       OMPT_SAFE_CALL(omp_target_memcpy(dst, const_cast<void*>(src), n, 0, 0,
                                        omp_get_initial_device(),
diff --git a/packages/kokkos/core/src/Kokkos_Parallel.hpp b/packages/kokkos/core/src/Kokkos_Parallel.hpp
index 85d1dad454ba64aa1311cf19437206768018571b..25ebe26155fed5812e161e14360811cfc660e105 100644
--- a/packages/kokkos/core/src/Kokkos_Parallel.hpp
+++ b/packages/kokkos/core/src/Kokkos_Parallel.hpp
@@ -48,23 +48,19 @@
 #ifndef KOKKOS_PARALLEL_HPP
 #define KOKKOS_PARALLEL_HPP
 
-#include <cstddef>
 #include <Kokkos_Core_fwd.hpp>
-#include <Kokkos_View.hpp>
+#include <Kokkos_DetectionIdiom.hpp>
 #include <Kokkos_ExecPolicy.hpp>
+#include <Kokkos_View.hpp>
 
 #include <impl/Kokkos_Tools.hpp>
-#include <type_traits>
-#include <typeinfo>
-
-#include <impl/Kokkos_Tags.hpp>
 #include <impl/Kokkos_Traits.hpp>
 #include <impl/Kokkos_FunctorAnalysis.hpp>
 #include <impl/Kokkos_FunctorAdapter.hpp>
 
-#ifdef KOKKOS_ENABLE_DEBUG_PRINT_KERNEL_NAMES
-#include <iostream>
-#endif
+#include <cstddef>
+#include <type_traits>
+#include <typeinfo>
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
@@ -72,34 +68,11 @@
 namespace Kokkos {
 namespace Impl {
 
-template <class T, class = void>
-struct is_detected_execution_space : std::false_type {
-  using type = not_a_type;
-};
-
 template <class T>
-struct is_detected_execution_space<T, void_t<typename T::execution_space>>
-    : std::true_type {
-  using type = typename T::execution_space;
-};
+using execution_space_t = typename T::execution_space;
 
 template <class T>
-using detected_execution_space_t =
-    typename is_detected_execution_space<T>::type;
-
-template <class T, class = void>
-struct is_detected_device_type : std::false_type {
-  using type = not_a_type;
-};
-
-template <class T>
-struct is_detected_device_type<T, void_t<typename T::device_type>>
-    : std::true_type {
-  using type = typename T::device_type;
-};
-
-template <class T>
-using detected_device_type_t = typename is_detected_device_type<T>::type;
+using device_type_t = typename T::device_type;
 
 //----------------------------------------------------------------------------
 /** \brief  Given a Functor and Execution Policy query an execution space.
@@ -112,16 +85,14 @@ using detected_device_type_t = typename is_detected_device_type<T>::type;
 
 template <class Functor, class Policy>
 struct FunctorPolicyExecutionSpace {
-  using execution_space = std::conditional_t<
-      is_detected_execution_space<Policy>::value,
-      detected_execution_space_t<Policy>,
-      std::conditional_t<
-          is_detected_execution_space<Functor>::value,
-          detected_execution_space_t<Functor>,
+  using execution_space = detected_or_t<
+      detected_or_t<
           std::conditional_t<
-              is_detected_device_type<Functor>::value,
-              detected_execution_space_t<detected_device_type_t<Functor>>,
-              Kokkos::DefaultExecutionSpace>>>;
+              is_detected<device_type_t, Functor>::value,
+              detected_t<execution_space_t, detected_t<device_type_t, Functor>>,
+              Kokkos::DefaultExecutionSpace>,
+          execution_space_t, Functor>,
+      execution_space_t, Policy>;
 };
 
 }  // namespace Impl
@@ -158,8 +129,7 @@ inline void parallel_for(
     const ExecPolicy& policy, const FunctorType& functor,
     const std::string& str = "",
     typename std::enable_if<
-        Kokkos::Impl::is_execution_policy<ExecPolicy>::value>::type* =
-        nullptr) {
+        Kokkos::is_execution_policy<ExecPolicy>::value>::type* = nullptr) {
   uint64_t kpID = 0;
 
   ExecPolicy inner_policy = policy;
@@ -200,18 +170,7 @@ inline void parallel_for(const size_t work_count, const FunctorType& functor,
 template <class ExecPolicy, class FunctorType>
 inline void parallel_for(const std::string& str, const ExecPolicy& policy,
                          const FunctorType& functor) {
-#if KOKKOS_ENABLE_DEBUG_PRINT_KERNEL_NAMES
-  Kokkos::fence();
-  std::cout << "KOKKOS_DEBUG Start parallel_for kernel: " << str << std::endl;
-#endif
-
   ::Kokkos::parallel_for(policy, functor, str);
-
-#if KOKKOS_ENABLE_DEBUG_PRINT_KERNEL_NAMES
-  Kokkos::fence();
-  std::cout << "KOKKOS_DEBUG End   parallel_for kernel: " << str << std::endl;
-#endif
-  (void)str;
 }
 
 }  // namespace Kokkos
@@ -255,9 +214,12 @@ namespace Kokkos {
 ///   // operator() or join().
 ///   using value_type = PodType;
 ///
-///   void operator () (const ExecPolicy::member_type & i, value_type& update,
-///   const bool final_pass) const; void init (value_type& update) const; void
-///   join (volatile value_type& update, volatile const value_type& input) const
+///   void operator () (const ExecPolicy::member_type & i,
+///                     value_type& update,
+///                     const bool final_pass) const;
+///   void init (value_type& update) const;
+///   void join (volatile value_type& update,
+//               volatile const value_type& input) const
 /// };
 /// \endcode
 ///
@@ -389,8 +351,7 @@ inline void parallel_scan(
     const ExecutionPolicy& policy, const FunctorType& functor,
     const std::string& str = "",
     typename std::enable_if<
-        Kokkos::Impl::is_execution_policy<ExecutionPolicy>::value>::type* =
-        nullptr) {
+        Kokkos::is_execution_policy<ExecutionPolicy>::value>::type* = nullptr) {
   uint64_t kpID                = 0;
   ExecutionPolicy inner_policy = policy;
   Kokkos::Tools::Impl::begin_parallel_scan(inner_policy, functor, str, kpID);
@@ -430,18 +391,7 @@ inline void parallel_scan(const size_t work_count, const FunctorType& functor,
 template <class ExecutionPolicy, class FunctorType>
 inline void parallel_scan(const std::string& str, const ExecutionPolicy& policy,
                           const FunctorType& functor) {
-#if KOKKOS_ENABLE_DEBUG_PRINT_KERNEL_NAMES
-  Kokkos::fence();
-  std::cout << "KOKKOS_DEBUG Start parallel_scan kernel: " << str << std::endl;
-#endif
-
   ::Kokkos::parallel_scan(policy, functor, str);
-
-#if KOKKOS_ENABLE_DEBUG_PRINT_KERNEL_NAMES
-  Kokkos::fence();
-  std::cout << "KOKKOS_DEBUG End parallel_scan kernel: " << str << std::endl;
-#endif
-  (void)str;
 }
 
 template <class ExecutionPolicy, class FunctorType, class ReturnType>
@@ -449,8 +399,7 @@ inline void parallel_scan(
     const ExecutionPolicy& policy, const FunctorType& functor,
     ReturnType& return_value, const std::string& str = "",
     typename std::enable_if<
-        Kokkos::Impl::is_execution_policy<ExecutionPolicy>::value>::type* =
-        nullptr) {
+        Kokkos::is_execution_policy<ExecutionPolicy>::value>::type* = nullptr) {
   uint64_t kpID                = 0;
   ExecutionPolicy inner_policy = policy;
   Kokkos::Tools::Impl::begin_parallel_scan(inner_policy, functor, str, kpID);
@@ -464,7 +413,8 @@ inline void parallel_scan(
 
   Kokkos::Tools::Impl::end_parallel_scan(inner_policy, functor, str, kpID);
 
-  policy.space().fence();
+  policy.space().fence(
+      "Kokkos::parallel_scan: fence due to result being a value, not a view");
 }
 
 template <class FunctorType, class ReturnType>
@@ -491,25 +441,15 @@ inline void parallel_scan(const size_t work_count, const FunctorType& functor,
 
   Kokkos::Tools::Impl::end_parallel_scan(execution_policy, functor, str, kpID);
 
-  execution_space().fence();
+  execution_space().fence(
+      "Kokkos::parallel_scan: fence after scan with return value");
 }
 
 template <class ExecutionPolicy, class FunctorType, class ReturnType>
 inline void parallel_scan(const std::string& str, const ExecutionPolicy& policy,
                           const FunctorType& functor,
                           ReturnType& return_value) {
-#if KOKKOS_ENABLE_DEBUG_PRINT_KERNEL_NAMES
-  Kokkos::fence();
-  std::cout << "KOKKOS_DEBUG Start parallel_scan kernel: " << str << std::endl;
-#endif
-
   ::Kokkos::parallel_scan(policy, functor, return_value, str);
-
-#if KOKKOS_ENABLE_DEBUG_PRINT_KERNEL_NAMES
-  Kokkos::fence();
-  std::cout << "KOKKOS_DEBUG End parallel_scan kernel: " << str << std::endl;
-#endif
-  (void)str;
 }
 
 }  // namespace Kokkos
diff --git a/packages/kokkos/core/src/Kokkos_Parallel_Reduce.hpp b/packages/kokkos/core/src/Kokkos_Parallel_Reduce.hpp
index 96242f99b0ca678e1ede6f148ae5d90a16127afe..bc613cea62b10a56f888baabbc16ed9258e041dd 100644
--- a/packages/kokkos/core/src/Kokkos_Parallel_Reduce.hpp
+++ b/packages/kokkos/core/src/Kokkos_Parallel_Reduce.hpp
@@ -811,7 +811,7 @@ struct ParallelReducePolicyType;
 template <class PolicyType, class FunctorType>
 struct ParallelReducePolicyType<
     typename std::enable_if<
-        Kokkos::Impl::is_execution_policy<PolicyType>::value>::type,
+        Kokkos::is_execution_policy<PolicyType>::value>::type,
     PolicyType, FunctorType> {
   using policy_type = PolicyType;
   static PolicyType policy(const PolicyType& policy_) { return policy_; }
@@ -948,9 +948,10 @@ parallel_reduce_needs_fence(ExecutionSpace const&, ViewLike const&) {
 template <class ExecutionSpace, class... Args>
 struct ParallelReduceFence {
   template <class... ArgsDeduced>
-  static void fence(const ExecutionSpace& ex, ArgsDeduced&&... args) {
+  static void fence(const ExecutionSpace& ex, const std::string& name,
+                    ArgsDeduced&&... args) {
     if (Impl::parallel_reduce_needs_fence(ex, (ArgsDeduced &&) args...)) {
-      ex.fence();
+      ex.fence(name);
     }
   }
 };
@@ -974,7 +975,6 @@ struct ParallelReduceFence {
  *    void join( volatile       <podType> & update ,
  *               volatile const <podType> & input ) const ;
  *
- *    using has_final = true_type;
  *    void final( <podType> & update ) const ;
  *  };
  * \endcode
@@ -991,7 +991,6 @@ struct ParallelReduceFence {
  *    void join( volatile       <podType> update[] ,
  *               volatile const <podType> input[] ) const ;
  *
- *    using has_final = true_type;
  *    void final( <podType> update[] ) const ;
  *  };
  * \endcode
@@ -1001,24 +1000,30 @@ struct ParallelReduceFence {
 
 template <class PolicyType, class FunctorType, class ReturnType>
 inline typename std::enable_if<
-    Kokkos::Impl::is_execution_policy<PolicyType>::value>::type
+    Kokkos::is_execution_policy<PolicyType>::value>::type
 parallel_reduce(const std::string& label, const PolicyType& policy,
                 const FunctorType& functor, ReturnType& return_value) {
   Impl::ParallelReduceAdaptor<PolicyType, FunctorType, ReturnType>::execute(
       label, policy, functor, return_value);
-  Impl::ParallelReduceFence<typename PolicyType::execution_space,
-                            ReturnType>::fence(policy.space(), return_value);
+  Impl::ParallelReduceFence<typename PolicyType::execution_space, ReturnType>::
+      fence(
+          policy.space(),
+          "Kokkos::parallel_reduce: fence due to result being value, not view",
+          return_value);
 }
 
 template <class PolicyType, class FunctorType, class ReturnType>
 inline typename std::enable_if<
-    Kokkos::Impl::is_execution_policy<PolicyType>::value>::type
+    Kokkos::is_execution_policy<PolicyType>::value>::type
 parallel_reduce(const PolicyType& policy, const FunctorType& functor,
                 ReturnType& return_value) {
   Impl::ParallelReduceAdaptor<PolicyType, FunctorType, ReturnType>::execute(
       "", policy, functor, return_value);
-  Impl::ParallelReduceFence<typename PolicyType::execution_space,
-                            ReturnType>::fence(policy.space(), return_value);
+  Impl::ParallelReduceFence<typename PolicyType::execution_space, ReturnType>::
+      fence(
+          policy.space(),
+          "Kokkos::parallel_reduce: fence due to result being value, not view",
+          return_value);
 }
 
 template <class FunctorType, class ReturnType>
@@ -1030,7 +1035,10 @@ inline void parallel_reduce(const size_t& policy, const FunctorType& functor,
   Impl::ParallelReduceAdaptor<policy_type, FunctorType, ReturnType>::execute(
       "", policy_type(0, policy), functor, return_value);
   Impl::ParallelReduceFence<typename policy_type::execution_space, ReturnType>::
-      fence(typename policy_type::execution_space(), return_value);
+      fence(
+          typename policy_type::execution_space(),
+          "Kokkos::parallel_reduce: fence due to result being value, not view",
+          return_value);
 }
 
 template <class FunctorType, class ReturnType>
@@ -1043,33 +1051,42 @@ inline void parallel_reduce(const std::string& label, const size_t& policy,
   Impl::ParallelReduceAdaptor<policy_type, FunctorType, ReturnType>::execute(
       label, policy_type(0, policy), functor, return_value);
   Impl::ParallelReduceFence<typename policy_type::execution_space, ReturnType>::
-      fence(typename policy_type::execution_space(), return_value);
+      fence(
+          typename policy_type::execution_space(),
+          "Kokkos::parallel_reduce: fence due to result being value, not view",
+          return_value);
 }
 
 // ReturnValue as View or Reducer: take by copy to allow for inline construction
 
 template <class PolicyType, class FunctorType, class ReturnType>
 inline typename std::enable_if<
-    Kokkos::Impl::is_execution_policy<PolicyType>::value>::type
+    Kokkos::is_execution_policy<PolicyType>::value>::type
 parallel_reduce(const std::string& label, const PolicyType& policy,
                 const FunctorType& functor, const ReturnType& return_value) {
   ReturnType return_value_impl = return_value;
   Impl::ParallelReduceAdaptor<PolicyType, FunctorType, ReturnType>::execute(
       label, policy, functor, return_value_impl);
-  Impl::ParallelReduceFence<typename PolicyType::execution_space,
-                            ReturnType>::fence(policy.space(), return_value);
+  Impl::ParallelReduceFence<typename PolicyType::execution_space, ReturnType>::
+      fence(
+          policy.space(),
+          "Kokkos::parallel_reduce: fence due to result being value, not view",
+          return_value);
 }
 
 template <class PolicyType, class FunctorType, class ReturnType>
 inline typename std::enable_if<
-    Kokkos::Impl::is_execution_policy<PolicyType>::value>::type
+    Kokkos::is_execution_policy<PolicyType>::value>::type
 parallel_reduce(const PolicyType& policy, const FunctorType& functor,
                 const ReturnType& return_value) {
   ReturnType return_value_impl = return_value;
   Impl::ParallelReduceAdaptor<PolicyType, FunctorType, ReturnType>::execute(
       "", policy, functor, return_value_impl);
-  Impl::ParallelReduceFence<typename PolicyType::execution_space,
-                            ReturnType>::fence(policy.space(), return_value);
+  Impl::ParallelReduceFence<typename PolicyType::execution_space, ReturnType>::
+      fence(
+          policy.space(),
+          "Kokkos::parallel_reduce: fence due to result being value, not view",
+          return_value);
 }
 
 template <class FunctorType, class ReturnType>
@@ -1082,7 +1099,10 @@ inline void parallel_reduce(const size_t& policy, const FunctorType& functor,
   Impl::ParallelReduceAdaptor<policy_type, FunctorType, ReturnType>::execute(
       "", policy_type(0, policy), functor, return_value_impl);
   Impl::ParallelReduceFence<typename policy_type::execution_space, ReturnType>::
-      fence(typename policy_type::execution_space(), return_value);
+      fence(
+          typename policy_type::execution_space(),
+          "Kokkos::parallel_reduce: fence due to result being value, not view",
+          return_value);
 }
 
 template <class FunctorType, class ReturnType>
@@ -1096,7 +1116,10 @@ inline void parallel_reduce(const std::string& label, const size_t& policy,
   Impl::ParallelReduceAdaptor<policy_type, FunctorType, ReturnType>::execute(
       label, policy_type(0, policy), functor, return_value_impl);
   Impl::ParallelReduceFence<typename policy_type::execution_space, ReturnType>::
-      fence(typename policy_type::execution_space(), return_value);
+      fence(
+          typename policy_type::execution_space(),
+          "Kokkos::parallel_reduce: fence due to result being value, not view",
+          return_value);
 }
 
 // No Return Argument
@@ -1106,8 +1129,7 @@ inline void parallel_reduce(
     const std::string& label, const PolicyType& policy,
     const FunctorType& functor,
     typename std::enable_if<
-        Kokkos::Impl::is_execution_policy<PolicyType>::value>::type* =
-        nullptr) {
+        Kokkos::is_execution_policy<PolicyType>::value>::type* = nullptr) {
   using ValueTraits = Kokkos::Impl::FunctorValueTraits<FunctorType, void>;
   using value_type  = std::conditional_t<(ValueTraits::StaticValueSize != 0),
                                         typename ValueTraits::value_type,
@@ -1131,8 +1153,7 @@ template <class PolicyType, class FunctorType>
 inline void parallel_reduce(
     const PolicyType& policy, const FunctorType& functor,
     typename std::enable_if<
-        Kokkos::Impl::is_execution_policy<PolicyType>::value>::type* =
-        nullptr) {
+        Kokkos::is_execution_policy<PolicyType>::value>::type* = nullptr) {
   using ValueTraits = Kokkos::Impl::FunctorValueTraits<FunctorType, void>;
   using value_type  = std::conditional_t<(ValueTraits::StaticValueSize != 0),
                                         typename ValueTraits::value_type,
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_SharedAlloc.cpp b/packages/kokkos/core/src/Kokkos_Rank.hpp
similarity index 71%
rename from packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_SharedAlloc.cpp
rename to packages/kokkos/core/src/Kokkos_Rank.hpp
index 4228b5181a0ccd68dfde87f71f92fd0a471a8e96..3603e2860891758e489471683d34438f219f84d5 100644
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_SharedAlloc.cpp
+++ b/packages/kokkos/core/src/Kokkos_Rank.hpp
@@ -42,5 +42,30 @@
 //@HEADER
 */
 
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestSharedAlloc.hpp>
+#ifndef KOKKOS_KOKKOS_RANK_HPP
+#define KOKKOS_KOKKOS_RANK_HPP
+
+#include <Kokkos_Macros.hpp>
+#include <Kokkos_Layout.hpp>  // Iterate
+
+namespace Kokkos {
+
+// Iteration Pattern
+template <unsigned N, Iterate OuterDir = Iterate::Default,
+          Iterate InnerDir = Iterate::Default>
+struct Rank {
+  static_assert(N != 0u, "Kokkos Error: rank 0 undefined");
+  static_assert(N != 1u,
+                "Kokkos Error: rank 1 is not a multi-dimensional range");
+  static_assert(N < 7u, "Kokkos Error: Unsupported rank...");
+
+  using iteration_pattern = Rank<N, OuterDir, InnerDir>;
+
+  static constexpr int rank                = N;
+  static constexpr Iterate outer_direction = OuterDir;
+  static constexpr Iterate inner_direction = InnerDir;
+};
+
+}  // end namespace Kokkos
+
+#endif  // KOKKOS_KOKKOS_RANK_HPP
diff --git a/packages/kokkos/core/src/Kokkos_SYCL.hpp b/packages/kokkos/core/src/Kokkos_SYCL.hpp
index aa720371df73cb1ad7bba8191e5c6d83c6c317c5..02095ff7b3f01f7558d8e154d1ebc49ff46d1241 100644
--- a/packages/kokkos/core/src/Kokkos_SYCL.hpp
+++ b/packages/kokkos/core/src/Kokkos_SYCL.hpp
@@ -83,7 +83,9 @@ class SYCL {
   SYCL();
   explicit SYCL(const sycl::queue&);
 
-  uint32_t impl_instance_id() const noexcept { return 0; }
+  uint32_t impl_instance_id() const noexcept {
+    return m_space_instance->impl_get_instance_id();
+  }
 
   sycl::context sycl_context() const noexcept {
     return m_space_instance->m_queue->get_context();
@@ -110,10 +112,12 @@ class SYCL {
 
   /** \brief Wait until all dispatched functors complete. A noop for OpenMP. */
   static void impl_static_fence();
+  static void impl_static_fence(const std::string&);
   void fence() const;
+  void fence(const std::string&) const;
 
   /// \brief Print configuration information to the given output stream.
-  static void print_configuration(std::ostream&, const bool detail = false);
+  void print_configuration(std::ostream&, const bool detail = false);
 
   /// \brief Free any resources being consumed by the device.
   static void impl_finalize();
@@ -131,12 +135,10 @@ class SYCL {
     sycl::device get_device() const;
 
     friend std::ostream& operator<<(std::ostream& os, const SYCLDevice& that) {
-      return that.info(os);
+      return SYCL::impl_sycl_info(os, that.m_device);
     }
 
    private:
-    std::ostream& info(std::ostream& os) const;
-
     sycl::device m_device;
   };
 
@@ -154,6 +156,9 @@ class SYCL {
   }
 
  private:
+  static std::ostream& impl_sycl_info(std::ostream& os,
+                                      const sycl::device& device);
+
   Kokkos::Impl::HostSharedPtr<Impl::SYCLInternal> m_space_instance;
 };
 
@@ -164,6 +169,7 @@ class SYCLSpaceInitializer : public Kokkos::Impl::ExecSpaceInitializerBase {
   void initialize(const InitArguments& args) final;
   void finalize(const bool) final;
   void fence() final;
+  void fence(const std::string&) final;
   void print_configuration(std::ostream& msg, const bool detail) final;
 };
 
@@ -180,6 +186,41 @@ struct DeviceTypeTraits<Kokkos::Experimental::SYCL> {
 }  // namespace Experimental
 }  // namespace Tools
 
+namespace Experimental {
+template <class... Args>
+std::vector<SYCL> partition_space(const SYCL& sycl_space, Args...) {
+#ifdef __cpp_fold_expressions
+  static_assert(
+      (... && std::is_arithmetic_v<Args>),
+      "Kokkos Error: partitioning arguments must be integers or floats");
+#endif
+
+  sycl::context context = sycl_space.sycl_context();
+  sycl::default_selector device_selector;
+  std::vector<SYCL> instances;
+  instances.reserve(sizeof...(Args));
+  for (unsigned int i = 0; i < sizeof...(Args); ++i)
+    instances.emplace_back(sycl::queue(context, device_selector));
+  return instances;
+}
+
+template <class T>
+std::vector<SYCL> partition_space(const SYCL& sycl_space,
+                                  std::vector<T>& weights) {
+  static_assert(
+      std::is_arithmetic<T>::value,
+      "Kokkos Error: partitioning arguments must be integers or floats");
+
+  sycl::context context = sycl_space.sycl_context();
+  sycl::default_selector device_selector;
+  std::vector<SYCL> instances;
+  instances.reserve(weights.size());
+  for (unsigned int i = 0; i < weights.size(); ++i)
+    instances.emplace_back(sycl::queue(context, device_selector));
+  return instances;
+}
+}  // namespace Experimental
+
 }  // namespace Kokkos
 
 #endif
diff --git a/packages/kokkos/core/src/Kokkos_SYCL_Space.hpp b/packages/kokkos/core/src/Kokkos_SYCL_Space.hpp
index 392ab0e59a7d01f42342318bb44aa172bcb4f705..15ef11024d53501356d8004c58fc94fbeca80227 100644
--- a/packages/kokkos/core/src/Kokkos_SYCL_Space.hpp
+++ b/packages/kokkos/core/src/Kokkos_SYCL_Space.hpp
@@ -49,12 +49,19 @@
 
 #ifdef KOKKOS_ENABLE_SYCL
 #include <Kokkos_Concepts.hpp>
+#include <Kokkos_HostSpace.hpp>
 #include <Kokkos_ScratchSpace.hpp>
 #include <SYCL/Kokkos_SYCL_Instance.hpp>
 #include <impl/Kokkos_SharedAlloc.hpp>
 #include <impl/Kokkos_Tools.hpp>
 
 namespace Kokkos {
+
+namespace Impl {
+template <typename T>
+struct is_sycl_type_space : public std::false_type {};
+}  // namespace Impl
+
 namespace Experimental {
 
 class SYCLDeviceUSMSpace {
@@ -118,9 +125,54 @@ class SYCLSharedUSMSpace {
  private:
   sycl::queue m_queue;
 };
+
+class SYCLHostUSMSpace {
+ public:
+  using execution_space = HostSpace::execution_space;
+  using memory_space    = SYCLHostUSMSpace;
+  using device_type     = Kokkos::Device<execution_space, memory_space>;
+  using size_type       = Impl::SYCLInternal::size_type;
+
+  SYCLHostUSMSpace();
+  explicit SYCLHostUSMSpace(sycl::queue queue);
+
+  void* allocate(const std::size_t arg_alloc_size) const;
+  void* allocate(const char* arg_label, const size_t arg_alloc_size,
+                 const size_t arg_logical_size = 0) const;
+
+  void deallocate(void* const arg_alloc_ptr,
+                  const std::size_t arg_alloc_size) const;
+  void deallocate(const char* arg_label, void* const arg_alloc_ptr,
+                  const size_t arg_alloc_size,
+                  const size_t arg_logical_size = 0) const;
+
+ private:
+  template <class, class, class, class>
+  friend class LogicalMemorySpace;
+
+ public:
+  static constexpr const char* name() { return "SYCLHostUSM"; };
+
+ private:
+  sycl::queue m_queue;
+};
+
 }  // namespace Experimental
 
 namespace Impl {
+
+template <>
+struct is_sycl_type_space<Kokkos::Experimental::SYCLDeviceUSMSpace>
+    : public std::true_type {};
+
+template <>
+struct is_sycl_type_space<Kokkos::Experimental::SYCLSharedUSMSpace>
+    : public std::true_type {};
+
+template <>
+struct is_sycl_type_space<Kokkos::Experimental::SYCLHostUSMSpace>
+    : public std::true_type {};
+
 static_assert(Kokkos::Impl::MemorySpaceAccess<
                   Kokkos::Experimental::SYCLDeviceUSMSpace,
                   Kokkos::Experimental::SYCLDeviceUSMSpace>::assignable,
@@ -131,6 +183,11 @@ static_assert(Kokkos::Impl::MemorySpaceAccess<
                   Kokkos::Experimental::SYCLSharedUSMSpace>::assignable,
               "");
 
+static_assert(Kokkos::Impl::MemorySpaceAccess<
+                  Kokkos::Experimental::SYCLDeviceUSMSpace,
+                  Kokkos::Experimental::SYCLDeviceUSMSpace>::assignable,
+              "");
+
 template <>
 struct MemorySpaceAccess<Kokkos::HostSpace,
                          Kokkos::Experimental::SYCLDeviceUSMSpace> {
@@ -148,6 +205,16 @@ struct MemorySpaceAccess<Kokkos::HostSpace,
   enum : bool { deepcopy = true };
 };
 
+template <>
+struct MemorySpaceAccess<Kokkos::HostSpace,
+                         Kokkos::Experimental::SYCLHostUSMSpace> {
+  // HostSpace::execution_space ==
+  // Experimental::SYCLHostUSMSpace::execution_space
+  enum : bool { assignable = true };
+  enum : bool { accessible = true };
+  enum : bool { deepcopy = true };
+};
+
 template <>
 struct MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
                          Kokkos::HostSpace> {
@@ -165,6 +232,18 @@ struct MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
   enum : bool { deepcopy = true };
 };
 
+template <>
+struct MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                         Kokkos::Experimental::SYCLHostUSMSpace> {
+  // Experimental::SYCLDeviceUSMSpace::execution_space !=
+  // Experimental::SYCLHostUSMSpace::execution_space
+  enum : bool { assignable = false };
+  enum : bool {
+    accessible = true
+  };  // Experimental::SYCLDeviceUSMSpace::execution_space
+  enum : bool { deepcopy = true };
+};
+
 //----------------------------------------
 // SYCLSharedUSMSpace::execution_space == SYCL
 // SYCLSharedUSMSpace accessible to both SYCL and Host
@@ -191,17 +270,46 @@ struct MemorySpaceAccess<Kokkos::Experimental::SYCLSharedUSMSpace,
 };
 
 template <>
-struct MemorySpaceAccess<
-    Kokkos::Experimental::SYCLDeviceUSMSpace,
-    Kokkos::ScratchMemorySpace<Kokkos::Experimental::SYCL>> {
+struct MemorySpaceAccess<Kokkos::Experimental::SYCLSharedUSMSpace,
+                         Kokkos::Experimental::SYCLHostUSMSpace> {
+  // Experimental::SYCLSharedUSMSpace::execution_space !=
+  // Experimental::SYCLHostUSMSpace::execution_space
   enum : bool { assignable = false };
-  enum : bool { accessible = true };
-  enum : bool { deepcopy = false };
+  enum : bool {
+    accessible = true
+  };  // Experimental::SYCLSharedUSMSpace::execution_space
+  enum : bool { deepcopy = true };
+};
+
+template <>
+struct MemorySpaceAccess<Kokkos::Experimental::SYCLHostUSMSpace,
+                         Kokkos::HostSpace> {
+  enum : bool { assignable = false };  // Cannot access from SYCL
+  enum : bool {
+    accessible = true
+  };  // Experimental::SYCLHostUSMSpace::execution_space
+  enum : bool { deepcopy = true };
+};
+
+template <>
+struct MemorySpaceAccess<Kokkos::Experimental::SYCLHostUSMSpace,
+                         Kokkos::Experimental::SYCLDeviceUSMSpace> {
+  enum : bool { assignable = false };  // Cannot access from Host
+  enum : bool { accessible = false };
+  enum : bool { deepcopy = true };
+};
+
+template <>
+struct MemorySpaceAccess<Kokkos::Experimental::SYCLHostUSMSpace,
+                         Kokkos::Experimental::SYCLSharedUSMSpace> {
+  enum : bool { assignable = false };  // different execution_space
+  enum : bool { accessible = true };   // same accessibility
+  enum : bool { deepcopy = true };
 };
 
 template <>
 struct MemorySpaceAccess<
-    Kokkos::Experimental::SYCLSharedUSMSpace,
+    Kokkos::Experimental::SYCLDeviceUSMSpace,
     Kokkos::ScratchMemorySpace<Kokkos::Experimental::SYCL>> {
   enum : bool { assignable = false };
   enum : bool { accessible = true };
@@ -276,6 +384,37 @@ class SharedAllocationRecord<Kokkos::Experimental::SYCLSharedUSMSpace, void>
       const RecordBase::function_type arg_dealloc = &base_t::deallocate);
 };
 
+template <>
+class SharedAllocationRecord<Kokkos::Experimental::SYCLHostUSMSpace, void>
+    : public SharedAllocationRecordCommon<
+          Kokkos::Experimental::SYCLHostUSMSpace> {
+ private:
+  friend class SharedAllocationRecordCommon<
+      Kokkos::Experimental::SYCLHostUSMSpace>;
+  using base_t =
+      SharedAllocationRecordCommon<Kokkos::Experimental::SYCLHostUSMSpace>;
+  using RecordBase = SharedAllocationRecord<void, void>;
+
+  SharedAllocationRecord(const SharedAllocationRecord&) = delete;
+  SharedAllocationRecord(SharedAllocationRecord&&)      = delete;
+  SharedAllocationRecord& operator=(const SharedAllocationRecord&) = delete;
+  SharedAllocationRecord& operator=(SharedAllocationRecord&&) = delete;
+
+  static RecordBase s_root_record;
+
+  const Kokkos::Experimental::SYCLHostUSMSpace m_space;
+
+ protected:
+  ~SharedAllocationRecord();
+
+  SharedAllocationRecord() = default;
+
+  SharedAllocationRecord(
+      const Kokkos::Experimental::SYCLHostUSMSpace& arg_space,
+      const std::string& arg_label, const size_t arg_alloc_size,
+      const RecordBase::function_type arg_dealloc = &base_t::deallocate);
+};
+
 }  // namespace Impl
 
 }  // namespace Kokkos
diff --git a/packages/kokkos/core/src/Kokkos_ScratchSpace.hpp b/packages/kokkos/core/src/Kokkos_ScratchSpace.hpp
index 2eebf5365e71d2c5cf42c356951ccec9d041fe14..bb740cfb86a966aefd4ac1ab6c9233ab81e0a97d 100644
--- a/packages/kokkos/core/src/Kokkos_ScratchSpace.hpp
+++ b/packages/kokkos/core/src/Kokkos_ScratchSpace.hpp
@@ -148,10 +148,10 @@ class ScratchMemorySpace {
                                             const IntType& size_L0,
                                             void* ptr_L1           = nullptr,
                                             const IntType& size_L1 = 0)
-      : m_iter_L0((char*)ptr_L0),
-        m_iter_L1((char*)ptr_L1),
-        m_end_L0((char*)ptr_L0 + size_L0),
-        m_end_L1((char*)ptr_L1 + size_L1),
+      : m_iter_L0(static_cast<char*>(ptr_L0)),
+        m_iter_L1(static_cast<char*>(ptr_L1)),
+        m_end_L0(static_cast<char*>(ptr_L0) + size_L0),
+        m_end_L1(static_cast<char*>(ptr_L1) + size_L1),
         m_multiplier(1),
         m_offset(0),
         m_default_level(0) {}
diff --git a/packages/kokkos/core/src/Kokkos_Serial.hpp b/packages/kokkos/core/src/Kokkos_Serial.hpp
index 4d5bb2410bfaabf6f752acf55795c9d7ef82016d..9c8ae70721e58bff5a91da3b99e3630ffe0c273a 100644
--- a/packages/kokkos/core/src/Kokkos_Serial.hpp
+++ b/packages/kokkos/core/src/Kokkos_Serial.hpp
@@ -53,6 +53,8 @@
 
 #include <cstddef>
 #include <iosfwd>
+#include <mutex>
+#include <thread>
 #include <Kokkos_Core_fwd.hpp>
 #include <Kokkos_Parallel.hpp>
 #include <Kokkos_TaskScheduler.hpp>
@@ -60,12 +62,12 @@
 #include <Kokkos_HostSpace.hpp>
 #include <Kokkos_ScratchSpace.hpp>
 #include <Kokkos_MemoryTraits.hpp>
-#include <impl/Kokkos_Tags.hpp>
 #include <impl/Kokkos_HostThreadTeam.hpp>
 #include <impl/Kokkos_FunctorAnalysis.hpp>
 #include <impl/Kokkos_FunctorAdapter.hpp>
 #include <impl/Kokkos_Tools.hpp>
 #include <impl/Kokkos_ExecSpaceInitializer.hpp>
+#include <impl/Kokkos_HostSharedPtr.hpp>
 
 #include <KokkosExp_MDRangePolicy.hpp>
 
@@ -73,6 +75,32 @@
 
 namespace Kokkos {
 
+namespace Impl {
+class SerialInternal {
+ public:
+  SerialInternal() = default;
+
+  bool is_initialized();
+
+  void initialize();
+
+  void finalize();
+
+  static SerialInternal& singleton();
+
+  std::mutex m_thread_team_data_mutex;
+
+  // Resize thread team data scratch memory
+  void resize_thread_team_data(size_t pool_reduce_bytes,
+                               size_t team_reduce_bytes,
+                               size_t team_shared_bytes,
+                               size_t thread_local_bytes);
+
+  HostThreadTeamData m_thread_team_data;
+  bool m_is_initialized = false;
+};
+}  // namespace Impl
+
 /// \class Serial
 /// \brief Kokkos device for non-parallel execution
 ///
@@ -107,6 +135,8 @@ class Serial {
 
   //@}
 
+  Serial();
+
   /// \brief True if and only if this method is being called in a
   ///   thread-parallel function.
   ///
@@ -121,9 +151,26 @@ class Serial {
   /// return asynchronously, before the functor completes.  This
   /// method does not return until all dispatched functors on this
   /// device have completed.
-  static void impl_static_fence() {}
+  static void impl_static_fence() {
+    impl_static_fence(
+        "Kokkos::Serial::impl_static_fence: Unnamed Static Fence");
+  }
+  static void impl_static_fence(const std::string& name) {
+    Kokkos::Tools::Experimental::Impl::profile_fence_event<Kokkos::Serial>(
+        name,
+        Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+            GlobalDeviceSynchronization,
+        []() {});  // TODO: correct device ID
+    Kokkos::memory_fence();
+  }
 
-  void fence() const {}
+  void fence() const { fence("Kokkos::Serial::fence: Unnamed Instance Fence"); }
+  void fence(const std::string& name) const {
+    Kokkos::Tools::Experimental::Impl::profile_fence_event<Kokkos::Serial>(
+        name, Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{1},
+        []() {});  // TODO: correct device ID
+    Kokkos::memory_fence();
+  }
 
   /** \brief  Return the maximum amount of concurrency.  */
   static int concurrency() { return 1; }
@@ -153,9 +200,24 @@ class Serial {
     return impl_thread_pool_size(0);
   }
 
-  uint32_t impl_instance_id() const noexcept { return 0; }
+  uint32_t impl_instance_id() const noexcept { return 1; }
 
   static const char* name();
+
+  Impl::SerialInternal* impl_internal_space_instance() const {
+#ifdef KOKKOS_IMPL_WORKAROUND_ICE_IN_TRILINOS_WITH_OLD_INTEL_COMPILERS
+    return m_space_instance;
+#else
+    return m_space_instance.get();
+#endif
+  }
+
+ private:
+#ifdef KOKKOS_IMPL_WORKAROUND_ICE_IN_TRILINOS_WITH_OLD_INTEL_COMPILERS
+  Impl::SerialInternal* m_space_instance;
+#else
+  Kokkos::Impl::HostSharedPtr<Impl::SerialInternal> m_space_instance;
+#endif
   //--------------------------------------------------------------------------
 };
 
@@ -177,6 +239,7 @@ class SerialSpaceInitializer : public ExecSpaceInitializerBase {
   void initialize(const InitArguments& args) final;
   void finalize(const bool) final;
   void fence() final;
+  void fence(const std::string&) final;
   void print_configuration(std::ostream& msg, const bool detail) final;
 };
 
@@ -206,20 +269,6 @@ struct MemorySpaceAccess<Kokkos::Serial::memory_space,
 namespace Kokkos {
 namespace Impl {
 
-// Resize thread team data scratch memory
-void serial_resize_thread_team_data(size_t pool_reduce_bytes,
-                                    size_t team_reduce_bytes,
-                                    size_t team_shared_bytes,
-                                    size_t thread_local_bytes);
-
-HostThreadTeamData* serial_get_thread_team_data();
-
-} /* namespace Impl */
-} /* namespace Kokkos */
-
-namespace Kokkos {
-namespace Impl {
-
 /*
  * < Kokkos::Serial , WorkArgTag >
  * < WorkArgTag , Impl::enable_if< std::is_same< Kokkos::Serial ,
@@ -510,13 +559,19 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
     const size_t team_shared_size  = 0;  // Never shrinks
     const size_t thread_local_size = 0;  // Never shrinks
 
-    serial_resize_thread_team_data(pool_reduce_size, team_reduce_size,
-                                   team_shared_size, thread_local_size);
-
-    HostThreadTeamData& data = *serial_get_thread_team_data();
+    auto* internal_instance = m_policy.space().impl_internal_space_instance();
+    // Need to lock resize_thread_team_data
+    std::lock_guard<std::mutex> lock(
+        internal_instance->m_thread_team_data_mutex);
+    internal_instance->resize_thread_team_data(
+        pool_reduce_size, team_reduce_size, team_shared_size,
+        thread_local_size);
 
     pointer_type ptr =
-        m_result_ptr ? m_result_ptr : pointer_type(data.pool_reduce_local());
+        m_result_ptr
+            ? m_result_ptr
+            : pointer_type(
+                  internal_instance->m_thread_team_data.pool_reduce_local());
 
     reference_type update =
         ValueInit::init(ReducerConditional::select(m_functor, m_reducer), ptr);
@@ -606,13 +661,18 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
     const size_t team_shared_size  = 0;  // Never shrinks
     const size_t thread_local_size = 0;  // Never shrinks
 
-    serial_resize_thread_team_data(pool_reduce_size, team_reduce_size,
-                                   team_shared_size, thread_local_size);
+    // Need to lock resize_thread_team_data
+    auto* internal_instance = m_policy.space().impl_internal_space_instance();
+    std::lock_guard<std::mutex> lock(
+        internal_instance->m_thread_team_data_mutex);
+    internal_instance->resize_thread_team_data(
+        pool_reduce_size, team_reduce_size, team_shared_size,
+        thread_local_size);
 
-    HostThreadTeamData& data = *serial_get_thread_team_data();
-
-    reference_type update =
-        ValueInit::init(m_functor, pointer_type(data.pool_reduce_local()));
+    reference_type update = ValueInit::init(
+        m_functor,
+        pointer_type(
+            internal_instance->m_thread_team_data.pool_reduce_local()));
 
     this->template exec<WorkTag>(update);
   }
@@ -667,13 +727,18 @@ class ParallelScanWithTotal<FunctorType, Kokkos::RangePolicy<Traits...>,
     const size_t team_shared_size  = 0;  // Never shrinks
     const size_t thread_local_size = 0;  // Never shrinks
 
-    serial_resize_thread_team_data(pool_reduce_size, team_reduce_size,
-                                   team_shared_size, thread_local_size);
+    // Need to lock resize_thread_team_data
+    auto* internal_instance = m_policy.space().impl_internal_space_instance();
+    std::lock_guard<std::mutex> lock(
+        internal_instance->m_thread_team_data_mutex);
+    internal_instance->resize_thread_team_data(
+        pool_reduce_size, team_reduce_size, team_shared_size,
+        thread_local_size);
 
-    HostThreadTeamData& data = *serial_get_thread_team_data();
-
-    reference_type update =
-        ValueInit::init(m_functor, pointer_type(data.pool_reduce_local()));
+    reference_type update = ValueInit::init(
+        m_functor,
+        pointer_type(
+            internal_instance->m_thread_team_data.pool_reduce_local()));
 
     this->template exec<WorkTag>(update);
 
@@ -797,13 +862,19 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
     const size_t team_shared_size  = 0;  // Never shrinks
     const size_t thread_local_size = 0;  // Never shrinks
 
-    serial_resize_thread_team_data(pool_reduce_size, team_reduce_size,
-                                   team_shared_size, thread_local_size);
-
-    HostThreadTeamData& data = *serial_get_thread_team_data();
+    auto* internal_instance = m_policy.space().impl_internal_space_instance();
+    // Need to lock resize_thread_team_data
+    std::lock_guard<std::mutex> lock(
+        internal_instance->m_thread_team_data_mutex);
+    internal_instance->resize_thread_team_data(
+        pool_reduce_size, team_reduce_size, team_shared_size,
+        thread_local_size);
 
     pointer_type ptr =
-        m_result_ptr ? m_result_ptr : pointer_type(data.pool_reduce_local());
+        m_result_ptr
+            ? m_result_ptr
+            : pointer_type(
+                  internal_instance->m_thread_team_data.pool_reduce_local());
 
     reference_type update =
         ValueInit::init(ReducerConditional::select(m_functor, m_reducer), ptr);
@@ -869,6 +940,7 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
   using Member = typename Policy::member_type;
 
   const FunctorType m_functor;
+  const Policy m_policy;
   const int m_league;
   const int m_shared;
 
@@ -896,16 +968,21 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
     const size_t team_shared_size  = m_shared;
     const size_t thread_local_size = 0;  // Never shrinks
 
-    serial_resize_thread_team_data(pool_reduce_size, team_reduce_size,
-                                   team_shared_size, thread_local_size);
-
-    HostThreadTeamData& data = *serial_get_thread_team_data();
+    auto* internal_instance = m_policy.space().impl_internal_space_instance();
+    // Need to lock resize_thread_team_data
+    std::lock_guard<std::mutex> lock(
+        internal_instance->m_thread_team_data_mutex);
+    internal_instance->resize_thread_team_data(
+        pool_reduce_size, team_reduce_size, team_shared_size,
+        thread_local_size);
 
-    this->template exec<typename Policy::work_tag>(data);
+    this->template exec<typename Policy::work_tag>(
+        internal_instance->m_thread_team_data);
   }
 
   ParallelFor(const FunctorType& arg_functor, const Policy& arg_policy)
       : m_functor(arg_functor),
+        m_policy(arg_policy),
         m_league(arg_policy.league_size()),
         m_shared(arg_policy.scratch_size(0) + arg_policy.scratch_size(1) +
                  FunctorTeamShmemSize<FunctorType>::value(arg_functor, 1)) {}
@@ -941,6 +1018,7 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
   using reference_type = typename Analysis::reference_type;
 
   const FunctorType m_functor;
+  const Policy m_policy;
   const int m_league;
   const ReducerType m_reducer;
   pointer_type m_result_ptr;
@@ -973,18 +1051,24 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
     const size_t team_shared_size  = m_shared;
     const size_t thread_local_size = 0;  // Never shrinks
 
-    serial_resize_thread_team_data(pool_reduce_size, team_reduce_size,
-                                   team_shared_size, thread_local_size);
-
-    HostThreadTeamData& data = *serial_get_thread_team_data();
+    auto* internal_instance = m_policy.space().impl_internal_space_instance();
+    // Need to lock resize_thread_team_data
+    std::lock_guard<std::mutex> lock(
+        internal_instance->m_thread_team_data_mutex);
+    internal_instance->resize_thread_team_data(
+        pool_reduce_size, team_reduce_size, team_shared_size,
+        thread_local_size);
 
     pointer_type ptr =
-        m_result_ptr ? m_result_ptr : pointer_type(data.pool_reduce_local());
+        m_result_ptr
+            ? m_result_ptr
+            : pointer_type(
+                  internal_instance->m_thread_team_data.pool_reduce_local());
 
     reference_type update =
         ValueInit::init(ReducerConditional::select(m_functor, m_reducer), ptr);
 
-    this->template exec<WorkTag>(data, update);
+    this->template exec<WorkTag>(internal_instance->m_thread_team_data, update);
 
     Kokkos::Impl::FunctorFinal<ReducerTypeFwd, WorkTagFwd>::final(
         ReducerConditional::select(m_functor, m_reducer), ptr);
@@ -998,6 +1082,7 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
                                   !Kokkos::is_reducer_type<ReducerType>::value,
                               void*>::type = nullptr)
       : m_functor(arg_functor),
+        m_policy(arg_policy),
         m_league(arg_policy.league_size()),
         m_reducer(InvalidType()),
         m_result_ptr(arg_result.data()),
@@ -1016,6 +1101,7 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
   inline ParallelReduce(const FunctorType& arg_functor, Policy arg_policy,
                         const ReducerType& reducer)
       : m_functor(arg_functor),
+        m_policy(arg_policy),
         m_league(arg_policy.league_size()),
         m_reducer(reducer),
         m_result_ptr(reducer.view().data()),
diff --git a/packages/kokkos/core/src/Kokkos_TaskPolicy.hpp b/packages/kokkos/core/src/Kokkos_TaskPolicy.hpp
index 91e079a0e78e314cdb4b22a42876564f25143a4c..9751fab460d4495b1683a94fc0e9d7f879c9a412 100644
--- a/packages/kokkos/core/src/Kokkos_TaskPolicy.hpp
+++ b/packages/kokkos/core/src/Kokkos_TaskPolicy.hpp
@@ -43,5 +43,9 @@
 */
 
 // For backward compatibility:
+#include <Kokkos_Macros.hpp>
+
+KOKKOS_IMPL_WARNING(
+    "This file is deprecated. Use <Kokkos_TaskScheduler.hpp> instead.")
 
 #include <Kokkos_TaskScheduler.hpp>
diff --git a/packages/kokkos/core/src/Kokkos_TaskScheduler.hpp b/packages/kokkos/core/src/Kokkos_TaskScheduler.hpp
index 743273670c9b5fa77f6d590596eb27fc7204396a..17e78f5e81fe83c940eea1bcd6c1d6c347649a4b 100644
--- a/packages/kokkos/core/src/Kokkos_TaskScheduler.hpp
+++ b/packages/kokkos/core/src/Kokkos_TaskScheduler.hpp
@@ -55,7 +55,6 @@
 //----------------------------------------------------------------------------
 
 #include <Kokkos_MemoryPool.hpp>
-#include <impl/Kokkos_Tags.hpp>
 
 #include <Kokkos_Future.hpp>
 #include <impl/Kokkos_TaskQueue.hpp>
@@ -372,7 +371,10 @@ class BasicTaskScheduler : public Impl::TaskSchedulerBase {
         task_base* const t = arg[i].m_task;
         if (nullptr != t) {
           // Increment reference count to track subsequent assignment.
-          Kokkos::atomic_increment(&(t->m_ref_count));
+          // This likely has to be SeqCst
+          Kokkos::Impl::desul_atomic_inc(&(t->m_ref_count),
+                                         Kokkos::Impl::MemoryOrderSeqCst(),
+                                         Kokkos::Impl::MemoryScopeDevice());
           if (q != static_cast<queue_type const*>(t->m_queue)) {
             Kokkos::abort(
                 "Kokkos when_all Futures must be in the same scheduler");
@@ -467,7 +469,10 @@ class BasicTaskScheduler : public Impl::TaskSchedulerBase {
           //  scheduler" );
           //}
           // Increment reference count to track subsequent assignment.
-          Kokkos::atomic_increment(&(arg_f.m_task->m_ref_count));
+          // This increment likely has to be SeqCst
+          Kokkos::Impl::desul_atomic_inc(&(arg_f.m_task->m_ref_count),
+                                         Kokkos::Impl::MemoryOrderSeqCst(),
+                                         Kokkos::Impl::MemoryScopeDevice());
           dep[i] = arg_f.m_task;
         }
       }
diff --git a/packages/kokkos/core/src/Kokkos_Threads.hpp b/packages/kokkos/core/src/Kokkos_Threads.hpp
index e827c2a2a1abd46999360c1eef57eb85428436aa..da9bea9c2347faca7b8e5944cb08a0783983f285 100644
--- a/packages/kokkos/core/src/Kokkos_Threads.hpp
+++ b/packages/kokkos/core/src/Kokkos_Threads.hpp
@@ -57,7 +57,6 @@
 #include <Kokkos_Layout.hpp>
 #include <Kokkos_MemoryTraits.hpp>
 #include <impl/Kokkos_Profiling_Interface.hpp>
-#include <impl/Kokkos_Tags.hpp>
 #include <impl/Kokkos_ExecSpaceInitializer.hpp>
 
 /*--------------------------------------------------------------------------*/
@@ -65,6 +64,7 @@
 namespace Kokkos {
 namespace Impl {
 class ThreadsExec;
+enum class fence_is_static { yes, no };
 }  // namespace Impl
 }  // namespace Kokkos
 
@@ -108,8 +108,10 @@ class Threads {
   /// method does not return until all dispatched functors on this
   /// device have completed.
   static void impl_static_fence();
+  static void impl_static_fence(const std::string& name);
 
   void fence() const;
+  void fence(const std::string&) const;
 
   /** \brief  Return the maximum amount of concurrency.  */
   static int concurrency();
@@ -167,7 +169,7 @@ class Threads {
     return impl_thread_pool_rank();
   }
 
-  uint32_t impl_instance_id() const noexcept { return 0; }
+  uint32_t impl_instance_id() const noexcept { return 1; }
 
   static const char* name();
   //@}
@@ -192,6 +194,7 @@ class ThreadsSpaceInitializer : public ExecSpaceInitializerBase {
   void initialize(const InitArguments& args) final;
   void finalize(const bool) final;
   void fence() final;
+  void fence(const std::string&) final;
   void print_configuration(std::ostream& msg, const bool detail) final;
 };
 
diff --git a/packages/kokkos/core/src/Kokkos_Tuners.hpp b/packages/kokkos/core/src/Kokkos_Tuners.hpp
index f7cc34cc114d29cbe5612bf4350fe01a498282c3..52edd82052f4cfb3919b4733e4acb167780eaf8e 100644
--- a/packages/kokkos/core/src/Kokkos_Tuners.hpp
+++ b/packages/kokkos/core/src/Kokkos_Tuners.hpp
@@ -306,7 +306,11 @@ class MultidimensionalSparseTuningProblem {
   static constexpr size_t max_space_dimension_size = MaxDimensionSize;
   static constexpr double tuning_min               = 0.0;
   static constexpr double tuning_max               = 0.999;
-  static constexpr double tuning_step = tuning_max / max_space_dimension_size;
+
+  // Not declared as static constexpr to work around the following compiler bug
+  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96862
+  // where a floating-point expression cannot be constexpr under -frounding-math
+  double tuning_step = tuning_max / max_space_dimension_size;
 
   using StoredProblemSpace =
       typename Impl::MapTypeConverter<ProblemSpaceInput>::type;
@@ -315,17 +319,45 @@ class MultidimensionalSparseTuningProblem {
 
   using ValueArray = std::array<Kokkos::Tools::Experimental::VariableValue,
                                 space_dimensionality>;
+  template <class Key, class Value>
+  using extended_map = std::map<Key, Value>;
+  template <typename Key>
+  using extended_problem =
+      MultidimensionalSparseTuningProblem<extended_map, MaxDimensionSize, Key,
+                                          ProblemSpaceInput>;
+  template <typename Key, typename Value>
+  using ExtendedProblemSpace =
+      typename Impl::MapTypeConverter<extended_map<Key, Value>>::type;
+
+  template <typename Key>
+  auto extend(const std::string& axis_name,
+              const std::vector<Key>& new_tuning_axis) const
+      -> extended_problem<Key> {
+    ExtendedProblemSpace<Key, ProblemSpaceInput> extended_space;
+    for (auto& key : new_tuning_axis) {
+      extended_space.add_root_value(key);
+      extended_space.add_sub_container(m_space);
+    }
+    std::vector<std::string> extended_names;
+    extended_names.reserve(m_variable_names.size() + 1);
+    extended_names.push_back(axis_name);
+    extended_names.insert(extended_names.end(), m_variable_names.begin(),
+                          m_variable_names.end());
+    return extended_problem<Key>(extended_space, extended_names);
+  }
 
  private:
   StoredProblemSpace m_space;
   std::array<size_t, space_dimensionality> variable_ids;
+  std::vector<std::string> m_variable_names;
   size_t context;
 
  public:
   MultidimensionalSparseTuningProblem() = default;
-  MultidimensionalSparseTuningProblem(ProblemSpaceInput space,
+
+  MultidimensionalSparseTuningProblem(StoredProblemSpace space,
                                       const std::vector<std::string>& names)
-      : m_space(HierarchyConstructor::build(space)) {
+      : m_space(std::move(space)), m_variable_names(names) {
     assert(names.size() == space_dimensionality);
     for (unsigned long x = 0; x < names.size(); ++x) {
       VariableInfo info;
@@ -340,6 +372,20 @@ class MultidimensionalSparseTuningProblem {
     }
   }
 
+  MultidimensionalSparseTuningProblem(ProblemSpaceInput space,
+                                      const std::vector<std::string>& names)
+      : MultidimensionalSparseTuningProblem(HierarchyConstructor::build(space),
+                                            names) {}
+
+  template <typename... Coordinates>
+  auto get_point(Coordinates... coordinates) {
+    using ArrayType = std::array<Kokkos::Tools::Experimental::VariableValue,
+                                 sizeof...(coordinates)>;
+    return Impl::get_point(
+        m_space, ArrayType({Kokkos::Tools::Experimental::make_variable_value(
+                     0, static_cast<double>(coordinates))...}));
+  }
+
   auto begin() {
     context = Kokkos::Tools::Experimental::get_new_context_id();
     ValueArray values;
@@ -349,12 +395,28 @@ class MultidimensionalSparseTuningProblem {
     }
     begin_context(context);
     request_output_values(context, space_dimensionality, values.data());
-    return get_point(m_space, values);
+    return Impl::get_point(m_space, values);
   }
 
   auto end() { end_context(context); }
 };
 
+template <typename Tuner>
+struct ExtendableTunerMixin {
+  template <typename Key>
+  auto combine(const std::string& axis_name,
+               const std::vector<Key>& new_axis) const {
+    const auto& sub_tuner = static_cast<const Tuner*>(this)->get_tuner();
+    return sub_tuner.extend(axis_name, new_axis);
+  }
+
+  template <typename... Coordinates>
+  auto get_point(Coordinates... coordinates) {
+    const auto& sub_tuner = static_cast<const Tuner*>(this)->get_tuner();
+    return sub_tuner.get_point(coordinates...);
+  }
+};
+
 template <size_t MaxDimensionSize = 100, template <class...> class Container,
           class... TemplateArguments>
 auto make_multidimensional_sparse_tuning_problem(
@@ -362,7 +424,8 @@ auto make_multidimensional_sparse_tuning_problem(
   return MultidimensionalSparseTuningProblem<Container, MaxDimensionSize,
                                              TemplateArguments...>(in, names);
 }
-class TeamSizeTuner {
+
+class TeamSizeTuner : public ExtendableTunerMixin<TeamSizeTuner> {
  private:
   using SpaceDescription = std::map<int64_t, std::vector<int64_t>>;
   using TunerType = decltype(make_multidimensional_sparse_tuning_problem<20>(
@@ -481,7 +544,7 @@ class TeamSizeTuner {
     }
   }
 
- private:
+  TunerType get_tuner() const { return tuner; }
 };
 
 namespace Impl {
@@ -501,7 +564,7 @@ void fill_tile(std::map<T, Mapped>& cont, int tile_size) {
 }  // namespace Impl
 
 template <int MDRangeRank>
-struct MDRangeTuner {
+struct MDRangeTuner : public ExtendableTunerMixin<MDRangeTuner<MDRangeRank>> {
  private:
   static constexpr int rank       = MDRangeRank;
   static constexpr int max_slices = 15;
@@ -548,8 +611,45 @@ struct MDRangeTuner {
       tuner.end();
     }
   }
+
+  TunerType get_tuner() const { return tuner; }
 };
 
+template <class Choice>
+struct CategoricalTuner {
+  using choice_list = std::vector<Choice>;
+  choice_list choices;
+  size_t context;
+  size_t tuning_variable_id;
+  CategoricalTuner(std::string name, choice_list m_choices)
+      : choices(m_choices) {
+    std::vector<int64_t> indices;
+    for (typename decltype(choices)::size_type x = 0; x < choices.size(); ++x) {
+      indices.push_back(x);
+    }
+    VariableInfo info;
+    info.category      = StatisticalCategory::kokkos_value_categorical;
+    info.valueQuantity = CandidateValueType::kokkos_value_set;
+    info.type          = ValueType::kokkos_value_int64;
+    info.candidates    = make_candidate_set(indices.size(), indices.data());
+    tuning_variable_id = declare_output_type(name, info);
+  }
+  const Choice& begin() {
+    context = get_new_context_id();
+    begin_context(context);
+    VariableValue value = make_variable_value(tuning_variable_id, int64_t(0));
+    request_output_values(context, 1, &value);
+    return choices[value.value.int_value];
+  }
+  void end() { end_context(context); }
+};
+
+template <typename Choice>
+auto make_categorical_tuner(std::string name, std::vector<Choice> choices)
+    -> CategoricalTuner<Choice> {
+  return CategoricalTuner<Choice>(name, choices);
+}
+
 }  // namespace Experimental
 }  // namespace Tools
 }  // namespace Kokkos
diff --git a/packages/kokkos/core/src/Kokkos_View.hpp b/packages/kokkos/core/src/Kokkos_View.hpp
index 1abe0a48df5eab32f01ef703e6d39921eb9c70c3..b217cc4bc171a94ce3fdeea9ce6301c70c106da9 100644
--- a/packages/kokkos/core/src/Kokkos_View.hpp
+++ b/packages/kokkos/core/src/Kokkos_View.hpp
@@ -190,9 +190,9 @@ struct ViewTraits<void, void, Prop...> {
 };
 
 template <class ArrayLayout, class... Prop>
-struct ViewTraits<typename std::enable_if<
-                      Kokkos::Impl::is_array_layout<ArrayLayout>::value>::type,
-                  ArrayLayout, Prop...> {
+struct ViewTraits<
+    typename std::enable_if<Kokkos::is_array_layout<ArrayLayout>::value>::type,
+    ArrayLayout, Prop...> {
   // Specify layout, keep subsequent space and memory traits arguments
 
   using execution_space = typename ViewTraits<void, Prop...>::execution_space;
@@ -204,9 +204,8 @@ struct ViewTraits<typename std::enable_if<
 };
 
 template <class Space, class... Prop>
-struct ViewTraits<
-    typename std::enable_if<Kokkos::Impl::is_space<Space>::value>::type, Space,
-    Prop...> {
+struct ViewTraits<typename std::enable_if<Kokkos::is_space<Space>::value>::type,
+                  Space, Prop...> {
   // Specify Space, memory traits should be the only subsequent argument.
 
   static_assert(
@@ -230,8 +229,8 @@ struct ViewTraits<
 };
 
 template <class MemoryTraits, class... Prop>
-struct ViewTraits<typename std::enable_if<Kokkos::Impl::is_memory_traits<
-                      MemoryTraits>::value>::type,
+struct ViewTraits<typename std::enable_if<
+                      Kokkos::is_memory_traits<MemoryTraits>::value>::type,
                   MemoryTraits, Prop...> {
   // Specify memory trait, should not be any subsequent arguments
 
@@ -1543,7 +1542,8 @@ class View : public ViewTraits<DataType, Properties...> {
     // to avoid incomplete type errors from using Kokkos::Cuda directly.
     if (std::is_same<Kokkos::CudaUVMSpace,
                      typename traits::device_type::memory_space>::value) {
-      typename traits::device_type::memory_space::execution_space().fence();
+      typename traits::device_type::memory_space::execution_space().fence(
+          "Kokkos::View<...>::View: fence before allocating UVM");
     }
 #endif
     //------------------------------------------------------------
@@ -1555,7 +1555,8 @@ class View : public ViewTraits<DataType, Properties...> {
 #if defined(KOKKOS_ENABLE_CUDA)
     if (std::is_same<Kokkos::CudaUVMSpace,
                      typename traits::device_type::memory_space>::value) {
-      typename traits::device_type::memory_space::execution_space().fence();
+      typename traits::device_type::memory_space::execution_space().fence(
+          "Kokkos::View<...>::View: fence after allocating UVM");
     }
 #endif
     //------------------------------------------------------------
diff --git a/packages/kokkos/core/src/Kokkos_WorkGraphPolicy.hpp b/packages/kokkos/core/src/Kokkos_WorkGraphPolicy.hpp
index bdc8993c398f2dd6d6b581008d1f0c8d3535d860..dbb557c13743fa79235ba3786a367b1ab2ac7adc 100644
--- a/packages/kokkos/core/src/Kokkos_WorkGraphPolicy.hpp
+++ b/packages/kokkos/core/src/Kokkos_WorkGraphPolicy.hpp
@@ -213,7 +213,9 @@ class WorkGraphPolicy : public Kokkos::Impl::PolicyTraits<Properties...> {
       using closure_type = Kokkos::Impl::ParallelFor<self_type, policy_type>;
       const closure_type closure(*this, policy_type(0, m_queue.size()));
       closure.execute();
-      execution_space().fence();
+      execution_space().fence(
+          "Kokkos::WorkGraphPolicy::WorkGraphPolicy: fence after executing "
+          "graph init");
     }
 
     {  // execute-after counts
@@ -221,7 +223,9 @@ class WorkGraphPolicy : public Kokkos::Impl::PolicyTraits<Properties...> {
       using closure_type = Kokkos::Impl::ParallelFor<self_type, policy_type>;
       const closure_type closure(*this, policy_type(0, m_graph.entries.size()));
       closure.execute();
-      execution_space().fence();
+      execution_space().fence(
+          "Kokkos::WorkGraphPolicy::WorkGraphPolicy: fence after executing "
+          "graph count");
     }
 
     {  // Scheduling ready tasks
@@ -229,7 +233,9 @@ class WorkGraphPolicy : public Kokkos::Impl::PolicyTraits<Properties...> {
       using closure_type = Kokkos::Impl::ParallelFor<self_type, policy_type>;
       const closure_type closure(*this, policy_type(0, m_graph.numRows()));
       closure.execute();
-      execution_space().fence();
+      execution_space().fence(
+          "Kokkos::WorkGraphPolicy::WorkGraphPolicy: fence after executing "
+          "readied graph");
     }
   }
 };
diff --git a/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.cpp b/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.cpp
index e530612a57f81dace23777bdf98670dd73a9d026..0d521479eef89121d30c33a41cebdfac4628646f 100644
--- a/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.cpp
+++ b/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.cpp
@@ -447,7 +447,13 @@ OpenMP OpenMP::create_instance(...) { return OpenMP(); }
 
 int OpenMP::concurrency() { return Impl::g_openmp_hardware_max_threads; }
 
-void OpenMP::fence() const {}
+void OpenMP::fence() const {
+  fence("Kokkos::OpenMP::fence: Unnamed Instance Fence");
+}
+void OpenMP::fence(const std::string &name) const {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<Kokkos::OpenMP>(
+      name, Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{1}, []() {});
+}
 
 namespace Impl {
 
@@ -474,6 +480,9 @@ void OpenMPSpaceInitializer::finalize(const bool) {
 }
 
 void OpenMPSpaceInitializer::fence() { Kokkos::OpenMP::impl_static_fence(); }
+void OpenMPSpaceInitializer::fence(const std::string &name) {
+  Kokkos::OpenMP::impl_static_fence(OpenMP(), name);
+}
 
 void OpenMPSpaceInitializer::print_configuration(std::ostream &msg,
                                                  const bool detail) {
diff --git a/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.hpp b/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.hpp
index 82f049ed136119c28b4add24f1460831fec55b16..1191e49cbe6ecd8d03069288062c88e38e6b8830 100644
--- a/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.hpp
+++ b/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Exec.hpp
@@ -151,7 +151,14 @@ int OpenMP::impl_thread_pool_rank() noexcept {
 #endif
 }
 
-inline void OpenMP::impl_static_fence(OpenMP const& /*instance*/) noexcept {}
+inline void OpenMP::impl_static_fence(OpenMP const& /**instance*/,
+                                      const std::string& name) noexcept {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<Kokkos::OpenMP>(
+      name,
+      Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+          GlobalDeviceSynchronization,
+      []() {});
+}
 
 inline bool OpenMP::is_asynchronous(OpenMP const& /*instance*/) noexcept {
   return false;
@@ -213,8 +220,9 @@ void OpenMP::partition_master(F const& f, int num_partitions,
 
 namespace Experimental {
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 template <>
-class MasterLock<OpenMP> {
+class KOKKOS_DEPRECATED MasterLock<OpenMP> {
  public:
   void lock() { omp_set_lock(&m_lock); }
   void unlock() { omp_unset_lock(&m_lock); }
@@ -231,6 +239,7 @@ class MasterLock<OpenMP> {
  private:
   omp_lock_t m_lock;
 };
+#endif
 
 template <>
 class UniqueToken<OpenMP, UniqueTokenScope::Instance> {
diff --git a/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.hpp b/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.hpp
index 2a4a7b1d53bd4785f26508fbc990148291bd9763..d9234e34191a39a84b34d9651975b09fa5801a35 100644
--- a/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.hpp
+++ b/packages/kokkos/core/src/OpenMP/Kokkos_OpenMP_Task.hpp
@@ -324,11 +324,6 @@ class TaskQueueSpecializationConstrained<
                 // count of 0 also. Otherwise, returns a task from another queue
                 // or `end` if one couldn't be popped
                 task = team_queue.attempt_to_steal_task();
-#if 0
-                if(task != no_more_tasks_sentinel && task != end) {
-                  std::printf("task stolen on rank %d\n", team_exec.league_rank());
-                }
-#endif
               }
 
               // If still tasks are still executing
diff --git a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTargetSpace.cpp b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTargetSpace.cpp
index 6fbb4245b8fb8b1e354452727ce9862c85a147c8..b99b0017ca17df6462f0ea2b03b65d47121e47d3 100644
--- a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTargetSpace.cpp
+++ b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTargetSpace.cpp
@@ -107,12 +107,6 @@ SharedAllocationRecord<Kokkos::Experimental::OpenMPTargetSpace,
                      SharedAllocationRecord<void, void>::m_alloc_size);
 }
 
-// TODO: Implement deep copy back see CudaSpace
-std::string SharedAllocationRecord<Kokkos::Experimental::OpenMPTargetSpace,
-                                   void>::get_label() const {
-  return std::string("OpenMPTargetAllocation");
-}
-
 SharedAllocationRecord<Kokkos::Experimental::OpenMPTargetSpace, void>::
     SharedAllocationRecord(
         const Kokkos::Experimental::OpenMPTargetSpace &arg_space,
@@ -141,23 +135,6 @@ SharedAllocationRecord<Kokkos::Experimental::OpenMPTargetSpace, void>::
 
 //----------------------------------------------------------------------------
 
-void *SharedAllocationRecord<Kokkos::Experimental::OpenMPTargetSpace, void>::
-    reallocate_tracked(void *const arg_alloc_ptr, const size_t arg_alloc_size) {
-  SharedAllocationRecord *const r_old = get_record(arg_alloc_ptr);
-  SharedAllocationRecord *const r_new =
-      allocate(r_old->m_space, r_old->get_label(), arg_alloc_size);
-
-  // Kokkos::Impl::DeepCopy<OpenMPTargetSpace,OpenMPTargetSpace>( r_new->data()
-  // , r_old->data()
-  //                                           , std::min( r_old->size() ,
-  //                                           r_new->size() ) );
-
-  RecordBase::increment(r_new);
-  RecordBase::decrement(r_old);
-
-  return r_new->data();
-}
-
 }  // namespace Impl
 }  // namespace Kokkos
 
diff --git a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.cpp b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.cpp
index f13875b440b63b729a64615a20da0f597a85cf6e..7ff885ed86b94e942cce18ff2e7ff1ed664129fa 100644
--- a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.cpp
+++ b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.cpp
@@ -77,9 +77,10 @@ namespace Kokkos {
 namespace Impl {
 
 void OpenMPTargetExec::verify_is_process(const char* const label) {
-  if (omp_in_parallel()) {
+  // Fails if the current task is in a parallel region or is not on the host.
+  if (omp_in_parallel() && (!omp_is_initial_device())) {
     std::string msg(label);
-    msg.append(" ERROR: in parallel");
+    msg.append(" ERROR: in parallel or on device");
     Kokkos::Impl::throw_runtime_exception(msg);
   }
 }
diff --git a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.hpp b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.hpp
index 0b65e0d4a4b2270fdf577b4fffc1a10835467a47..ccfc756213695df6479634a67405d7d89308dbb8 100644
--- a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.hpp
+++ b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Exec.hpp
@@ -54,7 +54,10 @@
 // FIXME_OPENMPTARGET - Using this macro to implement a workaround for
 // hierarchical reducers. It avoids hitting the code path which we wanted to
 // write but doesn't work. undef'ed at the end.
+// Intel compilers prefer the non-workaround version.
+#ifndef KOKKOS_ARCH_INTEL_GPU
 #define KOKKOS_IMPL_HIERARCHICAL_REDUCERS_WORKAROUND
+#endif
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
@@ -66,10 +69,6 @@ template <class Reducer>
 struct OpenMPTargetReducerWrapper {
   using value_type = typename Reducer::value_type;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   KOKKOS_INLINE_FUNCTION
   static void join(value_type&, const value_type&) {
     printf(
@@ -90,7 +89,6 @@ struct OpenMPTargetReducerWrapper {
         "Using a generic unknown Reducer for the OpenMPTarget backend is not "
         "implemented.");
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Space>
@@ -99,10 +97,6 @@ struct OpenMPTargetReducerWrapper<Sum<Scalar, Space>> {
   // Required
   using value_type = typename std::remove_cv<Scalar>::type;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) { dest += src; }
@@ -116,7 +110,6 @@ struct OpenMPTargetReducerWrapper<Sum<Scalar, Space>> {
   static void init(value_type& val) {
     val = reduction_identity<value_type>::sum();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Space>
@@ -125,10 +118,6 @@ struct OpenMPTargetReducerWrapper<Prod<Scalar, Space>> {
   // Required
   using value_type = typename std::remove_cv<Scalar>::type;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) { dest *= src; }
@@ -142,7 +131,6 @@ struct OpenMPTargetReducerWrapper<Prod<Scalar, Space>> {
   static void init(value_type& val) {
     val = reduction_identity<value_type>::prod();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Space>
@@ -151,10 +139,6 @@ struct OpenMPTargetReducerWrapper<Min<Scalar, Space>> {
   // Required
   using value_type = typename std::remove_cv<Scalar>::type;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
@@ -170,7 +154,6 @@ struct OpenMPTargetReducerWrapper<Min<Scalar, Space>> {
   static void init(value_type& val) {
     val = reduction_identity<value_type>::min();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Space>
@@ -179,10 +162,6 @@ struct OpenMPTargetReducerWrapper<Max<Scalar, Space>> {
   // Required
   using value_type = typename std::remove_cv<Scalar>::type;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
@@ -199,7 +178,6 @@ struct OpenMPTargetReducerWrapper<Max<Scalar, Space>> {
   static void init(value_type& val) {
     val = reduction_identity<value_type>::max();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Space>
@@ -208,10 +186,6 @@ struct OpenMPTargetReducerWrapper<LAnd<Scalar, Space>> {
   // Required
   using value_type = typename std::remove_cv<Scalar>::type;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
     dest = dest && src;
@@ -226,7 +200,6 @@ struct OpenMPTargetReducerWrapper<LAnd<Scalar, Space>> {
   static void init(value_type& val) {
     val = reduction_identity<value_type>::land();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Space>
@@ -237,10 +210,6 @@ struct OpenMPTargetReducerWrapper<LOr<Scalar, Space>> {
 
   using result_view_type = Kokkos::View<value_type, Space>;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
@@ -256,7 +225,6 @@ struct OpenMPTargetReducerWrapper<LOr<Scalar, Space>> {
   static void init(value_type& val) {
     val = reduction_identity<value_type>::lor();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Space>
@@ -265,10 +233,6 @@ struct OpenMPTargetReducerWrapper<BAnd<Scalar, Space>> {
   // Required
   using value_type = typename std::remove_cv<Scalar>::type;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
@@ -284,7 +248,6 @@ struct OpenMPTargetReducerWrapper<BAnd<Scalar, Space>> {
   static void init(value_type& val) {
     val = reduction_identity<value_type>::band();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Space>
@@ -293,10 +256,6 @@ struct OpenMPTargetReducerWrapper<BOr<Scalar, Space>> {
   // Required
   using value_type = typename std::remove_cv<Scalar>::type;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
@@ -312,7 +271,6 @@ struct OpenMPTargetReducerWrapper<BOr<Scalar, Space>> {
   static void init(value_type& val) {
     val = reduction_identity<value_type>::bor();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Index, class Space>
@@ -325,10 +283,6 @@ struct OpenMPTargetReducerWrapper<MinLoc<Scalar, Index, Space>> {
   // Required
   using value_type = ValLocScalar<scalar_type, index_type>;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
@@ -345,7 +299,6 @@ struct OpenMPTargetReducerWrapper<MinLoc<Scalar, Index, Space>> {
     val.val = reduction_identity<scalar_type>::min();
     val.loc = reduction_identity<index_type>::min();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Index, class Space>
@@ -358,10 +311,6 @@ struct OpenMPTargetReducerWrapper<MaxLoc<Scalar, Index, Space>> {
   // Required
   using value_type = ValLocScalar<scalar_type, index_type>;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
     if (src.val > dest.val) dest = src;
@@ -377,7 +326,6 @@ struct OpenMPTargetReducerWrapper<MaxLoc<Scalar, Index, Space>> {
     val.val = reduction_identity<scalar_type>::max();
     val.loc = reduction_identity<index_type>::min();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Space>
@@ -389,10 +337,6 @@ struct OpenMPTargetReducerWrapper<MinMax<Scalar, Space>> {
   // Required
   using value_type = MinMaxScalar<scalar_type>;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
@@ -419,7 +363,6 @@ struct OpenMPTargetReducerWrapper<MinMax<Scalar, Space>> {
     val.max_val = reduction_identity<scalar_type>::max();
     val.min_val = reduction_identity<scalar_type>::min();
   }
-#pragma omp end declare target
 };
 
 template <class Scalar, class Index, class Space>
@@ -432,10 +375,6 @@ struct OpenMPTargetReducerWrapper<MinMaxLoc<Scalar, Index, Space>> {
   // Required
   using value_type = MinMaxLocScalar<scalar_type, index_type>;
 
-// WORKAROUND OPENMPTARGET
-// This pragma omp declare target should not be necessary, but Intel compiler
-// fails without it
-#pragma omp declare target
   // Required
   KOKKOS_INLINE_FUNCTION
   static void join(value_type& dest, const value_type& src) {
@@ -468,7 +407,6 @@ struct OpenMPTargetReducerWrapper<MinMaxLoc<Scalar, Index, Space>> {
     val.max_loc = reduction_identity<index_type>::min();
     val.min_loc = reduction_identity<index_type>::min();
   }
-#pragma omp end declare target
 };
 /*
 template<class ReducerType>
@@ -560,47 +498,20 @@ class OpenMPTargetExecTeamMember {
   void* m_glb_scratch;
   void* m_reduce_scratch;
 
-  /*
-  // Fan-in team threads, root of the fan-in which does not block returns true
-  inline
-  bool team_fan_in() const
-    {
-      memory_fence();
-      for ( int n = 1 , j ; ( ( j = m_team_rank_rev + n ) < m_team_size ) && ! (
-  m_team_rank_rev & n ) ; n <<= 1 ) {
-
-        m_exec.pool_rev( m_team_base_rev + j )->state_wait( Active );
-      }
-
-      if ( m_team_rank_rev ) {
-        m_exec.state_set( Rendezvous );
-        memory_fence();
-        m_exec.state_wait( Rendezvous );
-      }
-
-      return 0 == m_team_rank_rev ;
-    }
-
-  inline
-  void team_fan_out() const
-    {
-      memory_fence();
-      for ( int n = 1 , j ; ( ( j = m_team_rank_rev + n ) < m_team_size ) && ! (
-  m_team_rank_rev & n ) ; n <<= 1 ) { m_exec.pool_rev( m_team_base_rev + j
-  )->state_set( Active ); memory_fence();
-      }
-    }
-  */
  public:
   KOKKOS_INLINE_FUNCTION
   const execution_space::scratch_memory_space& team_shmem() const {
     return m_team_shared.set_team_thread_mode(0, 1, 0);
   }
 
+  // set_team_thread_mode routine parameters for future understanding:
+  // first parameter - scratch level.
+  // second parameter - size multiplier for advancing scratch ptr after a
+  // request was serviced. third parameter - offset size multiplier from current
+  // scratch ptr when returning a ptr for a request.
   KOKKOS_INLINE_FUNCTION
   const execution_space::scratch_memory_space& team_scratch(int level) const {
-    return m_team_shared.set_team_thread_mode(level, 1,
-                                              m_team_scratch_size[level]);
+    return m_team_shared.set_team_thread_mode(level, 1, 0);
   }
 
   KOKKOS_INLINE_FUNCTION
@@ -627,8 +538,9 @@ class OpenMPTargetExecTeamMember {
     using type =
         typename std::conditional<(sizeof(ValueType) < TEAM_REDUCE_SIZE),
                                   ValueType, void>::type;
-    type* team_scratch = reinterpret_cast<type*>(
-        ((char*)(m_glb_scratch) + TEAM_REDUCE_SIZE * omp_get_team_num()));
+    type* team_scratch =
+        reinterpret_cast<type*>(static_cast<char*>(m_glb_scratch) +
+                                TEAM_REDUCE_SIZE * omp_get_team_num());
 #pragma omp barrier
     if (team_rank() == thread_id) *team_scratch = value;
 #pragma omp barrier
@@ -656,7 +568,8 @@ class OpenMPTargetExecTeamMember {
 
     const int n_values = TEAM_REDUCE_SIZE / sizeof(value_type);
     type* team_scratch =
-        (type*)((char*)m_glb_scratch + TEAM_REDUCE_SIZE * omp_get_team_num());
+        reinterpret_cast<type*>(static_cast<char*>(m_glb_scratch) +
+                                TEAM_REDUCE_SIZE * omp_get_team_num());
     for (int i = m_team_rank; i < n_values; i += m_team_size) {
       team_scratch[i] = value_type();
     }
@@ -770,27 +683,24 @@ class OpenMPTargetExecTeamMember {
         m_shmem_block_index(shmem_block_index),
         m_glb_scratch(glb_scratch) {
     const int omp_tid = omp_get_thread_num();
-    m_team_shared     = scratch_memory_space(
-        ((char*)glb_scratch +
-         m_shmem_block_index *
-             (shmem_size_L0 + shmem_size_L1 +
-              ((shmem_size_L0 + shmem_size_L1) * 10 / 100) + TEAM_REDUCE_SIZE)),
-        shmem_size_L0,
-        ((char*)glb_scratch +
-         m_shmem_block_index * (shmem_size_L0 + shmem_size_L1 +
-                                ((shmem_size_L0 + shmem_size_L1) * 10 / 100) +
-                                TEAM_REDUCE_SIZE)) +
-            shmem_size_L0 + ((shmem_size_L0 + shmem_size_L1) * 10 / 100) +
-            TEAM_REDUCE_SIZE,
-        shmem_size_L1);
-    m_reduce_scratch =
-        (char*)glb_scratch +
-        shmem_block_index *
-            (shmem_size_L0 + shmem_size_L1 +
-             ((shmem_size_L0 + shmem_size_L1) * 10 / 100) + TEAM_REDUCE_SIZE);
-    m_league_rank = league_rank;
-    m_team_rank   = omp_tid;
-    m_vector_lane = 0;
+
+    // The scratch memory allocated is a sum of TEAM_REDUCE_SIZE, L0 shmem size
+    // and L1 shmem size. TEAM_REDUCE_SIZE = 512 bytes saved per team for
+    // hierarchical reduction. There is an additional 10% of the requested
+    // scratch memory allocated per team as padding. Hence the product with 0.1.
+    const int reduce_offset =
+        m_shmem_block_index *
+        (shmem_size_L0 + shmem_size_L1 +
+         ((shmem_size_L0 + shmem_size_L1) * 0.1) + TEAM_REDUCE_SIZE);
+    const int l0_offset = reduce_offset + TEAM_REDUCE_SIZE;
+    const int l1_offset = l0_offset + shmem_size_L0;
+    m_team_shared       = scratch_memory_space(
+        (static_cast<char*>(glb_scratch) + l0_offset), shmem_size_L0,
+        static_cast<char*>(glb_scratch) + l1_offset, shmem_size_L1);
+    m_reduce_scratch = static_cast<char*>(glb_scratch) + reduce_offset;
+    m_league_rank    = league_rank;
+    m_team_rank      = omp_tid;
+    m_vector_lane    = 0;
   }
 
   static inline int team_reduce_size() { return TEAM_REDUCE_SIZE; }
@@ -877,6 +787,16 @@ class TeamPolicyInternal<Kokkos::Experimental::OpenMPTarget, Properties...>
   friend class TeamPolicyInternal;
 
  public:
+  // FIXME_OPENMPTARGET : Currently this routine is a copy of the Cuda
+  // implementation, but this has to be tailored to be architecture specific.
+  inline static int scratch_size_max(int level) {
+    return (
+        level == 0 ? 1024 * 40 :  // 48kB is the max for CUDA, but we need some
+                                  // for team_member.reduce etc.
+            20 * 1024 *
+                1024);  // arbitrarily setting this to 20MB, for a Volta V100
+                        // that would give us about 3.2GB for 2 teams per SM
+  }
   inline bool impl_auto_team_size() const { return m_tune_team_size; }
   inline bool impl_auto_vector_length() const { return m_tune_vector_length; }
   inline void impl_set_team_size(const size_t size) { m_team_size = size; }
@@ -884,9 +804,11 @@ class TeamPolicyInternal<Kokkos::Experimental::OpenMPTarget, Properties...>
     m_tune_vector_length = length;
   }
   inline int impl_vector_length() const { return m_vector_length; }
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
   KOKKOS_DEPRECATED inline int vector_length() const {
     return impl_vector_length();
   }
+#endif
   inline int team_size() const { return m_team_size; }
   inline int league_size() const { return m_league_size; }
   inline size_t scratch_size(const int& level, int team_size_ = -1) const {
@@ -1245,21 +1167,12 @@ KOKKOS_INLINE_FUNCTION
       static_cast<ValueType*>(loop_boundaries.team.impl_reduce_scratch());
 
 #pragma omp barrier
-  // These three lines all cause crash
   Impl::OpenMPTargetReducerWrapper<ReducerType>::init(TeamThread_scratch[0]);
-//  result.init(TeamThread_scratch[0]);
-//  Impl::OpenMPTargetReducerWrapper<ReducerType> red;
-//  red.init(TeamThread_scratch[0]);
 #pragma omp barrier
 
 #pragma omp for reduction(custominner : TeamThread_scratch[:1])
   for (iType i = loop_boundaries.start; i < loop_boundaries.end; i++) {
-    ValueType tmp;
-    result.init(tmp);
-    lambda(i, tmp);
-    // This line causes a crash
-    Impl::OpenMPTargetReducerWrapper<ReducerType>::join(TeamThread_scratch[0],
-                                                        tmp);
+    lambda(i, TeamThread_scratch[0]);
   }
   result.reference() = TeamThread_scratch[0];
 }
@@ -1305,6 +1218,12 @@ KOKKOS_INLINE_FUNCTION
          i += team_size) {
       lambda(i, tmp2);
     }
+
+    // FIXME_OPENMPTARGET: Join should work but doesn't. Every threads gets a
+    // private TeamThread_scratch[0] and at the end of the for-loop the `join`
+    // operation is performed by OpenMP itself and hence the simple assignment
+    // works.
+    //    result.join(TeamThread_scratch[0], tmp2);
     TeamThread_scratch[0] = tmp2;
   }
 
@@ -1336,28 +1255,31 @@ KOKKOS_INLINE_FUNCTION void parallel_reduce(
   static_assert(sizeof(ValueType) <=
                 Impl::OpenMPTargetExecTeamMember::TEAM_REDUCE_SIZE);
 
+  // FIXME_OPENMPTARGET: Still need to figure out how to get value_count here.
+  const int value_count = 1;
+
 #pragma omp barrier
   TeamThread_scratch[0] = init_result;
 #pragma omp barrier
 
-  if constexpr (std::is_arithmetic<ValueType>::value) {
-#pragma omp for reduction(+ : TeamThread_scratch[:1])
-    for (iType i = loop_boundaries.start; i < loop_boundaries.end; i++) {
-      ValueType tmp = ValueType();
-      lambda(i, tmp);
-      TeamThread_scratch[0] += tmp;
-    }
-  } else {
-#pragma omp declare reduction(custom:ValueType : omp_out += omp_in)
-
-#pragma omp for reduction(custom : TeamThread_scratch[:1])
-    for (iType i = loop_boundaries.start; i < loop_boundaries.end; i++) {
-      ValueType tmp = ValueType();
-      lambda(i, tmp);
-      join(TeamThread_scratch[0], tmp);
+#pragma omp for
+  for (iType i = loop_boundaries.start; i < loop_boundaries.end; i++) {
+    lambda(i, TeamThread_scratch[omp_get_num_threads() * value_count]);
+  }
+
+  // Reduce all partial results within a team.
+  const int team_size      = omp_get_num_threads();
+  int tree_neighbor_offset = 1;
+  do {
+#pragma omp for
+    for (int i = 0; i < team_size - tree_neighbor_offset;
+         i += 2 * tree_neighbor_offset) {
+      const int neighbor = i + tree_neighbor_offset;
+      join(lambda, &TeamThread_scratch[i * value_count],
+           &TeamThread_scratch[neighbor * value_count]);
     }
-  }
-
+    tree_neighbor_offset *= 2;
+  } while (tree_neighbor_offset < team_size);
   init_result = TeamThread_scratch[0];
 }
 
@@ -1402,7 +1324,6 @@ KOKKOS_INLINE_FUNCTION void parallel_scan(
 }
 
 }  // namespace Kokkos
-#undef KOKKOS_IMPL_HIERARCHICAL_REDUCERS_WORKAROUND
 
 namespace Kokkos {
 /** \brief  Intra-thread vector parallel_for. Executes lambda(iType i) for each
@@ -1530,8 +1451,7 @@ KOKKOS_INLINE_FUNCTION void parallel_scan(
 #ifdef KOKKOS_ENABLE_PRAGMA_IVDEP
 #pragma ivdep
 #endif
-  for (iType i = loop_boundaries.start; i < loop_boundaries.end;
-       i += loop_boundaries.increment) {
+  for (iType i = loop_boundaries.start; i < loop_boundaries.end; ++i) {
     lambda(i, scan_val, true);
   }
 }
@@ -1629,9 +1549,7 @@ KOKKOS_INLINE_FUNCTION
 
 #pragma omp for simd reduction(custom : TeamVector_scratch[:1])
   for (iType i = loop_boundaries.start; i < loop_boundaries.end; i++) {
-    ValueType tmp = ValueType();
-    lambda(i, tmp);
-    TeamVector_scratch[0] += tmp;
+    lambda(i, TeamVector_scratch[0]);
   }
 
   result.reference() = TeamVector_scratch[0];
@@ -1686,7 +1604,9 @@ KOKKOS_INLINE_FUNCTION
 #endif  // KOKKOS_IMPL_HIERARCHICAL_REDUCERS_WORKAROUND
 }  // namespace Kokkos
 
+#ifdef KOKKOS_IMPL_HIERARCHICAL_REDUCERS_WORKAROUND
 #undef KOKKOS_IMPL_HIERARCHICAL_REDUCERS_WORKAROUND
+#endif
 
 namespace Kokkos {
 
diff --git a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.cpp b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.cpp
index 4a79b72732dafb9bd93613723551ec7a9b01ddd1..e421edc5b4c108b03fb8680863184828df243dd7 100644
--- a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.cpp
+++ b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.cpp
@@ -59,7 +59,34 @@
 namespace Kokkos {
 namespace Experimental {
 namespace Impl {
-void OpenMPTargetInternal::fence() {}
+uint32_t OpenMPTargetInternal::impl_get_instance_id() const noexcept {
+  return m_instance_id;
+}
+
+void OpenMPTargetInternal::fence(openmp_fence_is_static is_static) {
+  fence(
+      "Kokkos::Experimental::Impl::OpenMPTargetInternal::fence: Unnamed "
+      "Internal Fence",
+      is_static);
+}
+void OpenMPTargetInternal::fence(const std::string& name,
+                                 openmp_fence_is_static is_static) {
+  if (is_static == openmp_fence_is_static::no) {
+    Kokkos::Tools::Experimental::Impl::profile_fence_event<
+        Kokkos::Experimental::OpenMPTarget>(
+        name,
+        Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{
+            impl_get_instance_id()},
+        [&]() {});
+  } else {
+    Kokkos::Tools::Experimental::Impl::profile_fence_event<
+        Kokkos::Experimental::OpenMPTarget>(
+        name,
+        Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+            GlobalDeviceSynchronization,
+        [&]() {});
+  }
+}
 int OpenMPTargetInternal::concurrency() { return 128000; }
 const char* OpenMPTargetInternal::name() { return "OpenMPTarget"; }
 void OpenMPTargetInternal::print_configuration(std::ostream& /*stream*/,
@@ -77,7 +104,18 @@ void OpenMPTargetInternal::impl_finalize() {
     Kokkos::kokkos_free<Kokkos::Experimental::OpenMPTargetSpace>(
         space.m_uniquetoken_ptr);
 }
-void OpenMPTargetInternal::impl_initialize() { m_is_initialized = true; }
+void OpenMPTargetInternal::impl_initialize() {
+  m_is_initialized = true;
+
+  // FIXME_OPENMPTARGET:  Only fix the number of teams for NVIDIA architectures
+  // from Pascal and upwards.
+#if defined(KOKKOS_ARCH_PASCAL) || defined(KOKKOS_ARCH_VOLTA) || \
+    defined(KOKKOS_ARCH_TURING75) || defined(KOKKOS_ARCH_AMPERE)
+#if defined(KOKKOS_COMPILER_CLANG) && (KOKKOS_COMPILER_CLANG >= 1300)
+  omp_set_num_teams(512);
+#endif
+#endif
+}
 int OpenMPTargetInternal::impl_is_initialized() {
   return m_is_initialized ? 1 : 0;
 }
@@ -100,11 +138,28 @@ void OpenMPTarget::print_configuration(std::ostream& stream,
   m_space_instance->print_configuration(stream, detail);
 }
 
+uint32_t OpenMPTarget::impl_instance_id() const noexcept {
+  return m_space_instance->impl_get_instance_id();
+}
+
 int OpenMPTarget::concurrency() {
   return Impl::OpenMPTargetInternal::impl_singleton()->concurrency();
 }
 void OpenMPTarget::fence() {
-  Impl::OpenMPTargetInternal::impl_singleton()->fence();
+  Impl::OpenMPTargetInternal::impl_singleton()->fence(
+      "Kokkos::OpenMPTarget::fence: Unnamed Instance Fence");
+}
+void OpenMPTarget::fence(const std::string& name) {
+  Impl::OpenMPTargetInternal::impl_singleton()->fence(name);
+}
+void OpenMPTarget::impl_static_fence() {
+  Impl::OpenMPTargetInternal::impl_singleton()->fence(
+      "Kokkos::OpenMPTarget::fence: Unnamed Instance Fence",
+      Kokkos::Experimental::Impl::openmp_fence_is_static::yes);
+}
+void OpenMPTarget::impl_static_fence(const std::string& name) {
+  Impl::OpenMPTargetInternal::impl_singleton()->fence(
+      name, Kokkos::Experimental::Impl::openmp_fence_is_static::yes);
 }
 
 void OpenMPTarget::impl_initialize() { m_space_instance->impl_initialize(); }
@@ -146,7 +201,10 @@ void OpenMPTargetSpaceInitializer::finalize(const bool all_spaces) {
 }
 
 void OpenMPTargetSpaceInitializer::fence() {
-  Kokkos::Experimental::OpenMPTarget::fence();
+  Kokkos::Experimental::OpenMPTarget::impl_static_fence();
+}
+void OpenMPTargetSpaceInitializer::fence(const std::string& name) {
+  Kokkos::Experimental::OpenMPTarget::impl_static_fence(name);
 }
 
 void OpenMPTargetSpaceInitializer::print_configuration(std::ostream& msg,
diff --git a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.hpp b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.hpp
index a1caf90c195b98511b4476db73345816af2b4669..b495771190e35c661df26a5ab3bc1d53a544cff7 100644
--- a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.hpp
+++ b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Instance.hpp
@@ -51,6 +51,8 @@ namespace Kokkos {
 namespace Experimental {
 namespace Impl {
 
+enum class openmp_fence_is_static { yes, no };
+
 class OpenMPTargetInternal {
  private:
   OpenMPTargetInternal()                            = default;
@@ -58,7 +60,9 @@ class OpenMPTargetInternal {
   OpenMPTargetInternal& operator=(const OpenMPTargetInternal&) = default;
 
  public:
-  void fence();
+  void fence(openmp_fence_is_static is_static = openmp_fence_is_static::no);
+  void fence(const std::string& name,
+             openmp_fence_is_static is_static = openmp_fence_is_static::no);
 
   /** \brief  Return the maximum amount of concurrency.  */
   int concurrency();
@@ -73,14 +77,16 @@ class OpenMPTargetInternal {
 
   //! Has been initialized
   int impl_is_initialized();
-
+  uint32_t impl_get_instance_id() const noexcept;
   //! Initialize, telling the CUDA run-time library which device to use.
   void impl_initialize();
 
   static OpenMPTargetInternal* impl_singleton();
 
  private:
-  bool m_is_initialized = false;
+  bool m_is_initialized  = false;
+  uint32_t m_instance_id = Kokkos::Tools::Experimental::Impl::idForInstance<
+      Kokkos::Experimental::OpenMPTarget>(reinterpret_cast<uintptr_t>(this));
 };
 }  // Namespace Impl
 }  // Namespace Experimental
diff --git a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel.hpp b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel.hpp
index a4092c3a37a7e9a1493576c5efe783334982a391..08a3109408bd88bcfa04f3fe31d09f5a6e1cff2c 100644
--- a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel.hpp
+++ b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel.hpp
@@ -51,8 +51,6 @@
 #include <OpenMPTarget/Kokkos_OpenMPTarget_Exec.hpp>
 #include <impl/Kokkos_FunctorAdapter.hpp>
 
-#define KOKKOS_IMPL_LOCK_FREE_HIERARCHICAL
-
 namespace Kokkos {
 namespace Impl {
 
@@ -69,24 +67,10 @@ class ParallelFor<FunctorType, Kokkos::RangePolicy<Traits...>,
   const Policy m_policy;
 
  public:
-  inline void execute() const { execute_impl<WorkTag>(); }
-  /*
-    template <class TagType>
-    inline typename std::enable_if<std::is_same<TagType, void>::value>::type
-    execute_impl() const {
-      OpenMPTargetExec::verify_is_process(
-          "Kokkos::Experimental::OpenMPTarget parallel_for");
-      OpenMPTargetExec::verify_initialized(
-          "Kokkos::Experimental::OpenMPTarget parallel_for");
-      const typename Policy::member_type begin = m_policy.begin();
-      const typename Policy::member_type end   = m_policy.end();
-
-  #pragma omp target teams distribute parallel for map(to: this->m_functor)
-      for (int i = begin; i < end; i++) m_functor(i);
-    }
-  */
+  void execute() const { execute_impl<WorkTag>(); }
+
   template <class TagType>
-  inline void execute_impl() const {
+  void execute_impl() const {
     OpenMPTargetExec::verify_is_process(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
     OpenMPTargetExec::verify_initialized(
@@ -98,16 +82,17 @@ class ParallelFor<FunctorType, Kokkos::RangePolicy<Traits...>,
 
     FunctorType a_functor(m_functor);
 
-    if constexpr (std::is_same<TagType, void>::value) {
 #pragma omp target teams distribute parallel for map(to : a_functor)
-      for (auto i = begin; i < end; i++) a_functor(i);
-    } else {
-#pragma omp target teams distribute parallel for map(to : a_functor)
-      for (auto i = begin; i < end; i++) a_functor(TagType(), i);
+    for (auto i = begin; i < end; ++i) {
+      if constexpr (std::is_same<TagType, void>::value) {
+        a_functor(i);
+      } else {
+        a_functor(TagType(), i);
+      }
     }
   }
 
-  inline ParallelFor(const FunctorType& arg_functor, Policy arg_policy)
+  ParallelFor(const FunctorType& arg_functor, Policy arg_policy)
       : m_functor(arg_functor), m_policy(arg_policy) {}
 };
 
@@ -120,12 +105,31 @@ class ParallelFor<FunctorType, Kokkos::RangePolicy<Traits...>,
 namespace Kokkos {
 namespace Impl {
 
+// This class has the memcpy routine that is commonly used by ParallelReduce
+// over RangePolicy and TeamPolicy.
+template <class PointerType>
+struct ParallelReduceCommon {
+  // Copy the result back to device if the view is on the device.
+  static void memcpy_result(PointerType dest, PointerType src, size_t size,
+                            bool ptr_on_device) {
+    if (ptr_on_device) {
+      OMPT_SAFE_CALL(omp_target_memcpy(dest, src, size, 0, 0,
+                                       omp_get_default_device(),
+                                       omp_get_initial_device()));
+    } else {
+      *dest = *src;
+    }
+  }
+};
+
 template <class FunctorType, class PolicyType, class ReducerType,
-          class PointerType, class ValueType, bool FunctorHasJoin,
-          bool UseReducerType>
+          class PointerType, class ValueType>
 struct ParallelReduceSpecialize {
-  static inline void execute(const FunctorType& /*f*/, const PolicyType& /*p*/,
+  inline static void execute(const FunctorType& /*f*/, const PolicyType& /*p*/,
                              PointerType /*result_ptr*/) {
+    constexpr int FunctorHasJoin = ReduceFunctorHasJoin<FunctorType>::value;
+    constexpr int UseReducerType = is_reducer_type<ReducerType>::value;
+
     std::stringstream error_message;
     error_message << "Error: Invalid Specialization " << FunctorHasJoin << ' '
                   << UseReducerType << '\n';
@@ -137,12 +141,26 @@ struct ParallelReduceSpecialize {
 template <class FunctorType, class ReducerType, class PointerType,
           class ValueType, class... PolicyArgs>
 struct ParallelReduceSpecialize<FunctorType, Kokkos::RangePolicy<PolicyArgs...>,
-                                ReducerType, PointerType, ValueType, false,
-                                false> {
+                                ReducerType, PointerType, ValueType> {
   using PolicyType = Kokkos::RangePolicy<PolicyArgs...>;
-  template <class TagType>
-  inline static void execute_impl(const FunctorType& f, const PolicyType& p,
-                                  PointerType result_ptr) {
+  using TagType    = typename PolicyType::work_tag;
+  using ReducerTypeFwd =
+      typename std::conditional<std::is_same<InvalidType, ReducerType>::value,
+                                FunctorType, ReducerType>::type;
+  using WorkTagFwd =
+      std::conditional_t<std::is_same<InvalidType, ReducerType>::value, TagType,
+                         void>;
+
+  using ValueTraits =
+      Kokkos::Impl::FunctorValueTraits<ReducerTypeFwd, WorkTagFwd>;
+  using ValueInit     = Kokkos::Impl::FunctorValueInit<FunctorType, TagType>;
+  using ValueJoin     = Kokkos::Impl::FunctorValueJoin<FunctorType, TagType>;
+  using ReferenceType = typename ValueTraits::reference_type;
+
+  using ParReduceCommon = ParallelReduceCommon<PointerType>;
+
+  static void execute_reducer(const FunctorType& f, const PolicyType& p,
+                              PointerType result_ptr, bool ptr_on_device) {
     OpenMPTargetExec::verify_is_process(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
     OpenMPTargetExec::verify_initialized(
@@ -153,69 +171,220 @@ struct ParallelReduceSpecialize<FunctorType, Kokkos::RangePolicy<PolicyArgs...>,
     if (end <= begin) return;
 
     ValueType result = ValueType();
-    if constexpr (std::is_same<TagType, void>::value) {
-#pragma omp target teams distribute parallel for num_teams(512) \
-                map(to:f) map(tofrom:result) reduction(+: result)
-      for (auto i = begin; i < end; i++) f(i, result);
-    } else {
-#pragma omp target teams distribute parallel for num_teams(512) \
-                map(to:f) map(tofrom:result) reduction(+: result)
-      for (auto i = begin; i < end; i++) f(TagType(), i, result);
-    }
-
-    *result_ptr = result;
-  }
-
-  inline static void execute(const FunctorType& f, const PolicyType& p,
-                             PointerType ptr) {
-    execute_impl<typename PolicyType::work_tag>(f, p, ptr);
-  }
-};
 
-template <class FunctorType, class PolicyType, class ReducerType,
-          class PointerType, class ValueType>
-struct ParallelReduceSpecialize<FunctorType, PolicyType, ReducerType,
-                                PointerType, ValueType, false, true> {
 #pragma omp declare reduction(                                         \
     custom:ValueType                                                   \
     : OpenMPTargetReducerWrapper <ReducerType>::join(omp_out, omp_in)) \
     initializer(OpenMPTargetReducerWrapper <ReducerType>::init(omp_priv))
 
-  template <class TagType>
-  inline static void execute_impl(const FunctorType& f, const PolicyType& p,
-                                  PointerType result_ptr) {
+    OpenMPTargetReducerWrapper<ReducerType>::init(result);
+#pragma omp target teams distribute parallel for map(to                    \
+                                                     : f) reduction(custom \
+                                                                    : result)
+    for (auto i = begin; i < end; ++i) {
+      if constexpr (std::is_same<TagType, void>::value) {
+        f(i, result);
+      } else {
+        f(TagType(), i, result);
+      }
+    }
+
+    ParReduceCommon::memcpy_result(result_ptr, &result, sizeof(ValueType),
+                                   ptr_on_device);
+  }
+
+  template <class TagType, int NumReductions>
+  static void execute_array(const FunctorType& f, const PolicyType& p,
+                            PointerType result_ptr, bool ptr_on_device) {
     OpenMPTargetExec::verify_is_process(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
     OpenMPTargetExec::verify_initialized(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
-    const typename PolicyType::member_type begin = p.begin();
-    const typename PolicyType::member_type end   = p.end();
+    const auto begin = p.begin();
+    const auto end   = p.end();
 
     if (end <= begin) return;
 
     ValueType result = ValueType();
-    OpenMPTargetReducerWrapper<ReducerType>::init(result);
 
-    if constexpr (std::is_same<TagType, void>::value) {
-#pragma omp target teams distribute parallel for num_teams(512) map(to   \
-                                                                    : f) \
-    reduction(custom                                                     \
-              : result)
-      for (auto i = begin; i < end; i++) f(i, result);
-      *result_ptr = result;
+    // Enter the loop if the reduction is on a scalar type.
+    if constexpr (NumReductions == 1) {
+      // Case where reduction is on a native data type.
+      if constexpr (std::is_arithmetic<ValueType>::value) {
+#pragma omp target teams distribute parallel for \
+         map(to:f) reduction(+: result)
+        for (auto i = begin; i < end; ++i)
+
+          if constexpr (std::is_same<TagType, void>::value) {
+            f(i, result);
+          } else {
+            f(TagType(), i, result);
+          }
+      } else {
+#pragma omp declare reduction(custom:ValueType : omp_out += omp_in)
+#pragma omp target teams distribute parallel for map(to                    \
+                                                     : f) reduction(custom \
+                                                                    : result)
+        for (auto i = begin; i < end; ++i)
+
+          if constexpr (std::is_same<TagType, void>::value) {
+            f(i, result);
+          } else {
+            f(TagType(), i, result);
+          }
+      }
     } else {
-#pragma omp target teams distribute parallel for num_teams(512) map(to   \
-                                                                    : f) \
-    reduction(custom                                                     \
-              : result)
-      for (auto i = begin; i < end; i++) f(TagType(), i, result);
-      *result_ptr = result;
+#pragma omp target teams distribute parallel for map(to:f) reduction(+:result[:NumReductions])
+      for (auto i = begin; i < end; ++i) {
+        if constexpr (std::is_same<TagType, void>::value) {
+          f(i, result);
+        } else {
+          f(TagType(), i, result);
+        }
+      }
     }
+
+    ParReduceCommon::memcpy_result(result_ptr, &result, sizeof(ValueType),
+                                   ptr_on_device);
   }
 
-  inline static void execute(const FunctorType& f, const PolicyType& p,
-                             PointerType ptr) {
-    execute_impl<typename PolicyType::work_tag>(f, p, ptr);
+  static void execute_init_join(const FunctorType& f, const PolicyType& p,
+                                PointerType ptr, const bool ptr_on_device) {
+    const auto begin = p.begin();
+    const auto end   = p.end();
+
+    constexpr int HasInit = ReduceFunctorHasInit<FunctorType>::value;
+
+    // Initialize the result pointer.
+
+    const auto size = end - begin;
+
+    // FIXME_OPENMPTARGET: The team size and MAX_ACTIVE_THREADS are currently
+    // based on NVIDIA-V100 and should be modifid to be based on the
+    // architecture in the future.
+    const int max_team_threads = 32;
+    const int max_teams =
+        OpenMPTargetExec::MAX_ACTIVE_THREADS / max_team_threads;
+    // Number of elements in the reduction
+    const auto value_count =
+        FunctorValueTraits<FunctorType, TagType>::value_count(f);
+
+    // Allocate scratch per active thread. Achieved by setting the first
+    // parameter of `resize_scratch=1`.
+    OpenMPTargetExec::resize_scratch(1, 0, value_count * sizeof(ValueType));
+    ValueType* scratch_ptr =
+        static_cast<ValueType*>(OpenMPTargetExec::get_scratch_ptr());
+
+#pragma omp target map(to : f) is_device_ptr(scratch_ptr)
+    {
+      // Enter this loop if the functor has an `init`
+      if constexpr (HasInit) {
+        // The `init` routine needs to be called on the device since it might
+        // need device members.
+        ValueInit::init(f, scratch_ptr);
+        if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
+          FunctorFinal<FunctorType, TagType>::final(f, scratch_ptr);
+      } else {
+        for (int i = 0; i < value_count; ++i) {
+          static_cast<ValueType*>(scratch_ptr)[i] = ValueType();
+        }
+
+        if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
+          FunctorFinal<FunctorType, TagType>::final(f, scratch_ptr);
+      }
+    }
+
+    if (end <= begin) {
+      // If there is no work to be done, copy back the initialized values and
+      // exit.
+      if (!ptr_on_device)
+        OMPT_SAFE_CALL(omp_target_memcpy(
+            ptr, scratch_ptr, value_count * sizeof(ValueType), 0, 0,
+            omp_get_initial_device(), omp_get_default_device()));
+      else
+        OMPT_SAFE_CALL(omp_target_memcpy(
+            ptr, scratch_ptr, value_count * sizeof(ValueType), 0, 0,
+            omp_get_default_device(), omp_get_default_device()));
+
+      return;
+    }
+
+#pragma omp target teams num_teams(max_teams) thread_limit(max_team_threads) \
+    map(to                                                                   \
+        : f) is_device_ptr(scratch_ptr)
+    {
+#pragma omp parallel
+      {
+        const int team_num    = omp_get_team_num();
+        const int num_teams   = omp_get_num_teams();
+        const auto chunk_size = size / num_teams;
+        const auto team_begin = begin + team_num * chunk_size;
+        const auto team_end =
+            (team_num == num_teams - 1) ? end : (team_begin + chunk_size);
+        ValueType* team_scratch =
+            scratch_ptr + team_num * max_team_threads * value_count;
+        ReferenceType result = ValueInit::init(
+            f, &team_scratch[omp_get_thread_num() * value_count]);
+
+        // Accumulate partial results in thread specific storage.
+#pragma omp for simd
+        for (auto i = team_begin; i < team_end; ++i) {
+          if constexpr (std::is_same<TagType, void>::value) {
+            f(i, result);
+          } else {
+            f(TagType(), i, result);
+          }
+        }
+
+        // Reduce all paritial results within a team.
+        const int team_size      = max_team_threads;
+        int tree_neighbor_offset = 1;
+        do {
+#pragma omp for simd
+          for (int i = 0; i < team_size - tree_neighbor_offset;
+               i += 2 * tree_neighbor_offset) {
+            const int neighbor = i + tree_neighbor_offset;
+            ValueJoin::join(f, &team_scratch[i * value_count],
+                            &team_scratch[neighbor * value_count]);
+          }
+          tree_neighbor_offset *= 2;
+        } while (tree_neighbor_offset < team_size);
+      }  // end parallel
+    }    // end target
+
+    int tree_neighbor_offset = 1;
+    do {
+#pragma omp target teams distribute parallel for simd map(to   \
+                                                          : f) \
+    is_device_ptr(scratch_ptr)
+      for (int i = 0; i < max_teams - tree_neighbor_offset;
+           i += 2 * tree_neighbor_offset) {
+        ValueType* team_scratch = scratch_ptr;
+        const int team_offset   = max_team_threads * value_count;
+        ValueJoin::join(
+            f, &team_scratch[i * team_offset],
+            &team_scratch[(i + tree_neighbor_offset) * team_offset]);
+
+        // If `final` is provided by the functor.
+        if constexpr (ReduceFunctorHasFinal<FunctorType>::value) {
+          // Do the final only once at the end.
+          if (tree_neighbor_offset * 2 >= max_teams &&
+              omp_get_team_num() == 0 && omp_get_thread_num() == 0)
+            FunctorFinal<FunctorType, TagType>::final(f, scratch_ptr);
+        }
+      }
+      tree_neighbor_offset *= 2;
+    } while (tree_neighbor_offset < max_teams);
+
+    // If the result view is on the host, copy back the values via memcpy.
+    if (!ptr_on_device)
+      OMPT_SAFE_CALL(omp_target_memcpy(
+          ptr, scratch_ptr, value_count * sizeof(ValueType), 0, 0,
+          omp_get_initial_device(), omp_get_default_device()));
+    else
+      OMPT_SAFE_CALL(omp_target_memcpy(
+          ptr, scratch_ptr, value_count * sizeof(ValueType), 0, 0,
+          omp_get_default_device(), omp_get_default_device()));
   }
 };
 
@@ -227,47 +396,77 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
 
   using WorkTag   = typename Policy::work_tag;
   using WorkRange = typename Policy::WorkRange;
-  using Member    = typename Policy::member_type;
 
-  using ReducerConditional =
-      Kokkos::Impl::if_c<std::is_same<InvalidType, ReducerType>::value,
-                         FunctorType, ReducerType>;
-  using ReducerTypeFwd = typename ReducerConditional::type;
+  using ReducerTypeFwd =
+      typename std::conditional<std::is_same<InvalidType, ReducerType>::value,
+                                FunctorType, ReducerType>::type;
   using WorkTagFwd =
       std::conditional_t<std::is_same<InvalidType, ReducerType>::value, WorkTag,
                          void>;
 
-  // Static Assert WorkTag void if ReducerType not InvalidType
-
   using ValueTraits =
       Kokkos::Impl::FunctorValueTraits<ReducerTypeFwd, WorkTagFwd>;
-  using ValueInit = Kokkos::Impl::FunctorValueInit<ReducerTypeFwd, WorkTagFwd>;
-  using ValueJoin = Kokkos::Impl::FunctorValueJoin<ReducerTypeFwd, WorkTagFwd>;
-
-  enum { HasJoin = ReduceFunctorHasJoin<FunctorType>::value };
-  enum { UseReducer = is_reducer_type<ReducerType>::value };
 
   using pointer_type   = typename ValueTraits::pointer_type;
   using reference_type = typename ValueTraits::reference_type;
 
+  static constexpr int HasJoin    = ReduceFunctorHasJoin<FunctorType>::value;
+  static constexpr int UseReducer = is_reducer_type<ReducerType>::value;
+  static constexpr int IsArray    = std::is_pointer<reference_type>::value;
+
   using ParReduceSpecialize =
       ParallelReduceSpecialize<FunctorType, Policy, ReducerType, pointer_type,
-                               typename ValueTraits::value_type, HasJoin,
-                               UseReducer>;
+                               typename ValueTraits::value_type>;
 
   const FunctorType m_functor;
   const Policy m_policy;
   const ReducerType m_reducer;
   const pointer_type m_result_ptr;
+  bool m_result_ptr_on_device;
+  const int m_result_ptr_num_elems;
+  using TagType = typename Policy::work_tag;
 
  public:
-  inline void execute() const {
-    ParReduceSpecialize::execute(m_functor, m_policy, m_result_ptr);
+  void execute() const {
+    if constexpr (HasJoin) {
+      // Enter this loop if the Functor has a init-join.
+      ParReduceSpecialize::execute_init_join(m_functor, m_policy, m_result_ptr,
+                                             m_result_ptr_on_device);
+    } else if constexpr (UseReducer) {
+      // Enter this loop if the Functor is a reducer type.
+      ParReduceSpecialize::execute_reducer(m_functor, m_policy, m_result_ptr,
+                                           m_result_ptr_on_device);
+    } else if constexpr (IsArray) {
+      // Enter this loop if the reduction is on an array and the routine is
+      // templated over the size of the array.
+      if (m_result_ptr_num_elems <= 2) {
+        ParReduceSpecialize::template execute_array<TagType, 2>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else if (m_result_ptr_num_elems <= 4) {
+        ParReduceSpecialize::template execute_array<TagType, 4>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else if (m_result_ptr_num_elems <= 8) {
+        ParReduceSpecialize::template execute_array<TagType, 8>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else if (m_result_ptr_num_elems <= 16) {
+        ParReduceSpecialize::template execute_array<TagType, 16>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else if (m_result_ptr_num_elems <= 32) {
+        ParReduceSpecialize::template execute_array<TagType, 32>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else {
+        Kokkos::abort("array reduction length must be <= 32");
+      }
+    } else {
+      // This loop handles the basic scalar reduction.
+      ParReduceSpecialize::template execute_array<TagType, 1>(
+          m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+    }
   }
 
   template <class ViewType>
-  inline ParallelReduce(
-      const FunctorType& arg_functor, Policy arg_policy,
+  ParallelReduce(
+      const FunctorType& arg_functor, Policy& arg_policy,
       const ViewType& arg_result_view,
       typename std::enable_if<Kokkos::is_view<ViewType>::value &&
                                   !Kokkos::is_reducer_type<ReducerType>::value,
@@ -275,14 +474,23 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
       : m_functor(arg_functor),
         m_policy(arg_policy),
         m_reducer(InvalidType()),
-        m_result_ptr(arg_result_view.data()) {}
-
-  inline ParallelReduce(const FunctorType& arg_functor, Policy arg_policy,
-                        const ReducerType& reducer)
+        m_result_ptr(arg_result_view.data()),
+        m_result_ptr_on_device(
+            MemorySpaceAccess<Kokkos::Experimental::OpenMPTargetSpace,
+                              typename ViewType::memory_space>::accessible),
+        m_result_ptr_num_elems(arg_result_view.size()) {}
+
+  ParallelReduce(const FunctorType& arg_functor, Policy& arg_policy,
+                 const ReducerType& reducer)
       : m_functor(arg_functor),
         m_policy(arg_policy),
         m_reducer(reducer),
-        m_result_ptr(reducer.view().data()) {}
+        m_result_ptr(reducer.view().data()),
+        m_result_ptr_on_device(
+            MemorySpaceAccess<Kokkos::Experimental::OpenMPTargetSpace,
+                              typename ReducerType::result_view_type::
+                                  memory_space>::accessible),
+        m_result_ptr_num_elems(reducer.view().size()) {}
 };
 
 }  // namespace Impl
@@ -318,20 +526,20 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
   const Policy m_policy;
 
   template <class TagType>
-  inline typename std::enable_if<std::is_same<TagType, void>::value>::type
+  typename std::enable_if<std::is_same<TagType, void>::value>::type
   call_with_tag(const FunctorType& f, const idx_type& idx, value_type& val,
                 const bool& is_final) const {
     f(idx, val, is_final);
   }
   template <class TagType>
-  inline typename std::enable_if<!std::is_same<TagType, void>::value>::type
+  typename std::enable_if<!std::is_same<TagType, void>::value>::type
   call_with_tag(const FunctorType& f, const idx_type& idx, value_type& val,
                 const bool& is_final) const {
     f(WorkTag(), idx, val, is_final);
   }
 
  public:
-  inline void impl_execute(
+  void impl_execute(
       Kokkos::View<value_type**, Kokkos::LayoutRight,
                    Kokkos::Experimental::OpenMPTargetSpace>
           element_values,
@@ -349,13 +557,13 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
 #pragma omp target teams distribute map(to                             \
                                         : a_functor) num_teams(nteams) \
     thread_limit(team_size)
-    for (idx_type team_id = 0; team_id < n_chunks; team_id++) {
+    for (idx_type team_id = 0; team_id < n_chunks; ++team_id) {
 #pragma omp parallel num_threads(team_size)
       {
         const idx_type local_offset = team_id * chunk_size;
 
 #pragma omp for
-        for (idx_type i = 0; i < chunk_size; i++) {
+        for (idx_type i = 0; i < chunk_size; ++i) {
           const idx_type idx = local_offset + i;
           value_type val;
           ValueInit::init(a_functor, &val);
@@ -366,7 +574,7 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
         if (omp_get_thread_num() == 0) {
           value_type sum;
           ValueInit::init(a_functor, &sum);
-          for (idx_type i = 0; i < chunk_size; i++) {
+          for (idx_type i = 0; i < chunk_size; ++i) {
             ValueJoin::join(a_functor, &sum, &element_values(team_id, i));
             element_values(team_id, i) = sum;
           }
@@ -377,7 +585,7 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
           if (Kokkos::atomic_fetch_add(&count(), 1) == n_chunks - 1) {
             value_type sum;
             ValueInit::init(a_functor, &sum);
-            for (idx_type i = 0; i < n_chunks; i++) {
+            for (idx_type i = 0; i < n_chunks; ++i) {
               ValueJoin::join(a_functor, &sum, &chunk_values(i));
               chunk_values(i) = sum;
             }
@@ -389,7 +597,7 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
 #pragma omp target teams distribute map(to                             \
                                         : a_functor) num_teams(nteams) \
     thread_limit(team_size)
-    for (idx_type team_id = 0; team_id < n_chunks; team_id++) {
+    for (idx_type team_id = 0; team_id < n_chunks; ++team_id) {
 #pragma omp parallel num_threads(team_size)
       {
         const idx_type local_offset = team_id * chunk_size;
@@ -400,7 +608,7 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
           ValueInit::init(a_functor, &offset_value);
 
 #pragma omp for
-        for (idx_type i = 0; i < chunk_size; i++) {
+        for (idx_type i = 0; i < chunk_size; ++i) {
           const idx_type idx = local_offset + i;
           value_type local_offset_value;
           if (i > 0) {
@@ -415,7 +623,7 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
     }
   }
 
-  inline void execute() const {
+  void execute() const {
     OpenMPTargetExec::verify_is_process(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
     OpenMPTargetExec::verify_initialized(
@@ -438,7 +646,7 @@ class ParallelScan<FunctorType, Kokkos::RangePolicy<Traits...>,
 
   //----------------------------------------
 
-  inline ParallelScan(const FunctorType& arg_functor, const Policy& arg_policy)
+  ParallelScan(const FunctorType& arg_functor, const Policy& arg_policy)
       : m_functor(arg_functor), m_policy(arg_policy) {}
 
   //----------------------------------------
@@ -455,7 +663,7 @@ class ParallelScanWithTotal<FunctorType, Kokkos::RangePolicy<Traits...>,
   value_type& m_returnvalue;
 
  public:
-  inline void execute() const {
+  void execute() const {
     OpenMPTargetExec::verify_is_process(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
     OpenMPTargetExec::verify_initialized(
@@ -513,7 +721,7 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
   const int m_shmem_size;
 
  public:
-  inline void execute() const {
+  void execute() const {
     OpenMPTargetExec::verify_is_process(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
     OpenMPTargetExec::verify_initialized(
@@ -523,7 +731,7 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
 
  private:
   template <class TagType>
-  inline void execute_impl() const {
+  void execute_impl() const {
     OpenMPTargetExec::verify_is_process(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
     OpenMPTargetExec::verify_initialized(
@@ -549,7 +757,6 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
     const auto nteams =
         league_size < max_active_teams ? league_size : max_active_teams;
 
-#ifdef KOKKOS_IMPL_LOCK_FREE_HIERARCHICAL
 // Performing our own scheduling of teams to avoid separation of code between
 // teams-distribute and parallel. Gave a 2x performance boost in test cases with
 // the clang compiler. atomic_compare_exchange can be avoided since the standard
@@ -580,49 +787,10 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
       } else
         Kokkos::abort("`num_teams` clause was not respected.\n");
     }
-
-#else
-// Saving the older implementation that uses `atomic_compare_exchange` to
-// calculate the shared memory block index and `distribute` clause to distribute
-// teams.
-#pragma omp target teams distribute map(to                   \
-                                        : a_functor)         \
-    is_device_ptr(scratch_ptr, lock_array) num_teams(nteams) \
-        thread_limit(team_size)
-    for (int i = 0; i < league_size; i++) {
-      int shmem_block_index = -1, lock_team = 99999, iter = -1;
-      iter = (omp_get_team_num() % max_active_teams);
-
-      // Loop as long as a shmem_block_index is not found.
-      while (shmem_block_index == -1) {
-        // Try and acquire a lock on the index.
-        lock_team = atomic_compare_exchange(&lock_array[iter], 0, 1);
-
-        // If lock is acquired assign it to the block index.
-        // lock_team = 0, implies atomic_compare_exchange is successfull.
-        if (lock_team == 0)
-          shmem_block_index = iter;
-        else
-          iter = ++iter % max_active_teams;
-      }
-
-#pragma omp parallel num_threads(team_size)
-      {
-        typename Policy::member_type team(
-            i, league_size, team_size, vector_length, scratch_ptr,
-            shmem_block_index, shmem_size_L0, shmem_size_L1);
-        m_functor(team);
-      }
-
-      // Free the locked block and increment the number of available free
-      // blocks.
-      lock_team = atomic_compare_exchange(&lock_array[shmem_block_index], 1, 0);
-    }
-#endif
   }
 
  public:
-  inline ParallelFor(const FunctorType& arg_functor, const Policy& arg_policy)
+  ParallelFor(const FunctorType& arg_functor, const Policy& arg_policy)
       : m_functor(arg_functor),
         m_policy(arg_policy),
         m_shmem_size(arg_policy.scratch_size(0) + arg_policy.scratch_size(1) +
@@ -633,13 +801,26 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
 template <class FunctorType, class ReducerType, class PointerType,
           class ValueType, class... PolicyArgs>
 struct ParallelReduceSpecialize<FunctorType, TeamPolicyInternal<PolicyArgs...>,
-                                ReducerType, PointerType, ValueType, false,
-                                false> {
+                                ReducerType, PointerType, ValueType> {
   using PolicyType = TeamPolicyInternal<PolicyArgs...>;
+  using TagType    = typename PolicyType::work_tag;
+  using ReducerTypeFwd =
+      typename std::conditional<std::is_same<InvalidType, ReducerType>::value,
+                                FunctorType, ReducerType>::type;
+  using WorkTagFwd =
+      std::conditional_t<std::is_same<InvalidType, ReducerType>::value, TagType,
+                         void>;
 
-  template <class TagType>
-  inline static void execute_impl(const FunctorType& f, const PolicyType& p,
-                                  PointerType result_ptr) {
+  using ValueTraits =
+      Kokkos::Impl::FunctorValueTraits<ReducerTypeFwd, WorkTagFwd>;
+  using ValueInit     = Kokkos::Impl::FunctorValueInit<FunctorType, TagType>;
+  using ValueJoin     = Kokkos::Impl::FunctorValueJoin<FunctorType, TagType>;
+  using ReferenceType = typename ValueTraits::reference_type;
+
+  using ParReduceCommon = ParallelReduceCommon<PointerType>;
+
+  static void execute_reducer(const FunctorType& f, const PolicyType& p,
+                              PointerType result_ptr, bool ptr_on_device) {
     OpenMPTargetExec::verify_is_process(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
     OpenMPTargetExec::verify_initialized(
@@ -662,11 +843,16 @@ struct ParallelReduceSpecialize<FunctorType, TeamPolicyInternal<PolicyArgs...>,
     const auto nteams =
         league_size < max_active_teams ? league_size : max_active_teams;
 
-#ifdef KOKKOS_IMPL_LOCK_FREE_HIERARCHICAL
+#pragma omp declare reduction(                                         \
+    custom:ValueType                                                   \
+    : OpenMPTargetReducerWrapper <ReducerType>::join(omp_out, omp_in)) \
+    initializer(OpenMPTargetReducerWrapper <ReducerType>::init(omp_priv))
+
 #pragma omp target teams num_teams(nteams) thread_limit(team_size) map(to   \
                                                                        : f) \
-    is_device_ptr(scratch_ptr) reduction(+: result)
-#pragma omp parallel reduction(+ : result)
+    is_device_ptr(scratch_ptr) reduction(custom                             \
+                                         : result)
+#pragma omp parallel reduction(custom : result)
     {
       const int blockIdx = omp_get_team_num();
       const int gridDim  = omp_get_num_teams();
@@ -687,79 +873,27 @@ struct ParallelReduceSpecialize<FunctorType, TeamPolicyInternal<PolicyArgs...>,
         Kokkos::abort("`num_teams` clause was not respected.\n");
     }
 
-    *result_ptr = result;
-#else
-// Saving the older implementation that uses `atomic_compare_exchange` to
-// calculate the shared memory block index and `distribute` clause to distribute
-// teams.
-#pragma omp target teams distribute num_teams(nteams) thread_limit(team_size) \
-         map(to:f) map(tofrom:result) reduction(+: result) \
-    is_device_ptr(scratch_ptr, lock_array)
-    for (int i = 0; i < league_size; i++) {
-      ValueType inner_result = ValueType();
-      int shmem_block_index = -1, lock_team = 99999, iter = -1;
-      iter = (omp_get_team_num() % max_active_teams);
-
-      // Loop as long as a shmem_block_index is not found.
-      while (shmem_block_index == -1) {
-        // Try and acquire a lock on the index.
-        lock_team = atomic_compare_exchange(&lock_array[iter], 0, 1);
-
-        // If lock is acquired assign it to the block index.
-        // lock_team = 0, implies atomic_compare_exchange is successfull.
-        if (lock_team == 0)
-          shmem_block_index = iter;
-        else
-          iter = ++iter % max_active_teams;
-      }
-#pragma omp parallel num_threads(team_size) reduction(+ : inner_result)
-      {
-        typename PolicyType::member_type team(
-            i, league_size, team_size, vector_length, scratch_ptr,
-            shmem_block_index, shmem_size_L0, shmem_size_L1);
-        f(team, inner_result);
-      }
-      result = inner_result;
-
-      // Free the locked block and increment the number of available free
-      // blocks.
-      lock_team = atomic_compare_exchange(&lock_array[shmem_block_index], 1, 0);
-    }
-
-    *result_ptr = result;
-#endif
-  }
-
-  inline static void execute(const FunctorType& f, const PolicyType& p,
-                             PointerType ptr) {
-    execute_impl<typename PolicyType::work_tag>(f, p, ptr);
+    // Copy results back to device if `parallel_reduce` is on a device view.
+    ParReduceCommon::memcpy_result(result_ptr, &result, sizeof(ValueType),
+                                   ptr_on_device);
   }
-};
 
-template <class FunctorType, class ReducerType, class PointerType,
-          class ValueType, class... PolicyArgs>
-struct ParallelReduceSpecialize<FunctorType, TeamPolicyInternal<PolicyArgs...>,
-                                ReducerType, PointerType, ValueType, false,
-                                true> {
-  using PolicyType = TeamPolicyInternal<PolicyArgs...>;
-  template <class TagType>
-  inline static void execute_impl(const FunctorType& f, const PolicyType& p,
-                                  PointerType result_ptr) {
+  template <int NumReductions>
+  static void execute_array(const FunctorType& f, const PolicyType& p,
+                            PointerType result_ptr, bool ptr_on_device) {
     OpenMPTargetExec::verify_is_process(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
     OpenMPTargetExec::verify_initialized(
         "Kokkos::Experimental::OpenMPTarget parallel_for");
 
-#pragma omp declare reduction(                                         \
-    custom:ValueType                                                   \
-    : OpenMPTargetReducerWrapper <ReducerType>::join(omp_out, omp_in)) \
-    initializer(OpenMPTargetReducerWrapper <ReducerType>::init(omp_priv))
-    const int league_size      = p.league_size();
-    const int team_size        = p.team_size();
-    const int vector_length    = p.impl_vector_length();
+    const int league_size   = p.league_size();
+    const int team_size     = p.team_size();
+    const int vector_length = p.impl_vector_length();
+
     const size_t shmem_size_L0 = p.scratch_size(0, team_size);
     const size_t shmem_size_L1 = p.scratch_size(1, team_size);
-    OpenMPTargetExec::resize_scratch(team_size, shmem_size_L0, shmem_size_L1);
+    OpenMPTargetExec::resize_scratch(PolicyType::member_type::TEAM_REDUCE_SIZE,
+                                     shmem_size_L0, shmem_size_L1);
     void* scratch_ptr = OpenMPTargetExec::get_scratch_ptr();
 
     ValueType result = ValueType();
@@ -769,37 +903,229 @@ struct ParallelReduceSpecialize<FunctorType, TeamPolicyInternal<PolicyArgs...>,
     const auto nteams =
         league_size < max_active_teams ? league_size : max_active_teams;
 
+    // Case where the number of reduction items is 1.
+    if constexpr (NumReductions == 1) {
+      // Case where reduction is on a native data type.
+      if constexpr (std::is_arithmetic<ValueType>::value) {
+#pragma omp target teams num_teams(nteams) thread_limit(team_size) map(to   \
+                                                                       : f) \
+    is_device_ptr(scratch_ptr) reduction(+: result)
+#pragma omp parallel reduction(+ : result)
+        {
+          const int blockIdx = omp_get_team_num();
+          const int gridDim  = omp_get_num_teams();
+
+          // Guarantee that the compilers respect the `num_teams` clause
+          if (gridDim <= nteams) {
+            for (int league_id = blockIdx; league_id < league_size;
+                 league_id += gridDim) {
+              typename PolicyType::member_type team(
+                  league_id, league_size, team_size, vector_length, scratch_ptr,
+                  blockIdx, shmem_size_L0, shmem_size_L1);
+              if constexpr (std::is_same<TagType, void>::value)
+                f(team, result);
+              else
+                f(TagType(), team, result);
+            }
+          } else
+            Kokkos::abort("`num_teams` clause was not respected.\n");
+        }
+      } else {
+        // Case where the reduction is on a non-native data type.
+#pragma omp declare reduction(custom:ValueType : omp_out += omp_in)
 #pragma omp target teams num_teams(nteams) thread_limit(team_size) map(to   \
                                                                        : f) \
     is_device_ptr(scratch_ptr) reduction(custom                             \
                                          : result)
 #pragma omp parallel reduction(custom : result)
-    {
-      const int blockIdx = omp_get_team_num();
-      const int gridDim  = omp_get_num_teams();
+        {
+          const int blockIdx = omp_get_team_num();
+          const int gridDim  = omp_get_num_teams();
+
+          // Guarantee that the compilers respect the `num_teams` clause
+          if (gridDim <= nteams) {
+            for (int league_id = blockIdx; league_id < league_size;
+                 league_id += gridDim) {
+              typename PolicyType::member_type team(
+                  league_id, league_size, team_size, vector_length, scratch_ptr,
+                  blockIdx, shmem_size_L0, shmem_size_L1);
+              if constexpr (std::is_same<TagType, void>::value)
+                f(team, result);
+              else
+                f(TagType(), team, result);
+            }
+          } else
+            Kokkos::abort("`num_teams` clause was not respected.\n");
+        }
+      }
+    } else {
+      // Case where the reduction is on an array.
+#pragma omp target teams num_teams(nteams) thread_limit(team_size) map(to   \
+                                                                       : f) \
+    is_device_ptr(scratch_ptr) reduction(+ : result[:NumReductions])
+#pragma omp parallel reduction(+ : result[:NumReductions])
+      {
+        const int blockIdx = omp_get_team_num();
+        const int gridDim  = omp_get_num_teams();
+
+        // Guarantee that the compilers respect the `num_teams` clause
+        if (gridDim <= nteams) {
+          for (int league_id = blockIdx; league_id < league_size;
+               league_id += gridDim) {
+            typename PolicyType::member_type team(
+                league_id, league_size, team_size, vector_length, scratch_ptr,
+                blockIdx, shmem_size_L0, shmem_size_L1);
+            if constexpr (std::is_same<TagType, void>::value)
+              f(team, result);
+            else
+              f(TagType(), team, result);
+          }
+        } else
+          Kokkos::abort("`num_teams` clause was not respected.\n");
+      }
+    }
 
-      // Guarantee that the compilers respect the `num_teams` clause
-      if (gridDim <= nteams) {
-        for (int league_id = blockIdx; league_id < league_size;
-             league_id += gridDim) {
+    // Copy results back to device if `parallel_reduce` is on a device view.
+    ParReduceCommon::memcpy_result(result_ptr, &result, sizeof(ValueType),
+                                   ptr_on_device);
+  }
+
+  // FIXME_OPENMPTARGET : This routine is a copy from `parallel_reduce` over
+  // RangePolicy. Need a new implementation.
+  static void execute_init_join(const FunctorType& f, const PolicyType& p,
+                                PointerType ptr, const bool ptr_on_device) {
+    const auto begin      = p.begin();
+    const auto end        = p.end();
+    constexpr int HasInit = ReduceFunctorHasInit<FunctorType>::value;
+
+    const auto size = end - begin;
+
+    const int league_size   = p.league_size();
+    const int team_size     = p.team_size();
+    const int vector_length = p.impl_vector_length();
+
+    const size_t shmem_size_L0 = p.scratch_size(0, team_size);
+    const size_t shmem_size_L1 = p.scratch_size(1, team_size);
+
+    // FIXME_OPENMPTARGET: This would oversubscribe scratch memory since we are
+    // already using the available scratch memory to create temporaries for each
+    // thread.
+    if constexpr ((shmem_size_L0 + shmem_size_L1) > 0) {
+      Kokkos::abort(
+          "OpenMPTarget: Scratch memory is not supported in `parallel_reduce` "
+          "over functors with init/join.");
+    }
+
+    // Maximum active teams possible.
+    int max_active_teams = OpenMPTargetExec::MAX_ACTIVE_THREADS / team_size;
+    const auto nteams =
+        league_size < max_active_teams ? league_size : max_active_teams;
+
+    // Number of elements in the reduction
+    const auto value_count =
+        FunctorValueTraits<FunctorType, TagType>::value_count(f);
+
+    // Allocate scratch per active thread.
+    OpenMPTargetExec::resize_scratch(1, 0, value_count * sizeof(ValueType));
+    void* scratch_ptr = OpenMPTargetExec::get_scratch_ptr();
+
+    // Enter this loop if the functor has an `init`
+    if constexpr (HasInit) {
+      // The `init` routine needs to be called on the device since it might need
+      // device members.
+#pragma omp target map(to : f) is_device_ptr(scratch_ptr)
+      {
+        ValueInit::init(f, scratch_ptr);
+
+        if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
+          FunctorFinal<FunctorType, TagType>::final(f, scratch_ptr);
+      }
+    } else {
+#pragma omp target map(to : f) is_device_ptr(scratch_ptr)
+      {
+        for (int i = 0; i < value_count; ++i) {
+          static_cast<ValueType*>(scratch_ptr)[i] = ValueType();
+        }
+
+        if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
+          FunctorFinal<FunctorType, TagType>::final(f, scratch_ptr);
+      }
+    }
+
+    if (end <= begin) {
+      // If there is no work to be done, copy back the initialized values and
+      // exit.
+      if (!ptr_on_device)
+        OMPT_SAFE_CALL(omp_target_memcpy(
+            ptr, scratch_ptr, value_count * sizeof(ValueType), 0, 0,
+            omp_get_initial_device(), omp_get_default_device()));
+      else
+        OMPT_SAFE_CALL(omp_target_memcpy(
+            ptr, scratch_ptr, value_count * sizeof(ValueType), 0, 0,
+            omp_get_default_device(), omp_get_default_device()));
+
+      return;
+    }
+
+#pragma omp target teams num_teams(nteams) thread_limit(team_size) map(to   \
+                                                                       : f) \
+    is_device_ptr(scratch_ptr)
+    {
+#pragma omp parallel
+      {
+        const int team_num      = omp_get_team_num();
+        const int num_teams     = omp_get_num_teams();
+        ValueType* team_scratch = static_cast<ValueType*>(scratch_ptr) +
+                                  team_num * team_size * value_count;
+        ReferenceType result = ValueInit::init(f, &team_scratch[0]);
+
+        for (int league_id = team_num; league_id < league_size;
+             league_id += num_teams) {
           typename PolicyType::member_type team(
               league_id, league_size, team_size, vector_length, scratch_ptr,
-              blockIdx, shmem_size_L0, shmem_size_L1);
-          if constexpr (std::is_same<TagType, void>::value)
+              team_num, shmem_size_L0, shmem_size_L1);
+          if constexpr (std::is_same<TagType, void>::value) {
             f(team, result);
-          else
+          } else {
             f(TagType(), team, result);
+          }
         }
-      } else
-        Kokkos::abort("`num_teams` clause was not respected.\n");
-    }
-
-    *result_ptr = result;
-  }
-
-  inline static void execute(const FunctorType& f, const PolicyType& p,
-                             PointerType ptr) {
-    execute_impl<typename PolicyType::work_tag>(f, p, ptr);
+      }  // end parallel
+    }    // end target
+
+    int tree_neighbor_offset = 1;
+    do {
+#pragma omp target teams distribute parallel for simd map(to   \
+                                                          : f) \
+    is_device_ptr(scratch_ptr)
+      for (int i = 0; i < nteams - tree_neighbor_offset;
+           i += 2 * tree_neighbor_offset) {
+        ValueType* team_scratch = scratch_ptr;
+        const int team_offset   = team_size * value_count;
+        ValueJoin::join(
+            f, &team_scratch[i * team_offset],
+            &team_scratch[(i + tree_neighbor_offset) * team_offset]);
+
+        // If `final` is provided by the functor.
+        if constexpr (ReduceFunctorHasFinal<FunctorType>::value) {
+          // Do the final only once at the end.
+          if (tree_neighbor_offset * 2 >= nteams && omp_get_team_num() == 0 &&
+              omp_get_thread_num() == 0)
+            FunctorFinal<FunctorType, TagType>::final(f, scratch_ptr);
+        }
+      }
+      tree_neighbor_offset *= 2;
+    } while (tree_neighbor_offset < nteams);
+
+    // If the result view is on the host, copy back the values via memcpy.
+    if (!ptr_on_device)
+      OMPT_SAFE_CALL(omp_target_memcpy(
+          ptr, scratch_ptr, value_count * sizeof(ValueType), 0, 0,
+          omp_get_initial_device(), omp_get_default_device()));
+    else
+      OMPT_SAFE_CALL(omp_target_memcpy(
+          ptr, scratch_ptr, value_count * sizeof(ValueType), 0, 0,
+          omp_get_default_device(), omp_get_default_device()));
   }
 };
 
@@ -813,11 +1139,9 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
 
   using WorkTag = typename Policy::work_tag;
   using Member  = typename Policy::member_type;
-
-  using ReducerConditional =
-      Kokkos::Impl::if_c<std::is_same<InvalidType, ReducerType>::value,
-                         FunctorType, ReducerType>;
-  using ReducerTypeFwd = typename ReducerConditional::type;
+  using ReducerTypeFwd =
+      typename std::conditional<std::is_same<InvalidType, ReducerType>::value,
+                                FunctorType, ReducerType>::type;
   using WorkTagFwd =
       std::conditional_t<std::is_same<InvalidType, ReducerType>::value, WorkTag,
                          void>;
@@ -831,13 +1155,16 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
   using reference_type = typename ValueTraits::reference_type;
   using value_type     = typename ValueTraits::value_type;
 
-  enum { HasJoin = ReduceFunctorHasJoin<FunctorType>::value };
-  enum { UseReducer = is_reducer_type<ReducerType>::value };
+  bool m_result_ptr_on_device;
+  const int m_result_ptr_num_elems;
+
+  static constexpr int HasJoin    = ReduceFunctorHasJoin<FunctorType>::value;
+  static constexpr int UseReducer = is_reducer_type<ReducerType>::value;
+  static constexpr int IsArray    = std::is_pointer<reference_type>::value;
 
-  using ParForSpecialize =
+  using ParReduceSpecialize =
       ParallelReduceSpecialize<FunctorType, Policy, ReducerType, pointer_type,
-                               typename ValueTraits::value_type, HasJoin,
-                               UseReducer>;
+                               typename ValueTraits::value_type>;
 
   const FunctorType m_functor;
   const Policy m_policy;
@@ -846,18 +1173,50 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
   const int m_shmem_size;
 
  public:
-  inline void execute() const {
-    ParForSpecialize::execute(m_functor, m_policy, m_result_ptr);
+  void execute() const {
+    if constexpr (HasJoin) {
+      ParReduceSpecialize::execute_init_join(m_functor, m_policy, m_result_ptr,
+                                             m_result_ptr_on_device);
+    } else if constexpr (UseReducer) {
+      ParReduceSpecialize::execute_reducer(m_functor, m_policy, m_result_ptr,
+                                           m_result_ptr_on_device);
+    } else if constexpr (IsArray) {
+      if (m_result_ptr_num_elems <= 2) {
+        ParReduceSpecialize::template execute_array<2>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else if (m_result_ptr_num_elems <= 4) {
+        ParReduceSpecialize::template execute_array<4>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else if (m_result_ptr_num_elems <= 8) {
+        ParReduceSpecialize::template execute_array<8>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else if (m_result_ptr_num_elems <= 16) {
+        ParReduceSpecialize::template execute_array<16>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else if (m_result_ptr_num_elems <= 32) {
+        ParReduceSpecialize::template execute_array<32>(
+            m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+      } else {
+        Kokkos::abort("array reduction length must be <= 32");
+      }
+    } else {
+      ParReduceSpecialize::template execute_array<1>(
+          m_functor, m_policy, m_result_ptr, m_result_ptr_on_device);
+    }
   }
 
   template <class ViewType>
-  inline ParallelReduce(
+  ParallelReduce(
       const FunctorType& arg_functor, const Policy& arg_policy,
       const ViewType& arg_result,
       typename std::enable_if<Kokkos::is_view<ViewType>::value &&
                                   !Kokkos::is_reducer_type<ReducerType>::value,
                               void*>::type = nullptr)
-      : m_functor(arg_functor),
+      : m_result_ptr_on_device(
+            MemorySpaceAccess<Kokkos::Experimental::OpenMPTargetSpace,
+                              typename ViewType::memory_space>::accessible),
+        m_result_ptr_num_elems(arg_result.size()),
+        m_functor(arg_functor),
         m_policy(arg_policy),
         m_reducer(InvalidType()),
         m_result_ptr(arg_result.data()),
@@ -865,9 +1224,14 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
                      FunctorTeamShmemSize<FunctorType>::value(
                          arg_functor, arg_policy.team_size())) {}
 
-  inline ParallelReduce(const FunctorType& arg_functor, Policy arg_policy,
-                        const ReducerType& reducer)
-      : m_functor(arg_functor),
+  ParallelReduce(const FunctorType& arg_functor, Policy& arg_policy,
+                 const ReducerType& reducer)
+      : m_result_ptr_on_device(
+            MemorySpaceAccess<Kokkos::Experimental::OpenMPTargetSpace,
+                              typename ReducerType::result_view_type::
+                                  memory_space>::accessible),
+        m_result_ptr_num_elems(reducer.view().size()),
+        m_functor(arg_functor),
         m_policy(arg_policy),
         m_reducer(reducer),
         m_result_ptr(reducer.view().data()),
@@ -889,11 +1253,11 @@ struct TeamThreadRangeBoundariesStruct<iType, OpenMPTargetExecTeamMember> {
   const iType end;
   const OpenMPTargetExecTeamMember& team;
 
-  inline TeamThreadRangeBoundariesStruct(
-      const OpenMPTargetExecTeamMember& thread_, iType count)
+  TeamThreadRangeBoundariesStruct(const OpenMPTargetExecTeamMember& thread_,
+                                  iType count)
       : start(0), end(count), team(thread_) {}
-  inline TeamThreadRangeBoundariesStruct(
-      const OpenMPTargetExecTeamMember& thread_, iType begin_, iType end_)
+  TeamThreadRangeBoundariesStruct(const OpenMPTargetExecTeamMember& thread_,
+                                  iType begin_, iType end_)
       : start(begin_), end(end_), team(thread_) {}
 };
 
@@ -904,12 +1268,11 @@ struct ThreadVectorRangeBoundariesStruct<iType, OpenMPTargetExecTeamMember> {
   const index_type end;
   const OpenMPTargetExecTeamMember& team;
 
-  inline ThreadVectorRangeBoundariesStruct(
-      const OpenMPTargetExecTeamMember& thread_, index_type count)
+  ThreadVectorRangeBoundariesStruct(const OpenMPTargetExecTeamMember& thread_,
+                                    index_type count)
       : start(0), end(count), team(thread_) {}
-  inline ThreadVectorRangeBoundariesStruct(
-      const OpenMPTargetExecTeamMember& thread_, index_type begin_,
-      index_type end_)
+  ThreadVectorRangeBoundariesStruct(const OpenMPTargetExecTeamMember& thread_,
+                                    index_type begin_, index_type end_)
       : start(begin_), end(end_), team(thread_) {}
 };
 
@@ -920,12 +1283,11 @@ struct TeamVectorRangeBoundariesStruct<iType, OpenMPTargetExecTeamMember> {
   const index_type end;
   const OpenMPTargetExecTeamMember& team;
 
-  inline TeamVectorRangeBoundariesStruct(
-      const OpenMPTargetExecTeamMember& thread_, index_type count)
+  TeamVectorRangeBoundariesStruct(const OpenMPTargetExecTeamMember& thread_,
+                                  index_type count)
       : start(0), end(count), team(thread_) {}
-  inline TeamVectorRangeBoundariesStruct(
-      const OpenMPTargetExecTeamMember& thread_, index_type begin_,
-      index_type end_)
+  TeamVectorRangeBoundariesStruct(const OpenMPTargetExecTeamMember& thread_,
+                                  index_type begin_, index_type end_)
       : start(begin_), end(end_), team(thread_) {}
 };
 
@@ -935,5 +1297,4 @@ struct TeamVectorRangeBoundariesStruct<iType, OpenMPTargetExecTeamMember> {
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 
-#undef KOKKOS_IMPL_LOCK_FREE_HIERARCHICAL
 #endif /* KOKKOS_OPENMPTARGET_PARALLEL_HPP */
diff --git a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel_MDRange.hpp b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel_MDRange.hpp
index 3dfad2bb856e0bb65a48dfd70b3458cee4c9beb5..40d8c45f5d0f8ce6798079d9eb3deb48a4361122 100644
--- a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel_MDRange.hpp
+++ b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Parallel_MDRange.hpp
@@ -91,7 +91,7 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
 
 #pragma omp target teams distribute map(to : functor) num_teams(end - begin)
     {
-      for (ptrdiff_t tile_idx = begin; tile_idx < end; tile_idx++) {
+      for (ptrdiff_t tile_idx = begin; tile_idx < end; ++tile_idx) {
 
 #pragma omp parallel
         {
@@ -116,31 +116,6 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
 #endif
   }
 
-  template <int Rank>
-  inline typename std::enable_if<Rank == 1>::type execute_tile(
-      typename Policy::point_type offset, const FunctorType& functor,
-      const Policy& policy) const {
-#ifdef KOKKOS_IMPL_MDRANGE_USE_NO_TILES
-    (void)offset;
-    const auto begin_0 = policy.m_lower[0];
-
-    const auto end_0 = policy.m_upper[0];
-
-#pragma omp target teams distribute parallel for map(to : functor)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++) {
-      functor(i0);
-    }
-#else
-    const ptrdiff_t begin_0 = offset[0];
-    ptrdiff_t end_0         = begin_0 + policy.m_tile[0];
-    end_0 = end_0 < policy.m_upper[0] ? end_0 : policy.m_upper[0];
-#pragma omp for
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++) {
-      functor(i0);
-    }
-#endif
-  }
-
   template <int Rank>
   inline typename std::enable_if<Rank == 2>::type execute_tile(
       typename Policy::point_type offset, const FunctorType& functor,
@@ -154,8 +129,8 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     const auto end_1 = policy.m_upper[1];
 
 #pragma omp target teams distribute parallel for collapse(2) map(to : functor)
-    for (auto i0 = begin_0; i0 < end_0; i0++) {
-      for (auto i1 = begin_1; i1 < end_1; i1++) {
+    for (auto i0 = begin_0; i0 < end_0; ++i0) {
+      for (auto i1 = begin_1; i1 < end_1; ++i1) {
         if constexpr (std::is_same<typename Policy::work_tag, void>::value)
           functor(i0, i1);
         else
@@ -172,8 +147,8 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     end_1 = end_1 < policy.m_upper[1] ? end_1 : policy.m_upper[1];
 
 #pragma omp for collapse(2)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++)
-      for (ptrdiff_t i1 = begin_1; i1 < end_1; i1++) {
+    for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0)
+      for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1) {
         if constexpr (std::is_same<typename Policy::work_tag, void>::value)
           functor(i0, i1);
         else
@@ -197,9 +172,9 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     const auto end_2 = policy.m_upper[2];
 
 #pragma omp target teams distribute parallel for collapse(3) map(to : functor)
-    for (auto i0 = begin_0; i0 < end_0; i0++) {
-      for (auto i1 = begin_1; i1 < end_1; i1++) {
-        for (auto i2 = begin_2; i2 < end_2; i2++) {
+    for (auto i0 = begin_0; i0 < end_0; ++i0) {
+      for (auto i1 = begin_1; i1 < end_1; ++i1) {
+        for (auto i2 = begin_2; i2 < end_2; ++i2) {
           if constexpr (std::is_same<typename Policy::work_tag, void>::value)
             functor(i0, i1, i2);
           else
@@ -221,9 +196,9 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     end_2 = end_2 < policy.m_upper[2] ? end_2 : policy.m_upper[2];
 
 #pragma omp for collapse(3)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++)
-      for (ptrdiff_t i1 = begin_1; i1 < end_1; i1++)
-        for (ptrdiff_t i2 = begin_2; i2 < end_2; i2++) {
+    for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0)
+      for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1)
+        for (ptrdiff_t i2 = begin_2; i2 < end_2; ++i2) {
           if constexpr (std::is_same<typename Policy::work_tag, void>::value)
             functor(i0, i1, i2);
           else
@@ -249,10 +224,10 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     const auto end_3 = policy.m_upper[3];
 
 #pragma omp target teams distribute parallel for collapse(4) map(to : functor)
-    for (auto i0 = begin_0; i0 < end_0; i0++) {
-      for (auto i1 = begin_1; i1 < end_1; i1++) {
-        for (auto i2 = begin_2; i2 < end_2; i2++) {
-          for (auto i3 = begin_3; i3 < end_3; i3++) {
+    for (auto i0 = begin_0; i0 < end_0; ++i0) {
+      for (auto i1 = begin_1; i1 < end_1; ++i1) {
+        for (auto i2 = begin_2; i2 < end_2; ++i2) {
+          for (auto i3 = begin_3; i3 < end_3; ++i3) {
             if constexpr (std::is_same<typename Policy::work_tag, void>::value)
               functor(i0, i1, i2, i3);
             else
@@ -279,10 +254,10 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     end_3 = end_3 < policy.m_upper[3] ? end_3 : policy.m_upper[3];
 
 #pragma omp for collapse(4)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++)
-      for (ptrdiff_t i1 = begin_1; i1 < end_1; i1++)
-        for (ptrdiff_t i2 = begin_2; i2 < end_2; i2++)
-          for (ptrdiff_t i3 = begin_3; i3 < end_3; i3++) {
+    for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0)
+      for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1)
+        for (ptrdiff_t i2 = begin_2; i2 < end_2; ++i2)
+          for (ptrdiff_t i3 = begin_3; i3 < end_3; ++i3) {
             if constexpr (std::is_same<typename Policy::work_tag, void>::value)
               functor(i0, i1, i2, i3);
             else
@@ -310,11 +285,11 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     const auto end_4 = policy.m_upper[4];
 
 #pragma omp target teams distribute parallel for collapse(5) map(to : functor)
-    for (auto i0 = begin_0; i0 < end_0; i0++) {
-      for (auto i1 = begin_1; i1 < end_1; i1++) {
-        for (auto i2 = begin_2; i2 < end_2; i2++) {
-          for (auto i3 = begin_3; i3 < end_3; i3++) {
-            for (auto i4 = begin_4; i4 < end_4; i4++) {
+    for (auto i0 = begin_0; i0 < end_0; ++i0) {
+      for (auto i1 = begin_1; i1 < end_1; ++i1) {
+        for (auto i2 = begin_2; i2 < end_2; ++i2) {
+          for (auto i3 = begin_3; i3 < end_3; ++i3) {
+            for (auto i4 = begin_4; i4 < end_4; ++i4) {
               if constexpr (std::is_same<typename Policy::work_tag,
                                          void>::value)
                 functor(i0, i1, i2, i3, i4);
@@ -347,11 +322,11 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     end_4 = end_4 < policy.m_upper[4] ? end_4 : policy.m_upper[4];
 
 #pragma omp for collapse(5)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++)
-      for (ptrdiff_t i1 = begin_1; i1 < end_1; i1++)
-        for (ptrdiff_t i2 = begin_2; i2 < end_2; i2++)
-          for (ptrdiff_t i3 = begin_3; i3 < end_3; i3++)
-            for (ptrdiff_t i4 = begin_4; i4 < end_4; i4++) {
+    for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0)
+      for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1)
+        for (ptrdiff_t i2 = begin_2; i2 < end_2; ++i2)
+          for (ptrdiff_t i3 = begin_3; i3 < end_3; ++i3)
+            for (ptrdiff_t i4 = begin_4; i4 < end_4; ++i4) {
               if constexpr (std::is_same<typename Policy::work_tag,
                                          void>::value)
                 functor(i0, i1, i2, i3, i4);
@@ -382,12 +357,12 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     const auto end_5 = policy.m_upper[5];
 
 #pragma omp target teams distribute parallel for collapse(6) map(to : functor)
-    for (auto i0 = begin_0; i0 < end_0; i0++) {
-      for (auto i1 = begin_1; i1 < end_1; i1++) {
-        for (auto i2 = begin_2; i2 < end_2; i2++) {
-          for (auto i3 = begin_3; i3 < end_3; i3++) {
-            for (auto i4 = begin_4; i4 < end_4; i4++) {
-              for (auto i5 = begin_5; i5 < end_5; i5++) {
+    for (auto i0 = begin_0; i0 < end_0; ++i0) {
+      for (auto i1 = begin_1; i1 < end_1; ++i1) {
+        for (auto i2 = begin_2; i2 < end_2; ++i2) {
+          for (auto i3 = begin_3; i3 < end_3; ++i3) {
+            for (auto i4 = begin_4; i4 < end_4; ++i4) {
+              for (auto i5 = begin_5; i5 < end_5; ++i5) {
                 {
                   if constexpr (std::is_same<typename Policy::work_tag,
                                              void>::value)
@@ -428,12 +403,12 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
     end_5 = end_5 < policy.m_upper[5] ? end_5 : policy.m_upper[5];
 
 #pragma omp for collapse(6)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++)
-      for (ptrdiff_t i1 = begin_1; i1 < end_1; i1++)
-        for (ptrdiff_t i2 = begin_2; i2 < end_2; i2++)
-          for (ptrdiff_t i3 = begin_3; i3 < end_3; i3++)
-            for (ptrdiff_t i4 = begin_4; i4 < end_4; i4++)
-              for (ptrdiff_t i5 = begin_5; i5 < end_5; i5++) {
+    for (ptrdiff_t i0 = begin_0; i0 < end_0; ++i0)
+      for (ptrdiff_t i1 = begin_1; i1 < end_1; ++i1)
+        for (ptrdiff_t i2 = begin_2; i2 < end_2; ++i2)
+          for (ptrdiff_t i3 = begin_3; i3 < end_3; ++i3)
+            for (ptrdiff_t i4 = begin_4; i4 < end_4; ++i4)
+              for (ptrdiff_t i5 = begin_5; i5 < end_5; ++i5) {
                 if constexpr (std::is_same<typename Policy::work_tag,
                                            void>::value)
                   functor(i0, i1, i2, i3, i4, i5);
@@ -443,195 +418,6 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
 #endif
   }
 
-  template <int Rank>
-  inline typename std::enable_if<Rank == 7>::type execute_tile(
-      typename Policy::point_type offset, const FunctorType& functor,
-      const Policy& policy) const {
-#ifdef KOKKOS_IMPL_MDRANGE_USE_NO_TILES
-    (void)offset;
-    const int begin_0 = policy.m_lower[0];
-    const int begin_1 = policy.m_lower[1];
-    const int begin_2 = policy.m_lower[2];
-    const int begin_3 = policy.m_lower[3];
-    const int begin_4 = policy.m_lower[4];
-    const int begin_5 = policy.m_lower[5];
-    const int begin_6 = policy.m_lower[6];
-
-    const int end_0 = policy.m_upper[0];
-    const int end_1 = policy.m_upper[1];
-    const int end_2 = policy.m_upper[2];
-    const int end_3 = policy.m_upper[3];
-    const int end_4 = policy.m_upper[4];
-    const int end_5 = policy.m_upper[5];
-    const int end_6 = policy.m_upper[6];
-
-#pragma omp target teams distribute parallel for collapse(7) map(to : functor)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++) {
-      for (ptrdiff_t i1 = begin_1; i1 < end_1; i1++) {
-        for (ptrdiff_t i2 = begin_2; i2 < end_2; i2++) {
-          for (ptrdiff_t i3 = begin_3; i3 < end_3; i3++) {
-            for (ptrdiff_t i4 = begin_4; i4 < end_4; i4++) {
-              for (ptrdiff_t i5 = begin_5; i5 < end_5; i5++) {
-                for (ptrdiff_t i6 = begin_6; i6 < end_6; i6++) {
-                  if constexpr (std::is_same<typename Policy::work_tag,
-                                             void>::value)
-                    functor(i0, i1, i2, i3, i4, i5, i6);
-                  else
-                    functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, i5,
-                            i6);
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-#else
-    const ptrdiff_t begin_0 = offset[0];
-    ptrdiff_t end_0         = begin_0 + policy.m_tile[0];
-    end_0 = end_0 < policy.m_upper[0] ? end_0 : policy.m_upper[0];
-
-    const ptrdiff_t begin_1 = offset[1];
-    ptrdiff_t end_1         = begin_1 + policy.m_tile[1];
-    end_1 = end_1 < policy.m_upper[1] ? end_1 : policy.m_upper[1];
-
-    const ptrdiff_t begin_2 = offset[2];
-    ptrdiff_t end_2         = begin_2 + policy.m_tile[2];
-    end_2 = end_2 < policy.m_upper[2] ? end_2 : policy.m_upper[2];
-
-    const ptrdiff_t begin_3 = offset[3];
-    ptrdiff_t end_3         = begin_3 + policy.m_tile[3];
-    end_3 = end_3 < policy.m_upper[3] ? end_3 : policy.m_upper[3];
-
-    const ptrdiff_t begin_4 = offset[4];
-    ptrdiff_t end_4         = begin_4 + policy.m_tile[4];
-    end_4 = end_4 < policy.m_upper[4] ? end_4 : policy.m_upper[4];
-
-    const ptrdiff_t begin_5 = offset[5];
-    ptrdiff_t end_5         = begin_5 + policy.m_tile[5];
-    end_5 = end_5 < policy.m_upper[5] ? end_5 : policy.m_upper[5];
-
-    const ptrdiff_t begin_6 = offset[6];
-    ptrdiff_t end_6         = begin_6 + policy.m_tile[6];
-    end_6 = end_6 < policy.m_upper[6] ? end_6 : policy.m_upper[6];
-
-#pragma omp for collapse(7)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++)
-      for (ptrdiff_t i1 = begin_1; i1 < end_1; i1++)
-        for (ptrdiff_t i2 = begin_2; i2 < end_2; i2++)
-          for (ptrdiff_t i3 = begin_3; i3 < end_3; i3++)
-            for (ptrdiff_t i4 = begin_4; i4 < end_4; i4++)
-              for (ptrdiff_t i5 = begin_5; i5 < end_5; i5++)
-                for (ptrdiff_t i6 = begin_6; i6 < end_6; i6++) {
-                  if constexpr (std::is_same<typename Policy::work_tag,
-                                             void>::value)
-                    functor(i0, i1, i2, i3, i4, i5, i6);
-                  else
-                    functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, i5,
-                            i6);
-                }
-#endif
-  }
-
-  template <int Rank>
-  inline typename std::enable_if<Rank == 8>::type execute_tile(
-      typename Policy::point_type offset, const FunctorType& functor,
-      const Policy& policy) const {
-#ifdef KOKKOS_IMPL_MDRANGE_USE_NO_TILES
-    (void)offset;
-    const int begin_0 = policy.m_lower[0];
-    const int begin_1 = policy.m_lower[1];
-    const int begin_2 = policy.m_lower[2];
-    const int begin_3 = policy.m_lower[3];
-    const int begin_4 = policy.m_lower[4];
-    const int begin_5 = policy.m_lower[5];
-    const int begin_6 = policy.m_lower[6];
-    const int begin_7 = policy.m_lower[7];
-
-    const int end_0 = policy.m_upper[0];
-    const int end_1 = policy.m_upper[1];
-    const int end_2 = policy.m_upper[2];
-    const int end_3 = policy.m_upper[3];
-    const int end_4 = policy.m_upper[4];
-    const int end_5 = policy.m_upper[5];
-    const int end_6 = policy.m_upper[6];
-    const int end_7 = policy.m_upper[7];
-
-#pragma omp target teams distribute parallel for collapse(8) map(to : functor)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++) {
-      for (ptrdiff_t i1 = begin_1; i1 < end_1; i1++) {
-        for (ptrdiff_t i2 = begin_2; i2 < end_2; i2++) {
-          for (ptrdiff_t i3 = begin_3; i3 < end_3; i3++) {
-            for (ptrdiff_t i4 = begin_4; i4 < end_4; i4++) {
-              for (ptrdiff_t i5 = begin_5; i5 < end_5; i5++) {
-                for (ptrdiff_t i6 = begin_6; i6 < end_6; i6++) {
-                  for (ptrdiff_t i7 = begin_7; i7 < end_7; i7++) {
-                    if constexpr (std::is_same<typename Policy::work_tag,
-                                               void>::value)
-                      functor(i0, i1, i2, i3, i4, i5, i6, i7);
-                    else
-                      functor(typename Policy::work_tag(), i0, i1, i2, i3, i4,
-                              i5, i6, i7);
-                  }
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-#else
-    const ptrdiff_t begin_0 = offset[0];
-    ptrdiff_t end_0         = begin_0 + policy.m_tile[0];
-    end_0 = end_0 < policy.m_upper[0] ? end_0 : policy.m_upper[0];
-
-    const ptrdiff_t begin_1 = offset[1];
-    ptrdiff_t end_1         = begin_1 + policy.m_tile[1];
-    end_1 = end_1 < policy.m_upper[1] ? end_1 : policy.m_upper[1];
-
-    const ptrdiff_t begin_2 = offset[2];
-    ptrdiff_t end_2         = begin_2 + policy.m_tile[2];
-    end_2 = end_2 < policy.m_upper[2] ? end_2 : policy.m_upper[2];
-
-    const ptrdiff_t begin_3 = offset[3];
-    ptrdiff_t end_3         = begin_3 + policy.m_tile[3];
-    end_3 = end_3 < policy.m_upper[3] ? end_3 : policy.m_upper[3];
-
-    const ptrdiff_t begin_4 = offset[4];
-    ptrdiff_t end_4         = begin_4 + policy.m_tile[4];
-    end_4 = end_4 < policy.m_upper[4] ? end_4 : policy.m_upper[4];
-
-    const ptrdiff_t begin_5 = offset[5];
-    ptrdiff_t end_5         = begin_5 + policy.m_tile[5];
-    end_5 = end_5 < policy.m_upper[5] ? end_5 : policy.m_upper[5];
-
-    const ptrdiff_t begin_6 = offset[6];
-    ptrdiff_t end_6         = begin_6 + policy.m_tile[6];
-    end_6 = end_6 < policy.m_upper[6] ? end_6 : policy.m_upper[6];
-
-    const ptrdiff_t begin_7 = offset[7];
-    ptrdiff_t end_7         = begin_7 + policy.m_tile[7];
-    end_7 = end_7 < policy.m_upper[7] ? end_7 : policy.m_upper[7];
-
-#pragma omp for collapse(8)
-    for (ptrdiff_t i0 = begin_0; i0 < end_0; i0++)
-      for (ptrdiff_t i1 = begin_1; i1 < end_1; i1++)
-        for (ptrdiff_t i2 = begin_2; i2 < end_2; i2++)
-          for (ptrdiff_t i3 = begin_3; i3 < end_3; i3++)
-            for (ptrdiff_t i4 = begin_4; i4 < end_4; i4++)
-              for (ptrdiff_t i5 = begin_5; i5 < end_5; i5++)
-                for (ptrdiff_t i6 = begin_6; i6 < end_6; i6++)
-                  for (ptrdiff_t i7 = begin_7; i7 < end_7; i7++) {
-                    if constexpr (std::is_same<typename Policy::work_tag,
-                                               void>::value)
-                      functor(i0, i1, i2, i3, i4, i5, i6, i7);
-                    else
-                      functor(typename Policy::work_tag(), i0, i1, i2, i3, i4,
-                              i5, i6, i7);
-                  }
-#endif
-  }
-
   inline ParallelFor(const FunctorType& arg_functor, Policy arg_policy)
       : m_functor(arg_functor), m_policy(arg_policy) {}
   // TODO DZP: based on a conversation with Christian, we're using 256 as a
@@ -652,112 +438,6 @@ class ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
 namespace Kokkos {
 namespace Impl {
 
-template <class FunctorType, class ReducerType, class PointerType,
-          class ValueType, class... PolicyArgs>
-struct ParallelReduceSpecialize<FunctorType,
-                                Kokkos::MDRangePolicy<PolicyArgs...>,
-                                ReducerType, PointerType, ValueType, 0, 0> {
-  using PolicyType = Kokkos::RangePolicy<PolicyArgs...>;
-  template <class TagType>
-  inline static
-      typename std::enable_if<std::is_same<TagType, void>::value>::type
-      execute_impl(const FunctorType& f, const PolicyType& p,
-                   PointerType result_ptr) {
-    OpenMPTargetExec::verify_is_process(
-        "Kokkos::Experimental::OpenMPTarget parallel_for");
-    OpenMPTargetExec::verify_initialized(
-        "Kokkos::Experimental::OpenMPTarget parallel_for");
-    const typename PolicyType::member_type begin = p.begin();
-    const typename PolicyType::member_type end   = p.end();
-
-    ValueType result = ValueType();
-#pragma omp target teams distribute parallel for num_teams(512) map(to:f) map(tofrom:result) reduction(+: result)
-    for (int i = begin; i < end; i++) f(i, result);
-
-    *result_ptr = result;
-  }
-
-  template <class TagType>
-  inline static
-      typename std::enable_if<!std::is_same<TagType, void>::value>::type
-      execute_impl(const FunctorType& f, const PolicyType& p,
-                   PointerType result_ptr) {
-    OpenMPTargetExec::verify_is_process(
-        "Kokkos::Experimental::OpenMPTarget parallel_for");
-    OpenMPTargetExec::verify_initialized(
-        "Kokkos::Experimental::OpenMPTarget parallel_for");
-    const typename PolicyType::member_type begin = p.begin();
-    const typename PolicyType::member_type end   = p.end();
-
-    ValueType result = ValueType();
-#pragma omp target teams distribute parallel for num_teams(512) map(to:f) map(tofrom: result) reduction(+: result)
-    for (int i = begin; i < end; i++) f(TagType(), i, result);
-
-    *result_ptr = result;
-  }
-
-  inline static void execute(const FunctorType& f, const PolicyType& p,
-                             PointerType ptr) {
-    execute_impl<typename PolicyType::work_tag>(f, p, ptr);
-  }
-};
-/*
-template<class FunctorType, class PolicyType, class ReducerType, class
-PointerType, class ValueType> struct ParallelReduceSpecialize<FunctorType,
-PolicyType, ReducerType, PointerType, ValueType, 0,1> {
-
-  #pragma omp declare reduction(custom: ValueType : ReducerType::join(omp_out,
-omp_in)) initializer ( ReducerType::init(omp_priv) )
-
-  template< class TagType >
-  inline static
-  typename std::enable_if< std::is_same< TagType , void >::value >::type
-  execute_impl(const FunctorType& f, const PolicyType& p, PointerType
-result_ptr)
-    {
-      OpenMPTargetExec::verify_is_process("Kokkos::Experimental::OpenMPTarget
-parallel_for");
-      OpenMPTargetExec::verify_initialized("Kokkos::Experimental::OpenMPTarget
-parallel_for"); const typename PolicyType::member_type begin = p.begin(); const
-typename PolicyType::member_type end = p.end();
-
-      ValueType result = ValueType();
-      #pragma omp target teams distribute parallel for num_teams(512) map(to:f)
-map(tofrom:result) reduction(custom: result) for(int i=begin; i<end; i++)
-        f(i,result);
-
-      *result_ptr=result;
-    }
-
-
-  template< class TagType >
-  inline static
-  typename std::enable_if< ! std::is_same< TagType , void >::value >::type
-  execute_impl(const FunctorType& f, const PolicyType& p, PointerType
-result_ptr)
-    {
-      OpenMPTargetExec::verify_is_process("Kokkos::Experimental::OpenMPTarget
-parallel_for");
-      OpenMPTargetExec::verify_initialized("Kokkos::Experimental::OpenMPTarget
-parallel_for"); const typename PolicyType::member_type begin = p.begin(); const
-typename PolicyType::member_type end = p.end();
-
-      ValueType result = ValueType();
-      #pragma omp target teams distribute parallel for num_teams(512) map(to:f)
-map(tofrom: result) reduction(custom: result) for(int i=begin; i<end; i++)
-        f(TagType(),i,result);
-
-      *result_ptr=result;
-    }
-
-
-    inline static
-    void execute(const FunctorType& f, const PolicyType& p, PointerType ptr) {
-      execute_impl<typename PolicyType::work_tag>(f,p,ptr);
-    }
-};
-
-
 template <class FunctorType, class ReducerType, class... Traits>
 class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
                      Kokkos::Experimental::OpenMPTarget> {
@@ -765,42 +445,38 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
   using Policy = Kokkos::MDRangePolicy<Traits...>;
 
   using WorkTag = typename Policy::work_tag;
-  using WorkRange = typename Policy::WorkRange;
-  using Member = typename Policy::member_type;
+  using Member  = typename Policy::member_type;
 
   using ReducerConditional =
-      Kokkos::Impl::if_c<std::is_same<InvalidType, ReducerType>::value,
-                         FunctorType, ReducerType>;
+      std::conditional<std::is_same<InvalidType, ReducerType>::value,
+                       FunctorType, ReducerType>;
   using ReducerTypeFwd = typename ReducerConditional::type;
   using WorkTagFwd =
-      typename Kokkos::Impl::if_c<std::is_same<InvalidType, ReducerType>::value,
-                                               WorkTag, void>::type;
-
-  // Static Assert WorkTag void if ReducerType not InvalidType
+      std::conditional_t<std::is_same<InvalidType, ReducerType>::value, WorkTag,
+                         void>;
 
   using ValueTraits =
       Kokkos::Impl::FunctorValueTraits<ReducerTypeFwd, WorkTagFwd>;
-  using ValueInit = Kokkos::Impl::FunctorValueInit<ReducerTypeFwd, WorkTagFwd>;
-  using ValueJoin = Kokkos::Impl::FunctorValueJoin<ReducerTypeFwd, WorkTagFwd>;
-
-  enum { HasJoin = ReduceFunctorHasJoin<FunctorType>::value };
-  enum { UseReducer = is_reducer_type<ReducerType>::value };
 
-  using pointer_type = typename ValueTraits::pointer_type;
+  using pointer_type   = typename ValueTraits::pointer_type;
   using reference_type = typename ValueTraits::reference_type;
 
-  using ParForSpecialize = ParallelReduceSpecialize<
-      FunctorType, Policy, ReducerType, pointer_type,
-      typename ValueTraits::value_type, HasJoin, UseReducer>;
+  enum { HasJoin = ReduceFunctorHasJoin<FunctorType>::value };
+  enum { UseReducer = is_reducer_type<ReducerType>::value };
 
+  const pointer_type m_result_ptr;
   const FunctorType m_functor;
   const Policy m_policy;
   const ReducerType m_reducer;
-  const pointer_type m_result_ptr;
+
+  using ParReduceCommon = ParallelReduceCommon<pointer_type>;
+
+  bool m_result_ptr_on_device;
 
  public:
   inline void execute() const {
-    ParForSpecialize::execute(m_functor, m_policy, m_result_ptr);
+    execute_tile<Policy::rank, typename ValueTraits::value_type>(
+        m_functor, m_policy, m_result_ptr);
   }
 
   template <class ViewType>
@@ -810,35 +486,345 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
       typename std::enable_if<Kokkos::is_view<ViewType>::value &&
                                   !Kokkos::is_reducer_type<ReducerType>::value,
                               void*>::type = NULL)
-      : m_functor(arg_functor),
+      : m_result_ptr(arg_result_view.data()),
+        m_functor(arg_functor),
         m_policy(arg_policy),
         m_reducer(InvalidType()),
-        m_result_ptr(arg_result_view.data()) {
-    //static_assert( std::is_same< typename ViewType::memory_space
-    //                                , Kokkos::HostSpace >::value
-    //  , "Reduction result on Kokkos::Experimental::OpenMPTarget must be a
-    //  Kokkos::View in HostSpace" );
-  }
+        m_result_ptr_on_device(
+            MemorySpaceAccess<Kokkos::Experimental::OpenMPTargetSpace,
+                              typename ViewType::memory_space>::accessible) {}
 
   inline ParallelReduce(const FunctorType& arg_functor, Policy arg_policy,
                         const ReducerType& reducer)
-      : m_functor(arg_functor),
+      : m_result_ptr(reducer.view().data()),
+        m_functor(arg_functor),
         m_policy(arg_policy),
         m_reducer(reducer),
-        m_result_ptr(reducer.view().data()) {
-    //static_assert( std::is_same< typename ViewType::memory_space
-    //                                , Kokkos::HostSpace >::value
-    //  , "Reduction result on Kokkos::Experimental::OpenMPTarget must be a
-    //  Kokkos::View in HostSpace" );
+        m_result_ptr_on_device(
+            MemorySpaceAccess<Kokkos::Experimental::OpenMPTargetSpace,
+                              typename ReducerType::result_view_type::
+                                  memory_space>::accessible) {}
+
+  template <int Rank, class ValueType>
+  inline typename std::enable_if<Rank == 2>::type execute_tile(
+      const FunctorType& functor, const Policy& policy,
+      pointer_type ptr) const {
+    const auto begin_0 = policy.m_lower[0];
+    const auto begin_1 = policy.m_lower[1];
+
+    const auto end_0 = policy.m_upper[0];
+    const auto end_1 = policy.m_upper[1];
+
+    ValueType result = ValueType();
+
+    // FIXME_OPENMPTARGET: Unable to separate directives and their companion
+    // loops which leads to code duplication for different reduction types.
+    if constexpr (UseReducer) {
+#pragma omp declare reduction(                                         \
+    custom:ValueType                                                   \
+    : OpenMPTargetReducerWrapper <ReducerType>::join(omp_out, omp_in)) \
+    initializer(OpenMPTargetReducerWrapper <ReducerType>::init(omp_priv))
+
+#pragma omp target teams distribute parallel for collapse(2) map(to         \
+                                                                 : functor) \
+    reduction(custom                                                        \
+              : result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          if constexpr (std::is_same<typename Policy::work_tag, void>::value)
+            functor(i0, i1, result);
+          else
+            functor(typename Policy::work_tag(), i0, i1, result);
+        }
+      }
+    } else {
+#pragma omp target teams distribute parallel for collapse(2) map(to : functor) \
+reduction(+:result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          if constexpr (std::is_same<typename Policy::work_tag, void>::value)
+            functor(i0, i1, result);
+          else
+            functor(typename Policy::work_tag(), i0, i1, result);
+        }
+      }
+    }
+
+    ParReduceCommon::memcpy_result(ptr, &result, sizeof(ValueType),
+                                   m_result_ptr_on_device);
   }
-  // TODO DZP: based on a conversation with Christian, we're using 256 as a
-heuristic
-  // here. We need something better once we can query these kinds of properties
-  template<typename Policy, typename Functor>
-static int max_tile_size_product(const Policy&, const Functor&) {
+
+  template <int Rank, class ValueType>
+  inline typename std::enable_if<Rank == 3>::type execute_tile(
+      const FunctorType& functor, const Policy& policy,
+      pointer_type ptr) const {
+    const auto begin_0 = policy.m_lower[0];
+    const auto begin_1 = policy.m_lower[1];
+    const auto begin_2 = policy.m_lower[2];
+
+    const auto end_0 = policy.m_upper[0];
+    const auto end_1 = policy.m_upper[1];
+    const auto end_2 = policy.m_upper[2];
+
+    ValueType result = ValueType();
+
+    // FIXME_OPENMPTARGET: Unable to separate directives and their companion
+    // loops which leads to code duplication for different reduction types.
+    if constexpr (UseReducer) {
+#pragma omp declare reduction(                                         \
+    custom:ValueType                                                   \
+    : OpenMPTargetReducerWrapper <ReducerType>::join(omp_out, omp_in)) \
+    initializer(OpenMPTargetReducerWrapper <ReducerType>::init(omp_priv))
+
+#pragma omp target teams distribute parallel for collapse(3) map(to         \
+                                                                 : functor) \
+    reduction(custom                                                        \
+              : result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          for (auto i2 = begin_2; i2 < end_2; ++i2) {
+            if constexpr (std::is_same<typename Policy::work_tag, void>::value)
+              functor(i0, i1, i2, result);
+            else
+              functor(typename Policy::work_tag(), i0, i1, i2, result);
+          }
+        }
+      }
+    } else {
+#pragma omp target teams distribute parallel for collapse(3) map(to : functor) \
+reduction(+:result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          for (auto i2 = begin_2; i2 < end_2; ++i2) {
+            if constexpr (std::is_same<typename Policy::work_tag, void>::value)
+              functor(i0, i1, i2, result);
+            else
+              functor(typename Policy::work_tag(), i0, i1, i2, result);
+          }
+        }
+      }
+    }
+
+    ParReduceCommon::memcpy_result(ptr, &result, sizeof(ValueType),
+                                   m_result_ptr_on_device);
+  }
+
+  template <int Rank, class ValueType>
+  inline typename std::enable_if<Rank == 4>::type execute_tile(
+      const FunctorType& functor, const Policy& policy,
+      pointer_type ptr) const {
+    const auto begin_0 = policy.m_lower[0];
+    const auto begin_1 = policy.m_lower[1];
+    const auto begin_2 = policy.m_lower[3];
+    const auto begin_3 = policy.m_lower[2];
+
+    const auto end_0 = policy.m_upper[0];
+    const auto end_1 = policy.m_upper[1];
+    const auto end_2 = policy.m_upper[2];
+    const auto end_3 = policy.m_upper[3];
+
+    ValueType result = ValueType();
+
+    // FIXME_OPENMPTARGET: Unable to separate directives and their companion
+    // loops which leads to code duplication for different reduction types.
+    if constexpr (UseReducer) {
+#pragma omp declare reduction(                                         \
+    custom:ValueType                                                   \
+    : OpenMPTargetReducerWrapper <ReducerType>::join(omp_out, omp_in)) \
+    initializer(OpenMPTargetReducerWrapper <ReducerType>::init(omp_priv))
+
+#pragma omp target teams distribute parallel for collapse(4) map(to         \
+                                                                 : functor) \
+    reduction(custom                                                        \
+              : result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          for (auto i2 = begin_2; i2 < end_2; ++i2) {
+            for (auto i3 = begin_3; i3 < end_3; ++i3) {
+              if constexpr (std::is_same<typename Policy::work_tag,
+                                         void>::value)
+                functor(i0, i1, i2, i3, result);
+              else
+                functor(typename Policy::work_tag(), i0, i1, i2, i3, result);
+            }
+          }
+        }
+      }
+    } else {
+#pragma omp target teams distribute parallel for collapse(4) map(to : functor) \
+reduction(+:result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          for (auto i2 = begin_2; i2 < end_2; ++i2) {
+            for (auto i3 = begin_3; i3 < end_3; ++i3) {
+              if constexpr (std::is_same<typename Policy::work_tag,
+                                         void>::value)
+                functor(i0, i1, i2, i3, result);
+              else
+                functor(typename Policy::work_tag(), i0, i1, i2, i3, result);
+            }
+          }
+        }
+      }
+    }
+
+    ParReduceCommon::memcpy_result(ptr, &result, sizeof(ValueType),
+                                   m_result_ptr_on_device);
+  }
+
+  template <int Rank, class ValueType>
+  inline typename std::enable_if<Rank == 5>::type execute_tile(
+      const FunctorType& functor, const Policy& policy,
+      pointer_type ptr) const {
+    const auto begin_0 = policy.m_lower[0];
+    const auto begin_1 = policy.m_lower[1];
+    const auto begin_2 = policy.m_lower[2];
+    const auto begin_3 = policy.m_lower[3];
+    const auto begin_4 = policy.m_lower[4];
+
+    const auto end_0 = policy.m_upper[0];
+    const auto end_1 = policy.m_upper[1];
+    const auto end_2 = policy.m_upper[2];
+    const auto end_3 = policy.m_upper[3];
+    const auto end_4 = policy.m_upper[4];
+
+    ValueType result = ValueType();
+
+    // FIXME_OPENMPTARGET: Unable to separate directives and their companion
+    // loops which leads to code duplication for different reduction types.
+    if constexpr (UseReducer) {
+#pragma omp declare reduction(                                         \
+    custom:ValueType                                                   \
+    : OpenMPTargetReducerWrapper <ReducerType>::join(omp_out, omp_in)) \
+    initializer(OpenMPTargetReducerWrapper <ReducerType>::init(omp_priv))
+
+#pragma omp target teams distribute parallel for collapse(5) map(to         \
+                                                                 : functor) \
+    reduction(custom                                                        \
+              : result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          for (auto i2 = begin_2; i2 < end_2; ++i2) {
+            for (auto i3 = begin_3; i3 < end_3; ++i3) {
+              for (auto i4 = begin_4; i4 < end_4; ++i4) {
+                if constexpr (std::is_same<typename Policy::work_tag,
+                                           void>::value)
+                  functor(i0, i1, i2, i3, i4, result);
+                else
+                  functor(typename Policy::work_tag(), i0, i1, i2, i3, i4,
+                          result);
+              }
+            }
+          }
+        }
+      }
+    } else {
+#pragma omp target teams distribute parallel for collapse(5) map(to : functor) \
+reduction(+:result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          for (auto i2 = begin_2; i2 < end_2; ++i2) {
+            for (auto i3 = begin_3; i3 < end_3; ++i3) {
+              for (auto i4 = begin_4; i4 < end_4; ++i4) {
+                if constexpr (std::is_same<typename Policy::work_tag,
+                                           void>::value)
+                  functor(i0, i1, i2, i3, i4, result);
+                else
+                  functor(typename Policy::work_tag(), i0, i1, i2, i3, i4,
+                          result);
+              }
+            }
+          }
+        }
+      }
+    }
+
+    ParReduceCommon::memcpy_result(ptr, &result, sizeof(ValueType),
+                                   m_result_ptr_on_device);
+  }
+
+  template <int Rank, class ValueType>
+  inline typename std::enable_if<Rank == 6>::type execute_tile(
+      const FunctorType& functor, const Policy& policy,
+      pointer_type ptr) const {
+    const auto begin_0 = policy.m_lower[0];
+    const auto begin_1 = policy.m_lower[1];
+    const auto begin_2 = policy.m_lower[2];
+    const auto begin_3 = policy.m_lower[3];
+    const auto begin_4 = policy.m_lower[4];
+    const auto begin_5 = policy.m_lower[5];
+
+    const auto end_0 = policy.m_upper[0];
+    const auto end_1 = policy.m_upper[1];
+    const auto end_2 = policy.m_upper[2];
+    const auto end_3 = policy.m_upper[3];
+    const auto end_4 = policy.m_upper[4];
+    const auto end_5 = policy.m_upper[5];
+
+    ValueType result = ValueType();
+
+    // FIXME_OPENMPTARGET: Unable to separate directives and their companion
+    // loops which leads to code duplication for different reduction types.
+    if constexpr (UseReducer) {
+#pragma omp declare reduction(                                         \
+    custom:ValueType                                                   \
+    : OpenMPTargetReducerWrapper <ReducerType>::join(omp_out, omp_in)) \
+    initializer(OpenMPTargetReducerWrapper <ReducerType>::init(omp_priv))
+
+#pragma omp target teams distribute parallel for collapse(6) map(to         \
+                                                                 : functor) \
+    reduction(custom                                                        \
+              : result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          for (auto i2 = begin_2; i2 < end_2; ++i2) {
+            for (auto i3 = begin_3; i3 < end_3; ++i3) {
+              for (auto i4 = begin_4; i4 < end_4; ++i4) {
+                for (auto i5 = begin_5; i5 < end_5; ++i5) {
+                  if constexpr (std::is_same<typename Policy::work_tag,
+                                             void>::value)
+                    functor(i0, i1, i2, i3, i4, i5, result);
+                  else
+                    functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, i5,
+                            result);
+                }
+              }
+            }
+          }
+        }
+      }
+    } else {
+#pragma omp target teams distribute parallel for collapse(6) map(to : functor) \
+reduction(+:result)
+      for (auto i0 = begin_0; i0 < end_0; ++i0) {
+        for (auto i1 = begin_1; i1 < end_1; ++i1) {
+          for (auto i2 = begin_2; i2 < end_2; ++i2) {
+            for (auto i3 = begin_3; i3 < end_3; ++i3) {
+              for (auto i4 = begin_4; i4 < end_4; ++i4) {
+                for (auto i5 = begin_5; i5 < end_5; ++i5) {
+                  if constexpr (std::is_same<typename Policy::work_tag,
+                                             void>::value)
+                    functor(i0, i1, i2, i3, i4, i5, result);
+                  else
+                    functor(typename Policy::work_tag(), i0, i1, i2, i3, i4, i5,
+                            result);
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    ParReduceCommon::memcpy_result(ptr, &result, sizeof(ValueType),
+                                   m_result_ptr_on_device);
+  }
+
+  template <typename Policy, typename Functor>
+  static int max_tile_size_product(const Policy&, const Functor&) {
     return 256;
   }
-};*/
+};
 
 }  // namespace Impl
 }  // namespace Kokkos
diff --git a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Task.cpp b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Task.cpp
index be924ffa61c1f8cf696b3b84cb44765536fde4f9..0e71a239caf343d77f6ed05ff02bb2e45ca64efd 100644
--- a/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Task.cpp
+++ b/packages/kokkos/core/src/OpenMPTarget/Kokkos_OpenMPTarget_Task.cpp
@@ -112,35 +112,11 @@ void TaskExec<Kokkos::Experimental::OpenMPTarget>::team_barrier_impl() const {
   // This team member sets one byte within the sync variable
   int8_t volatile *const sync_self = ((int8_t *)sync) + m_team_rank;
 
-#if 0
-fprintf( stdout
-       , "barrier group(%d) member(%d) step(%d) wait(%lx) : before(%lx)\n"
-       , m_group_rank
-       , m_team_rank
-       , m_sync_step
-       , m_sync_value
-       , *sync
-       );
-fflush(stdout);
-#endif
-
   *sync_self = int8_t(m_sync_value & 0x03);  // signal arrival
 
   while (m_sync_value != *sync)
     ;  // wait for team to arrive
 
-#if 0
-fprintf( stdout
-       , "barrier group(%d) member(%d) step(%d) wait(%lx) : after(%lx)\n"
-       , m_group_rank
-       , m_team_rank
-       , m_sync_step
-       , m_sync_value
-       , *sync
-       );
-fflush(stdout);
-#endif
-
   ++m_sync_step;
 
   if (0 == (0x01 & m_sync_step)) {  // Every other step
@@ -222,17 +198,6 @@ void TaskQueueSpecialization<Kokkos::Experimental::OpenMPTarget>::execute(
         task = *task_shared;
       }
 
-#if 0
-fprintf( stdout
-       , "\nexecute group(%d) member(%d) task_shared(0x%lx) task(0x%lx)\n"
-       , team_exec.m_group_rank
-       , team_exec.m_team_rank
-       , uintptr_t(task_shared)
-       , uintptr_t(task)
-       );
-fflush(stdout);
-#endif
-
       if (0 == task) break;  // 0 == m_ready_count
 
       if (end == task) {
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL.cpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL.cpp
index 9c29eb190d17b64c0340751a3459785c070d7c47..18d33317a29819274037085b6a69e9239797f1a8 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL.cpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL.cpp
@@ -105,15 +105,43 @@ bool SYCL::impl_is_initialized() {
 
 void SYCL::impl_finalize() { Impl::SYCLInternal::singleton().finalize(); }
 
+void SYCL::print_configuration(std::ostream& s, const bool detailed) {
+  s << "macro  KOKKOS_ENABLE_SYCL : defined" << '\n';
+  if (detailed)
+    SYCL::impl_sycl_info(s, m_space_instance->m_queue->get_device());
+}
+
 void SYCL::fence() const {
-  Impl::SYCLInternal::fence(*m_space_instance->m_queue);
+  fence("Kokkos::Experimental::SYCL::fence: Unnamed Instance Fence");
+}
+void SYCL::fence(const std::string& name) const {
+  Impl::SYCLInternal::fence(*m_space_instance->m_queue, name,
+                            impl_instance_id());
 }
 
 void SYCL::impl_static_fence() {
-  // guard accessing all_queues
-  std::lock_guard<std::mutex> lock(Impl::SYCLInternal::mutex);
-  for (auto& queue : Impl::SYCLInternal::all_queues)
-    Impl::SYCLInternal::fence(**queue);
+  impl_static_fence(
+      "Kokkos::Experimental::SYCL::fence: Unnamed Instance Fence");
+}
+void SYCL::impl_static_fence(const std::string& name) {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<
+      Kokkos::Experimental::SYCL>(
+      name,
+      Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+          GlobalDeviceSynchronization,
+      [&]() {
+        // guard accessing all_queues
+        std::lock_guard<std::mutex> lock(Impl::SYCLInternal::mutex);
+        for (auto& queue : Impl::SYCLInternal::all_queues) {
+          try {
+            (*queue)->wait_and_throw();
+          } catch (sycl::exception const& e) {
+            Kokkos::Impl::throw_runtime_exception(
+                std::string("There was a synchronous SYCL error:\n") +=
+                e.what());
+          }
+        }
+      });
 }
 
 int SYCL::sycl_device() const {
@@ -143,119 +171,114 @@ void SYCL::impl_initialize(SYCL::SYCLDevice d) {
   Impl::SYCLInternal::singleton().initialize(d.get_device());
 }
 
-std::ostream& SYCL::SYCLDevice::info(std::ostream& os) const {
+std::ostream& SYCL::impl_sycl_info(std::ostream& os,
+                                   const sycl::device& device) {
   using namespace sycl::info;
-  return os << "Name: " << m_device.get_info<device::name>()
-            << "\nDriver Version: "
-            << m_device.get_info<device::driver_version>()
-            << "\nIs Host: " << m_device.is_host()
-            << "\nIs CPU: " << m_device.is_cpu()
-            << "\nIs GPU: " << m_device.is_gpu()
-            << "\nIs Accelerator: " << m_device.is_accelerator()
-            << "\nVendor Id: " << m_device.get_info<device::vendor_id>()
+  return os << "Name: " << device.get_info<device::name>()
+            << "\nDriver Version: " << device.get_info<device::driver_version>()
+            << "\nIs Host: " << device.is_host()
+            << "\nIs CPU: " << device.is_cpu()
+            << "\nIs GPU: " << device.is_gpu()
+            << "\nIs Accelerator: " << device.is_accelerator()
+            << "\nVendor Id: " << device.get_info<device::vendor_id>()
             << "\nMax Compute Units: "
-            << m_device.get_info<device::max_compute_units>()
+            << device.get_info<device::max_compute_units>()
             << "\nMax Work Item Dimensions: "
-            << m_device.get_info<device::max_work_item_dimensions>()
+            << device.get_info<device::max_work_item_dimensions>()
             << "\nMax Work Group Size: "
-            << m_device.get_info<device::max_work_group_size>()
+            << device.get_info<device::max_work_group_size>()
             << "\nPreferred Vector Width Char: "
-            << m_device.get_info<device::preferred_vector_width_char>()
+            << device.get_info<device::preferred_vector_width_char>()
             << "\nPreferred Vector Width Short: "
-            << m_device.get_info<device::preferred_vector_width_short>()
+            << device.get_info<device::preferred_vector_width_short>()
             << "\nPreferred Vector Width Int: "
-            << m_device.get_info<device::preferred_vector_width_int>()
+            << device.get_info<device::preferred_vector_width_int>()
             << "\nPreferred Vector Width Long: "
-            << m_device.get_info<device::preferred_vector_width_long>()
+            << device.get_info<device::preferred_vector_width_long>()
             << "\nPreferred Vector Width Float: "
-            << m_device.get_info<device::preferred_vector_width_float>()
+            << device.get_info<device::preferred_vector_width_float>()
             << "\nPreferred Vector Width Double: "
-            << m_device.get_info<device::preferred_vector_width_double>()
+            << device.get_info<device::preferred_vector_width_double>()
             << "\nPreferred Vector Width Half: "
-            << m_device.get_info<device::preferred_vector_width_half>()
+            << device.get_info<device::preferred_vector_width_half>()
             << "\nNative Vector Width Char: "
-            << m_device.get_info<device::native_vector_width_char>()
+            << device.get_info<device::native_vector_width_char>()
             << "\nNative Vector Width Short: "
-            << m_device.get_info<device::native_vector_width_short>()
+            << device.get_info<device::native_vector_width_short>()
             << "\nNative Vector Width Int: "
-            << m_device.get_info<device::native_vector_width_int>()
+            << device.get_info<device::native_vector_width_int>()
             << "\nNative Vector Width Long: "
-            << m_device.get_info<device::native_vector_width_long>()
+            << device.get_info<device::native_vector_width_long>()
             << "\nNative Vector Width Float: "
-            << m_device.get_info<device::native_vector_width_float>()
+            << device.get_info<device::native_vector_width_float>()
             << "\nNative Vector Width Double: "
-            << m_device.get_info<device::native_vector_width_double>()
+            << device.get_info<device::native_vector_width_double>()
             << "\nNative Vector Width Half: "
-            << m_device.get_info<device::native_vector_width_half>()
-            << "\nAddress Bits: " << m_device.get_info<device::address_bits>()
-            << "\nImage Support: " << m_device.get_info<device::image_support>()
+            << device.get_info<device::native_vector_width_half>()
+            << "\nAddress Bits: " << device.get_info<device::address_bits>()
+            << "\nImage Support: " << device.get_info<device::image_support>()
             << "\nMax Mem Alloc Size: "
-            << m_device.get_info<device::max_mem_alloc_size>()
+            << device.get_info<device::max_mem_alloc_size>()
             << "\nMax Read Image Args: "
-            << m_device.get_info<device::max_read_image_args>()
+            << device.get_info<device::max_read_image_args>()
             << "\nImage2d Max Width: "
-            << m_device.get_info<device::image2d_max_width>()
+            << device.get_info<device::image2d_max_width>()
             << "\nImage2d Max Height: "
-            << m_device.get_info<device::image2d_max_height>()
+            << device.get_info<device::image2d_max_height>()
             << "\nImage3d Max Width: "
-            << m_device.get_info<device::image3d_max_width>()
+            << device.get_info<device::image3d_max_width>()
             << "\nImage3d Max Height: "
-            << m_device.get_info<device::image3d_max_height>()
+            << device.get_info<device::image3d_max_height>()
             << "\nImage3d Max Depth: "
-            << m_device.get_info<device::image3d_max_depth>()
+            << device.get_info<device::image3d_max_depth>()
             << "\nImage Max Buffer Size: "
-            << m_device.get_info<device::image_max_buffer_size>()
+            << device.get_info<device::image_max_buffer_size>()
             << "\nImage Max Array Size: "
-            << m_device.get_info<device::image_max_array_size>()
-            << "\nMax Samplers: " << m_device.get_info<device::max_samplers>()
+            << device.get_info<device::image_max_array_size>()
+            << "\nMax Samplers: " << device.get_info<device::max_samplers>()
             << "\nMax Parameter Size: "
-            << m_device.get_info<device::max_parameter_size>()
+            << device.get_info<device::max_parameter_size>()
             << "\nMem Base Addr Align: "
-            << m_device.get_info<device::mem_base_addr_align>()
+            << device.get_info<device::mem_base_addr_align>()
             << "\nGlobal Cache Mem Line Size: "
-            << m_device.get_info<device::global_mem_cache_line_size>()
+            << device.get_info<device::global_mem_cache_line_size>()
             << "\nGlobal Mem Cache Size: "
-            << m_device.get_info<device::global_mem_cache_size>()
+            << device.get_info<device::global_mem_cache_size>()
             << "\nGlobal Mem Size: "
-            << m_device.get_info<device::global_mem_size>()
-            << "\nMax Constant Buffer Size: "
-            << m_device.get_info<device::max_constant_buffer_size>()
-            << "\nMax Constant Args: "
-            << m_device.get_info<device::max_constant_args>()
-            << "\nLocal Mem Size: "
-            << m_device.get_info<device::local_mem_size>()
+            << device.get_info<device::global_mem_size>()
+            << "\nLocal Mem Size: " << device.get_info<device::local_mem_size>()
             << "\nError Correction Support: "
-            << m_device.get_info<device::error_correction_support>()
+            << device.get_info<device::error_correction_support>()
             << "\nHost Unified Memory: "
-            << m_device.get_info<device::host_unified_memory>()
+            << device.get_info<device::host_unified_memory>()
             << "\nProfiling Timer Resolution: "
-            << m_device.get_info<device::profiling_timer_resolution>()
+            << device.get_info<device::profiling_timer_resolution>()
             << "\nIs Endian Little: "
-            << m_device.get_info<device::is_endian_little>()
-            << "\nIs Available: " << m_device.get_info<device::is_available>()
+            << device.get_info<device::is_endian_little>()
+            << "\nIs Available: " << device.get_info<device::is_available>()
             << "\nIs Compiler Available: "
-            << m_device.get_info<device::is_compiler_available>()
+            << device.get_info<device::is_compiler_available>()
             << "\nIs Linker Available: "
-            << m_device.get_info<device::is_linker_available>()
+            << device.get_info<device::is_linker_available>()
             << "\nQueue Profiling: "
-            << m_device.get_info<device::queue_profiling>()
+            << device.get_info<device::queue_profiling>()
             << "\nBuilt In Kernels: "
             << Container<std::vector<std::string>>(
-                   m_device.get_info<device::built_in_kernels>())
-            << "\nVendor: " << m_device.get_info<device::vendor>()
-            << "\nProfile: " << m_device.get_info<device::profile>()
-            << "\nVersion: " << m_device.get_info<device::version>()
+                   device.get_info<device::built_in_kernels>())
+            << "\nVendor: " << device.get_info<device::vendor>()
+            << "\nProfile: " << device.get_info<device::profile>()
+            << "\nVersion: " << device.get_info<device::version>()
             << "\nExtensions: "
             << Container<std::vector<std::string>>(
-                   m_device.get_info<device::extensions>())
+                   device.get_info<device::extensions>())
             << "\nPrintf Buffer Size: "
-            << m_device.get_info<device::printf_buffer_size>()
+            << device.get_info<device::printf_buffer_size>()
             << "\nPreferred Interop User Sync: "
-            << m_device.get_info<device::preferred_interop_user_sync>()
+            << device.get_info<device::preferred_interop_user_sync>()
             << "\nPartition Max Sub Devices: "
-            << m_device.get_info<device::partition_max_sub_devices>()
+            << device.get_info<device::partition_max_sub_devices>()
             << "\nReference Count: "
-            << m_device.get_info<device::reference_count>() << '\n';
+            << device.get_info<device::reference_count>() << '\n';
 }
 
 namespace Impl {
@@ -291,17 +314,18 @@ void SYCLSpaceInitializer::finalize(const bool all_spaces) {
 void SYCLSpaceInitializer::fence() {
   Kokkos::Experimental::SYCL::impl_static_fence();
 }
+void SYCLSpaceInitializer::fence(const std::string& name) {
+  Kokkos::Experimental::SYCL::impl_static_fence(name);
+}
 
 void SYCLSpaceInitializer::print_configuration(std::ostream& msg,
-                                               const bool /*detail*/) {
+                                               const bool detail) {
   msg << "Devices:" << std::endl;
   msg << "  KOKKOS_ENABLE_SYCL: ";
   msg << "yes" << std::endl;
 
   msg << "\nRuntime Configuration:" << std::endl;
-  // FIXME_SYCL not implemented
-  std::abort();
-  // Experimental::SYCL::print_configuration(msg, detail);
+  Experimental::SYCL{}.print_configuration(msg, detail);
 }
 
 }  // namespace Impl
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_DeepCopy.hpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_DeepCopy.hpp
index aef65ee7ecbbf3c39432b42a42b595dbfe00b239..3eeab5636342031955920c81e807b218d662f3b8 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_DeepCopy.hpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_DeepCopy.hpp
@@ -48,181 +48,144 @@
 #include <Kokkos_Core_fwd.hpp>
 #include <Kokkos_SYCL.hpp>
 
+#include <vector>
+
 #ifdef KOKKOS_ENABLE_SYCL
 
 namespace Kokkos {
 namespace Impl {
 
-template <>
-struct DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
-                Kokkos::Experimental::SYCLDeviceUSMSpace,
-                Kokkos::Experimental::SYCL> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Kokkos::Experimental::SYCL&, void* dst, const void* src,
-           size_t);
-};
-
-template <>
-struct DeepCopy<Kokkos::HostSpace, Kokkos::Experimental::SYCLDeviceUSMSpace,
-                Kokkos::Experimental::SYCL> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Kokkos::Experimental::SYCL&, void* dst, const void* src,
-           size_t);
-};
-
-template <>
-struct DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace, Kokkos::HostSpace,
-                Kokkos::Experimental::SYCL> {
-  DeepCopy(void* dst, const void* src, size_t);
-  DeepCopy(const Kokkos::Experimental::SYCL&, void* dst, const void* src,
-           size_t);
-};
-
-template <class ExecutionSpace>
-struct DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
-                Kokkos::Experimental::SYCLDeviceUSMSpace, ExecutionSpace> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
-                   Kokkos::Experimental::SYCLDeviceUSMSpace,
-                   Kokkos::Experimental::SYCL>(dst, src, n);
+template <class DT, class... DP>
+struct ZeroMemset<Kokkos::Experimental::SYCL, DT, DP...> {
+  ZeroMemset(const Kokkos::Experimental::SYCL& exec_space,
+             const View<DT, DP...>& dst,
+             typename View<DT, DP...>::const_value_type&) {
+    auto event = exec_space.impl_internal_space_instance()->m_queue->memset(
+        dst.data(), 0,
+        dst.size() * sizeof(typename View<DT, DP...>::value_type));
+    exec_space.impl_internal_space_instance()->m_queue->submit_barrier(
+        std::vector<sycl::event>{event});
   }
 
-  DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
-    DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
-             Kokkos::Experimental::SYCLDeviceUSMSpace,
-             Kokkos::Experimental::SYCL>(Kokkos::Experimental::SYCL(), dst, src,
-                                         n);
-    Kokkos::Experimental::SYCL().fence();
+  ZeroMemset(const View<DT, DP...>& dst,
+             typename View<DT, DP...>::const_value_type&) {
+    Experimental::Impl::SYCLInternal::singleton().m_queue->memset(
+        dst.data(), 0,
+        dst.size() * sizeof(typename View<DT, DP...>::value_type));
   }
 };
 
-template <class ExecutionSpace>
-struct DeepCopy<Kokkos::HostSpace, Kokkos::Experimental::SYCLDeviceUSMSpace,
-                ExecutionSpace> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<Kokkos::HostSpace, Kokkos::Experimental::SYCLDeviceUSMSpace,
-                   Kokkos::Experimental::SYCL>(dst, src, n);
-  }
+void DeepCopySYCL(void* dst, const void* src, size_t n);
+void DeepCopyAsyncSYCL(const Kokkos::Experimental::SYCL& instance, void* dst,
+                       const void* src, size_t n);
+void DeepCopyAsyncSYCL(void* dst, const void* src, size_t n);
 
-  DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
-    DeepCopy<Kokkos::HostSpace, Kokkos::Experimental::SYCLDeviceUSMSpace,
-             Kokkos::Experimental::SYCL>(Kokkos::Experimental::SYCL(), dst, src,
-                                         n);
-    Kokkos::Experimental::SYCL().fence();
+template <class MemSpace>
+struct DeepCopy<MemSpace, HostSpace, Kokkos::Experimental::SYCL,
+                std::enable_if_t<is_sycl_type_space<MemSpace>::value>> {
+  DeepCopy(void* dst, const void* src, size_t n) { DeepCopySYCL(dst, src, n); }
+  DeepCopy(const Kokkos::Experimental::SYCL& instance, void* dst,
+           const void* src, size_t n) {
+    DeepCopyAsyncSYCL(instance, dst, src, n);
   }
 };
 
-template <class ExecutionSpace>
-struct DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace, Kokkos::HostSpace,
-                ExecutionSpace> {
-  DeepCopy(void* dst, const void* src, size_t n) {
-    (void)DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace, Kokkos::HostSpace,
-                   Kokkos::Experimental::SYCL>(dst, src, n);
-  }
-
-  DeepCopy(const ExecutionSpace& exec, void* dst, const void* src, size_t n) {
-    exec.fence();
-    DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace, Kokkos::HostSpace,
-             Kokkos::Experimental::SYCL>(Kokkos::Experimental::SYCL(), dst, src,
-                                         n);
-    Kokkos::Experimental::SYCL().fence();
+template <class MemSpace>
+struct DeepCopy<HostSpace, MemSpace, Kokkos::Experimental::SYCL,
+                std::enable_if_t<is_sycl_type_space<MemSpace>::value>> {
+  DeepCopy(void* dst, const void* src, size_t n) { DeepCopySYCL(dst, src, n); }
+  DeepCopy(const Kokkos::Experimental::SYCL& instance, void* dst,
+           const void* src, size_t n) {
+    DeepCopyAsyncSYCL(instance, dst, src, n);
   }
 };
 
-template <>
-struct DeepCopy<Experimental::SYCLSharedUSMSpace,
-                Experimental::SYCLSharedUSMSpace, Kokkos::Experimental::SYCL>
-    : public DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                      Experimental::SYCLDeviceUSMSpace,
-                      Kokkos::Experimental::SYCL> {
-  using DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                 Experimental::SYCLDeviceUSMSpace,
-                 Kokkos::Experimental::SYCL>::DeepCopy;
+template <class MemSpace1, class MemSpace2>
+struct DeepCopy<MemSpace1, MemSpace2, Kokkos::Experimental::SYCL,
+                std::enable_if_t<is_sycl_type_space<MemSpace1>::value &&
+                                 is_sycl_type_space<MemSpace2>::value>> {
+  DeepCopy(void* dst, const void* src, size_t n) { DeepCopySYCL(dst, src, n); }
+  DeepCopy(const Kokkos::Experimental::SYCL& instance, void* dst,
+           const void* src, size_t n) {
+    DeepCopyAsyncSYCL(instance, dst, src, n);
+  }
 };
 
-template <>
-struct DeepCopy<Experimental::SYCLSharedUSMSpace, HostSpace,
-                Kokkos::Experimental::SYCL>
-    : public DeepCopy<Experimental::SYCLDeviceUSMSpace, HostSpace,
-                      Kokkos::Experimental::SYCL> {
-  using DeepCopy<Experimental::SYCLDeviceUSMSpace, HostSpace,
-                 Kokkos::Experimental::SYCL>::DeepCopy;
-};
+template <class MemSpace1, class MemSpace2, class ExecutionSpace>
+struct DeepCopy<
+    MemSpace1, MemSpace2, ExecutionSpace,
+    std::enable_if_t<
+        is_sycl_type_space<MemSpace1>::value &&
+        is_sycl_type_space<MemSpace2>::value &&
+        !std::is_same<ExecutionSpace, Kokkos::Experimental::SYCL>::value>> {
+  inline DeepCopy(void* dst, const void* src, size_t n) {
+    DeepCopySYCL(dst, src, n);
+  }
 
-template <>
-struct DeepCopy<HostSpace, Experimental::SYCLSharedUSMSpace,
-                Kokkos::Experimental::SYCL>
-    : public DeepCopy<HostSpace, Experimental::SYCLDeviceUSMSpace,
-                      Kokkos::Experimental::SYCL> {
-  using DeepCopy<HostSpace, Experimental::SYCLDeviceUSMSpace,
-                 Kokkos::Experimental::SYCL>::DeepCopy;
-};
+  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
+                  size_t n) {
+    exec.fence(fence_string());
+    DeepCopyAsyncSYCL(dst, src, n);
+  }
 
-template <>
-struct DeepCopy<Experimental::SYCLSharedUSMSpace,
-                Experimental::SYCLDeviceUSMSpace, Kokkos::Experimental::SYCL>
-    : public DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                      Experimental::SYCLDeviceUSMSpace,
-                      Kokkos::Experimental::SYCL> {
-  using DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                 Experimental::SYCLDeviceUSMSpace,
-                 Kokkos::Experimental::SYCL>::DeepCopy;
+ private:
+  static const std::string& fence_string() {
+    static const std::string string =
+        std::string("Kokkos::Impl::DeepCopy<") + MemSpace1::name() + "Space, " +
+        MemSpace2::name() +
+        "Space, ExecutionSpace>::DeepCopy: fence before copy";
+    return string;
+  }
 };
 
-template <>
-struct DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                Experimental::SYCLSharedUSMSpace, Kokkos::Experimental::SYCL>
-    : public DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                      Experimental::SYCLDeviceUSMSpace,
-                      Kokkos::Experimental::SYCL> {
-  using DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                 Experimental::SYCLDeviceUSMSpace,
-                 Kokkos::Experimental::SYCL>::DeepCopy;
-};
+template <class MemSpace, class ExecutionSpace>
+struct DeepCopy<
+    MemSpace, HostSpace, ExecutionSpace,
+    std::enable_if_t<
+        is_sycl_type_space<MemSpace>::value &&
+        !std::is_same<ExecutionSpace, Kokkos::Experimental::SYCL>::value>> {
+  inline DeepCopy(void* dst, const void* src, size_t n) {
+    DeepCopySYCL(dst, src, n);
+  }
 
-template <class ExecutionSpace>
-struct DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                Experimental::SYCLSharedUSMSpace, ExecutionSpace>
-    : public DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                      Experimental::SYCLDeviceUSMSpace, ExecutionSpace> {
-  using DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                 Experimental::SYCLDeviceUSMSpace, ExecutionSpace>::DeepCopy;
-};
+  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
+                  size_t n) {
+    exec.fence(fence_string());
+    DeepCopyAsyncSYCL(dst, src, n);
+  }
 
-template <class ExecutionSpace>
-struct DeepCopy<Experimental::SYCLSharedUSMSpace,
-                Experimental::SYCLDeviceUSMSpace, ExecutionSpace>
-    : public DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                      Experimental::SYCLDeviceUSMSpace, ExecutionSpace> {
-  using DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                 Experimental::SYCLDeviceUSMSpace, ExecutionSpace>::DeepCopy;
+ private:
+  static const std::string& fence_string() {
+    static const std::string string =
+        std::string("Kokkos::Impl::DeepCopy<") + MemSpace::name() +
+        "Space, HostSpace, ExecutionSpace>::DeepCopy: fence before copy";
+    return string;
+  }
 };
 
-template <class ExecutionSpace>
-struct DeepCopy<Experimental::SYCLSharedUSMSpace,
-                Experimental::SYCLSharedUSMSpace, ExecutionSpace>
-    : public DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                      Experimental::SYCLDeviceUSMSpace, ExecutionSpace> {
-  using DeepCopy<Experimental::SYCLDeviceUSMSpace,
-                 Experimental::SYCLDeviceUSMSpace, ExecutionSpace>::DeepCopy;
-};
+template <class MemSpace, class ExecutionSpace>
+struct DeepCopy<
+    HostSpace, MemSpace, ExecutionSpace,
+    std::enable_if_t<
+        is_sycl_type_space<MemSpace>::value &&
+        !std::is_same<ExecutionSpace, Kokkos::Experimental::SYCL>::value>> {
+  inline DeepCopy(void* dst, const void* src, size_t n) {
+    DeepCopySYCL(dst, src, n);
+  }
 
-template <class ExecutionSpace>
-struct DeepCopy<Experimental::SYCLSharedUSMSpace, HostSpace, ExecutionSpace>
-    : public DeepCopy<Experimental::SYCLDeviceUSMSpace, HostSpace,
-                      ExecutionSpace> {
-  using DeepCopy<Experimental::SYCLDeviceUSMSpace, HostSpace,
-                 ExecutionSpace>::DeepCopy;
-};
+  inline DeepCopy(const ExecutionSpace& exec, void* dst, const void* src,
+                  size_t n) {
+    exec.fence(fence_string());
+    DeepCopyAsyncSYCL(dst, src, n);
+  }
 
-template <class ExecutionSpace>
-struct DeepCopy<HostSpace, Experimental::SYCLSharedUSMSpace, ExecutionSpace>
-    : public DeepCopy<HostSpace, Experimental::SYCLDeviceUSMSpace,
-                      ExecutionSpace> {
-  using DeepCopy<HostSpace, Experimental::SYCLDeviceUSMSpace,
-                 ExecutionSpace>::DeepCopy;
+ private:
+  static const std::string& fence_string() {
+    static const std::string string =
+        std::string("Kokkos::Impl::DeepCopy<HostSpace, ") + MemSpace::name() +
+        "Space, ExecutionSpace>::DeepCopy: fence before copy";
+    return string;
+  }
 };
 
 }  // namespace Impl
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.cpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.cpp
index 5a702b5027277cc7137cba9bba72e7367e9ae97b..816b42038ed0bb1605d005375bd39d2de4e3d69d 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.cpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.cpp
@@ -42,14 +42,7 @@
 //@HEADER
 */
 
-#include <Kokkos_Concepts.hpp>
-#include <SYCL/Kokkos_SYCL_Instance.hpp>
-#include <KokkosCore_Config_DeclareBackend.hpp>
-#include <Kokkos_SYCL.hpp>
-#include <Kokkos_HostSpace.hpp>
-#include <Kokkos_Serial.hpp>
-#include <impl/Kokkos_ConcurrentBitset.hpp>
-#include <impl/Kokkos_Error.hpp>
+#include <Kokkos_Core.hpp>  //kokkos_malloc
 
 namespace Kokkos {
 namespace Experimental {
@@ -122,7 +115,6 @@ void SYCLInternal::initialize(const sycl::queue& q) {
       all_queues.push_back(&m_queue);
     }
     const sycl::device& d = m_queue->get_device();
-    std::cout << SYCL::SYCLDevice(d) << '\n';
 
     m_maxWorkgroupSize =
         d.template get_info<sycl::info::device::max_work_group_size>();
@@ -140,19 +132,22 @@ void SYCLInternal::initialize(const sycl::queue& q) {
           Kokkos::Experimental::SYCLDeviceUSMSpace, void>;
       Record* const r =
           Record::allocate(Kokkos::Experimental::SYCLDeviceUSMSpace(*m_queue),
-                           "Kokkos::SYCL::InternalScratchBitset",
+                           "Kokkos::Experimental::SYCL::InternalScratchBitset",
                            sizeof(uint32_t) * buffer_bound);
       Record::increment(r);
       m_scratchConcurrentBitset = reinterpret_cast<uint32_t*>(r->data());
       auto event                = m_queue->memset(m_scratchConcurrentBitset, 0,
                                    sizeof(uint32_t) * buffer_bound);
-      fence(event);
+      fence(event,
+            "Kokkos::Experimental::SYCLInternal::initialize: fence after "
+            "initializing m_scratchConcurrentBitset",
+            m_instance_id);
     }
 
     m_maxShmemPerBlock =
         d.template get_info<sycl::info::device::local_mem_size>();
-    m_indirectKernelMem.reset(*m_queue);
-    m_indirectReducerMem.reset(*m_queue);
+    m_indirectKernelMem.reset(*m_queue, m_instance_id);
+    m_indirectReducerMem.reset(*m_queue, m_instance_id);
   } else {
     std::ostringstream msg;
     msg << "Kokkos::Experimental::SYCL::initialize(...) FAILED";
@@ -162,10 +157,36 @@ void SYCLInternal::initialize(const sycl::queue& q) {
     }
     Kokkos::Impl::throw_runtime_exception(msg.str());
   }
+
+  m_team_scratch_current_size = 0;
+  m_team_scratch_ptr          = nullptr;
+}
+
+void* SYCLInternal::resize_team_scratch_space(std::int64_t bytes,
+                                              bool force_shrink) {
+  if (m_team_scratch_current_size == 0) {
+    m_team_scratch_current_size = bytes;
+    m_team_scratch_ptr =
+        Kokkos::kokkos_malloc<Experimental::SYCLDeviceUSMSpace>(
+            "Kokkos::Experimental::SYCLDeviceUSMSpace::TeamScratchMemory",
+            m_team_scratch_current_size);
+  }
+  if ((bytes > m_team_scratch_current_size) ||
+      ((bytes < m_team_scratch_current_size) && (force_shrink))) {
+    m_team_scratch_current_size = bytes;
+    m_team_scratch_ptr =
+        Kokkos::kokkos_realloc<Experimental::SYCLDeviceUSMSpace>(
+            m_team_scratch_ptr, m_team_scratch_current_size);
+  }
+  return m_team_scratch_ptr;
 }
 
+uint32_t SYCLInternal::impl_get_instance_id() const { return m_instance_id; }
+
 void SYCLInternal::finalize() {
-  SYCL().fence();
+  SYCLInternal::fence(*m_queue,
+                      "Kokkos::SYCLInternal::finalize: fence on finalization",
+                      m_instance_id);
   was_finalized = true;
 
   using RecordSYCL = Kokkos::Impl::SharedAllocationRecord<SYCLDeviceUSMSpace>;
@@ -182,6 +203,12 @@ void SYCLInternal::finalize() {
   RecordSYCL::decrement(RecordSYCL::get_record(m_scratchConcurrentBitset));
   m_scratchConcurrentBitset = nullptr;
 
+  if (m_team_scratch_current_size > 0)
+    Kokkos::kokkos_free<Kokkos::Experimental::SYCLDeviceUSMSpace>(
+        m_team_scratch_ptr);
+  m_team_scratch_current_size = 0;
+  m_team_scratch_ptr          = nullptr;
+
   m_indirectKernelMem.reset();
   m_indirectReducerMem.reset();
   // guard erasing from all_queues
@@ -208,7 +235,7 @@ void* SYCLInternal::scratch_space(
 
     Record* const r =
         Record::allocate(Kokkos::Experimental::SYCLDeviceUSMSpace(*m_queue),
-                         "Kokkos::SYCL::InternalScratchSpace",
+                         "Kokkos::Experimental::SYCL::InternalScratchSpace",
                          (sizeScratchGrain * m_scratchSpaceCount));
 
     Record::increment(r);
@@ -235,7 +262,7 @@ void* SYCLInternal::scratch_flags(
 
     Record* const r =
         Record::allocate(Kokkos::Experimental::SYCLDeviceUSMSpace(*m_queue),
-                         "Kokkos::SYCL::InternalScratchFlags",
+                         "Kokkos::Experimental::SYCL::InternalScratchFlags",
                          (sizeScratchGrain * m_scratchFlagsCount));
 
     Record::increment(r);
@@ -243,14 +270,38 @@ void* SYCLInternal::scratch_flags(
     m_scratchFlags = reinterpret_cast<size_type*>(r->data());
   }
   m_queue->memset(m_scratchFlags, 0, m_scratchFlagsCount * sizeScratchGrain);
-  fence(*m_queue);
+  fence(*m_queue,
+        "Kokkos::Experimental::SYCLInternal::scratch_flags fence after "
+        "initializing m_scratchFlags",
+        m_instance_id);
 
   return m_scratchFlags;
 }
 
+template <typename WAT>
+void SYCLInternal::fence_helper(WAT& wat, const std::string& name,
+                                uint32_t instance_id) {
+  Kokkos::Tools::Experimental::Impl::profile_fence_event<
+      Kokkos::Experimental::SYCL>(
+      name, Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{instance_id},
+      [&]() {
+        try {
+          wat.wait_and_throw();
+        } catch (sycl::exception const& e) {
+          Kokkos::Impl::throw_runtime_exception(
+              std::string("There was a synchronous SYCL error:\n") += e.what());
+        }
+      });
+}
+template void SYCLInternal::fence_helper<sycl::queue>(sycl::queue&,
+                                                      const std::string&,
+                                                      uint32_t);
+template void SYCLInternal::fence_helper<sycl::event>(sycl::event&,
+                                                      const std::string&,
+                                                      uint32_t);
+
 template <sycl::usm::alloc Kind>
 size_t SYCLInternal::USMObjectMem<Kind>::reserve(size_t n) {
-  assert(m_size == 0);
   assert(m_q);
 
   if (m_capacity < n) {
@@ -258,8 +309,8 @@ size_t SYCLInternal::USMObjectMem<Kind>::reserve(size_t n) {
     // First free what we have (in case malloc can reuse it)
     if (m_data) Record::decrement(Record::get_record(m_data));
 
-    Record* const r = Record::allocate(AllocationSpace(*m_q),
-                                       "Kokkos::SYCL::USMObjectMem", n);
+    Record* const r = Record::allocate(
+        AllocationSpace(*m_q), "Kokkos::Experimental::SYCL::USMObjectMem", n);
     Record::increment(r);
 
     m_data     = r->data();
@@ -271,9 +322,9 @@ size_t SYCLInternal::USMObjectMem<Kind>::reserve(size_t n) {
 
 template <sycl::usm::alloc Kind>
 void SYCLInternal::USMObjectMem<Kind>::reset() {
-  assert(m_size == 0);
-
   if (m_data) {
+    // This implies a fence since this class is not copyable
+    // and deallocating implies a fence across all registered queues.
     using Record = Kokkos::Impl::SharedAllocationRecord<AllocationSpace, void>;
     Record::decrement(Record::get_record(m_data));
 
@@ -285,6 +336,7 @@ void SYCLInternal::USMObjectMem<Kind>::reset() {
 
 template class SYCLInternal::USMObjectMem<sycl::usm::alloc::shared>;
 template class SYCLInternal::USMObjectMem<sycl::usm::alloc::device>;
+template class SYCLInternal::USMObjectMem<sycl::usm::alloc::host>;
 
 }  // namespace Impl
 }  // namespace Experimental
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp
index e797411cd40bdd734c04d2a9b0e51151fa269ebd..bf4d6c5b459579213866f6dcb99332c6e641c3a1 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Instance.hpp
@@ -49,7 +49,7 @@
 #include <CL/sycl.hpp>
 
 #include <impl/Kokkos_Error.hpp>
-
+#include <impl/Kokkos_Profiling.hpp>
 namespace Kokkos {
 namespace Experimental {
 namespace Impl {
@@ -68,7 +68,10 @@ class SYCLInternal {
 
   void* scratch_space(const size_type size);
   void* scratch_flags(const size_type size);
+  void* resize_team_scratch_space(std::int64_t bytes,
+                                  bool force_shrink = false);
 
+  uint32_t impl_get_instance_id() const;
   int m_syclDev = -1;
 
   size_t m_maxWorkgroupSize   = 0;
@@ -81,6 +84,11 @@ class SYCLInternal {
   size_type m_scratchFlagsCount       = 0;
   size_type* m_scratchFlags           = nullptr;
 
+  int64_t m_team_scratch_current_size = 0;
+  void* m_team_scratch_ptr            = nullptr;
+
+  uint32_t m_instance_id = Kokkos::Tools::Experimental::Impl::idForInstance<
+      Kokkos::Experimental::SYCL>(reinterpret_cast<uintptr_t>(this));
   std::optional<sycl::queue> m_queue;
 
   // Using std::vector<std::optional<sycl::queue>> reveals a compiler bug when
@@ -94,40 +102,16 @@ class SYCLInternal {
   template <sycl::usm::alloc Kind>
   class USMObjectMem {
    public:
-    class Deleter {
-     public:
-      Deleter() = default;
-      explicit Deleter(USMObjectMem* mem) : m_mem(mem) {}
-
-      template <typename T>
-      void operator()(T* p) const noexcept {
-        assert(m_mem);
-        assert(sizeof(T) == m_mem->size());
-
-        if constexpr (sycl::usm::alloc::device == kind)
-          // Only skipping the dtor on trivially copyable types
-          static_assert(std::is_trivially_copyable_v<T>);
-        else
-          p->~T();
-
-        m_mem->m_size = 0;
-      }
-
-     private:
-      USMObjectMem* m_mem = nullptr;
-    };
-
-    static constexpr sycl::usm::alloc kind = Kind;
-
     void reset();
 
-    void reset(sycl::queue q) {
+    void reset(sycl::queue q, uint32_t instance_id) {
+      m_instance_id = instance_id;
       reset();
       m_q.emplace(std::move(q));
     }
-
     USMObjectMem() = default;
-    explicit USMObjectMem(sycl::queue q) noexcept : m_q(std::move(q)) {}
+    explicit USMObjectMem(sycl::queue q, uint32_t instance_id) noexcept
+        : m_q(std::move(q)), m_instance_id(instance_id) {}
 
     USMObjectMem(USMObjectMem const&) = delete;
     USMObjectMem(USMObjectMem&&)      = delete;
@@ -139,7 +123,6 @@ class SYCLInternal {
     void* data() noexcept { return m_data; }
     const void* data() const noexcept { return m_data; }
 
-    size_t size() const noexcept { return m_size; }
     size_t capacity() const noexcept { return m_capacity; }
 
     // reserve() allocates space for at least n bytes
@@ -147,120 +130,68 @@ class SYCLInternal {
     size_t reserve(size_t n);
 
    private:
-    using AllocationSpace =
-        std::conditional_t<Kind == sycl::usm::alloc::device,
-                           Kokkos::Experimental::SYCLDeviceUSMSpace,
-                           Kokkos::Experimental::SYCLSharedUSMSpace>;
-
-    // This will memcpy an object T into memory held by this object
-    // returns: a T* to that object
-    //
-    // Note:  it is UB to dereference this pointer with an object that is
-    // not an implicit-lifetime nor trivially-copyable type, but presumably much
-    // faster because we can use USM device memory
-    template <typename T>
-    std::unique_ptr<T, Deleter> memcpy_from(const T& t) {
-      reserve(sizeof(T));
-      sycl::event memcopied = m_q->memcpy(m_data, std::addressof(t), sizeof(T));
-      fence(memcopied);
-
-      std::unique_ptr<T, Deleter> ptr(reinterpret_cast<T*>(m_data),
-                                      Deleter(this));
-      m_size = sizeof(T);
-      return ptr;
-    }
-
-    // This will copy-constuct an object T into memory held by this object
-    // returns: a unique_ptr<T, destruct_delete> that will call the
-    // destructor on the type when it goes out of scope.
-    //
-    // Note:  This will not work with USM device memory
-    template <typename T>
-    std::unique_ptr<T, Deleter> copy_construct_from(const T& t) {
-      static_assert(kind != sycl::usm::alloc::device,
-                    "Cannot copy construct into USM device memory");
-
-      reserve(sizeof(T));
-
-      std::unique_ptr<T, Deleter> ptr(new (m_data) T(t), Deleter(this));
-      m_size = sizeof(T);
-      return ptr;
-    }
+    using AllocationSpace = std::conditional_t<
+        Kind == sycl::usm::alloc::device,
+        Kokkos::Experimental::SYCLDeviceUSMSpace,
+        std::conditional_t<Kind == sycl::usm::alloc::shared,
+                           Kokkos::Experimental::SYCLSharedUSMSpace,
+                           Kokkos::Experimental::SYCLHostUSMSpace>>;
 
    public:
-    // Performs either memcpy (for USM device memory) and returns a T*
-    // (but is technically UB when dereferenced on an object that is not
-    // an implicit-lifetime nor trivially-copyable type
-    //
-    // or
-    //
-    // performs copy construction (for other USM memory types) and returns a
-    // unique_ptr<T, ...>
-    template <typename T>
-    std::unique_ptr<T, Deleter> copy_from(const T& t) {
-      if constexpr (sycl::usm::alloc::device == kind)
-        return memcpy_from(t);
-      else
-        return copy_construct_from(t);
-    }
-
-   private:
-    // Returns a reference to t (helpful when debugging)
+    // Performs either sycl::memcpy (for USM device memory) or std::memcpy
+    // (otherwise) and returns a reference to the copied object.
     template <typename T>
-    T& memcpy_to(T& t) {
-      assert(sizeof(T) == m_size);
-
-      sycl::event memcopied = m_q->memcpy(std::addressof(t), m_data, sizeof(T));
-      fence(memcopied);
-
-      return t;
+    T& copy_from(const T& t) {
+      fence();
+      reserve(sizeof(T));
+      if constexpr (sycl::usm::alloc::device == Kind) {
+        sycl::event memcopied =
+            m_q->memcpy(m_data, std::addressof(t), sizeof(T));
+        SYCLInternal::fence(
+            memcopied,
+            "Kokkos::Experimental::SYCLInternal::USMObject fence after copy",
+            m_instance_id);
+      } else
+        std::memcpy(m_data, std::addressof(t), sizeof(T));
+      return *reinterpret_cast<T*>(m_data);
     }
 
-    // Returns a reference to t (helpful when debugging)
-    template <typename T>
-    T& move_assign_to(T& t) {
-      static_assert(kind != sycl::usm::alloc::device,
-                    "Cannot move_assign_to from USM device memory");
-
-      assert(sizeof(T) == m_size);
-
-      t = std::move(*static_cast<T*>(m_data));
-
-      return t;
+    void fence() {
+      SYCLInternal::fence(
+          m_last_event,
+          "Kokkos::Experimental::SYCLInternal::USMObject fence to wait for "
+          "last event to finish",
+          m_instance_id);
     }
 
-   public:
-    // Returns a reference to t (helpful when debugging)
-    template <typename T>
-    T& transfer_to(T& t) {
-      if constexpr (sycl::usm::alloc::device == kind)
-        return memcpy_to(t);
-      else
-        return move_assign_to(t);
+    void register_event(sycl::event event) {
+      assert(m_last_event
+                 .get_info<sycl::info::event::command_execution_status>() ==
+             sycl::info::event_command_status::complete);
+      m_last_event = event;
     }
 
    private:
     // USMObjectMem class invariants
     // All four expressions below must evaluate to true:
     //
-    //  !m_data == !m_capacity
-    //  m_q || !m_data
-    //  m_data || !m_size
-    //  m_size <= m_capacity
+    //  !m_data == (m_capacity == 0)
+    //      m_q || !m_data
     //
     //  The above invariants mean that:
-    //  if m_size != 0 then m_data != 0
-    //  if m_data != 0 then m_capacity != 0 && m_q != nullopt
-    //  if m_data == 0 then m_capacity == 0
+    //  if m_data != nullptr then m_capacity != 0 && m_q != nullopt
+    //  if m_data == nullptr then m_capacity == 0
 
     std::optional<sycl::queue> m_q;
     void* m_data      = nullptr;
-    size_t m_size     = 0;  // sizeof(T) iff m_data points to live T
     size_t m_capacity = 0;
+    sycl::event m_last_event;
+
+    uint32_t m_instance_id;
   };
 
   // An indirect kernel is one where the functor to be executed is explicitly
-  // copied to USM device memory before being executed, to get around the
+  // copied to USM memory before being executed, to get around the
   // trivially copyable limitation of SYCL.
   using IndirectKernelMem = USMObjectMem<sycl::usm::alloc::shared>;
   IndirectKernelMem m_indirectKernelMem;
@@ -286,18 +217,18 @@ class SYCLInternal {
   // fence(...) takes any type with a .wait_and_throw() method
   // (sycl::event and sycl::queue)
   template <typename WAT>
-  static void fence_helper(WAT& wat) {
-    try {
-      wat.wait_and_throw();
-    } catch (sycl::exception const& e) {
-      Kokkos::Impl::throw_runtime_exception(
-          std::string("There was a synchronous SYCL error:\n") += e.what());
-    }
-  }
+  static void fence_helper(WAT& wat, const std::string& name,
+                           uint32_t instance_id);
 
  public:
-  static void fence(sycl::queue& q) { fence_helper(q); }
-  static void fence(sycl::event& e) { fence_helper(e); }
+  static void fence(sycl::queue& q, const std::string& name,
+                    uint32_t instance_id) {
+    fence_helper(q, name, instance_id);
+  }
+  static void fence(sycl::event& e, const std::string& name,
+                    uint32_t instance_id) {
+    fence_helper(e, name, instance_id);
+  }
 };
 
 template <typename Functor, typename Storage,
@@ -312,20 +243,24 @@ class SYCLFunctionWrapper<Functor, Storage, true> {
   SYCLFunctionWrapper(const Functor& functor, Storage&) : m_functor(functor) {}
 
   const Functor& get_functor() const { return m_functor; }
+
+  static void register_event(Storage&, sycl::event){};
 };
 
 template <typename Functor, typename Storage>
 class SYCLFunctionWrapper<Functor, Storage, false> {
-  std::unique_ptr<Functor,
-                  Experimental::Impl::SYCLInternal::IndirectKernelMem::Deleter>
-      m_kernelFunctorPtr;
+  const Functor& m_kernelFunctor;
 
  public:
   SYCLFunctionWrapper(const Functor& functor, Storage& storage)
-      : m_kernelFunctorPtr(storage.copy_from(functor)) {}
+      : m_kernelFunctor(storage.copy_from(functor)) {}
 
   std::reference_wrapper<const Functor> get_functor() const {
-    return {*m_kernelFunctorPtr};
+    return {m_kernelFunctor};
+  }
+
+  static void register_event(Storage& storage, sycl::event event) {
+    storage.register_event(event);
   }
 };
 
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Range.hpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Range.hpp
index a286169c45988339dce1b14c6d6a4ffde25dcea5..dca73683c3d1f06157affec3e8fe00feb7d36fd0 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Range.hpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Range.hpp
@@ -47,11 +47,13 @@
 
 #include <impl/KokkosExp_IterateTileGPU.hpp>
 
-template <class FunctorType, class ExecPolicy>
-class Kokkos::Impl::ParallelFor<FunctorType, ExecPolicy,
+#include <vector>
+
+template <class FunctorType, class... Traits>
+class Kokkos::Impl::ParallelFor<FunctorType, Kokkos::RangePolicy<Traits...>,
                                 Kokkos::Experimental::SYCL> {
  public:
-  using Policy = ExecPolicy;
+  using Policy = Kokkos::RangePolicy<Traits...>;
 
  private:
   using Member       = typename Policy::member_type;
@@ -62,16 +64,15 @@ class Kokkos::Impl::ParallelFor<FunctorType, ExecPolicy,
   const Policy m_policy;
 
   template <typename Functor>
-  static void sycl_direct_launch(const Policy& policy, const Functor& functor) {
+  static sycl::event sycl_direct_launch(const Policy& policy,
+                                        const Functor& functor) {
     // Convenience references
     const Kokkos::Experimental::SYCL& space = policy.space();
     Kokkos::Experimental::Impl::SYCLInternal& instance =
         *space.impl_internal_space_instance();
     sycl::queue& q = *instance.m_queue;
 
-    space.fence();
-
-    q.submit([functor, policy](sycl::handler& cgh) {
+    auto parallel_for_event = q.submit([functor, policy](sycl::handler& cgh) {
       sycl::range<1> range(policy.end() - policy.begin());
       const auto begin = policy.begin();
 
@@ -83,8 +84,9 @@ class Kokkos::Impl::ParallelFor<FunctorType, ExecPolicy,
           functor(WorkTag(), id);
       });
     });
+    q.submit_barrier(std::vector<sycl::event>{parallel_for_event});
 
-    space.fence();
+    return parallel_for_event;
   }
 
  public:
@@ -100,7 +102,9 @@ class Kokkos::Impl::ParallelFor<FunctorType, ExecPolicy,
 
     const auto functor_wrapper = Experimental::Impl::make_sycl_function_wrapper(
         m_functor, indirectKernelMem);
-    sycl_direct_launch(m_policy, functor_wrapper.get_functor());
+    sycl::event event =
+        sycl_direct_launch(m_policy, functor_wrapper.get_functor());
+    functor_wrapper.register_event(indirectKernelMem, event);
   }
 
   ParallelFor(const ParallelFor&) = delete;
@@ -201,41 +205,48 @@ class Kokkos::Impl::ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
   }
 
   template <typename Functor>
-  void sycl_direct_launch(const Functor& functor) const {
+  sycl::event sycl_direct_launch(const Functor& functor) const {
     // Convenience references
     Kokkos::Experimental::Impl::SYCLInternal& instance =
         *m_space.impl_internal_space_instance();
     sycl::queue& q = *instance.m_queue;
 
-    m_space.fence();
-
-    if (m_policy.m_num_tiles == 0) return;
+    if (m_policy.m_num_tiles == 0) return {};
 
     const BarePolicy bare_policy(m_policy);
 
-    q.submit([functor, this, bare_policy](sycl::handler& cgh) {
-      const auto range = compute_ranges();
-
-      cgh.parallel_for(range, [functor, bare_policy](sycl::nd_item<3> item) {
-        const index_type local_x    = item.get_local_id(0);
-        const index_type local_y    = item.get_local_id(1);
-        const index_type local_z    = item.get_local_id(2);
-        const index_type global_x   = item.get_group(0);
-        const index_type global_y   = item.get_group(1);
-        const index_type global_z   = item.get_group(2);
-        const index_type n_global_x = item.get_group_range(0);
-        const index_type n_global_y = item.get_group_range(1);
-        const index_type n_global_z = item.get_group_range(2);
-
-        Kokkos::Impl::DeviceIterateTile<Policy::rank, BarePolicy, Functor,
-                                        typename Policy::work_tag>(
-            bare_policy, functor, {n_global_x, n_global_y, n_global_z},
-            {global_x, global_y, global_z}, {local_x, local_y, local_z})
-            .exec_range();
-      });
-    });
-
-    m_space.fence();
+    auto parallel_for_event =
+        q.submit([functor, this, bare_policy](sycl::handler& cgh) {
+          const auto range                  = compute_ranges();
+          const sycl::range<3> global_range = range.get_global_range();
+          const sycl::range<3> local_range  = range.get_local_range();
+          const sycl::nd_range sycl_swapped_range{
+              sycl::range<3>{global_range[2], global_range[1], global_range[0]},
+              sycl::range<3>{local_range[2], local_range[1], local_range[0]}};
+
+          cgh.parallel_for(sycl_swapped_range, [functor, bare_policy](
+                                                   sycl::nd_item<3> item) {
+            // swap back for correct index calculations in DeviceIterateTile
+            const index_type local_x    = item.get_local_id(2);
+            const index_type local_y    = item.get_local_id(1);
+            const index_type local_z    = item.get_local_id(0);
+            const index_type global_x   = item.get_group(2);
+            const index_type global_y   = item.get_group(1);
+            const index_type global_z   = item.get_group(0);
+            const index_type n_global_x = item.get_group_range(2);
+            const index_type n_global_y = item.get_group_range(1);
+            const index_type n_global_z = item.get_group_range(0);
+
+            Kokkos::Impl::DeviceIterateTile<Policy::rank, BarePolicy, Functor,
+                                            typename Policy::work_tag>(
+                bare_policy, functor, {n_global_x, n_global_y, n_global_z},
+                {global_x, global_y, global_z}, {local_x, local_y, local_z})
+                .exec_range();
+          });
+        });
+    q.submit_barrier(std::vector<sycl::event>{parallel_for_event});
+
+    return parallel_for_event;
   }
 
  public:
@@ -253,7 +264,8 @@ class Kokkos::Impl::ParallelFor<FunctorType, Kokkos::MDRangePolicy<Traits...>,
 
     const auto functor_wrapper = Experimental::Impl::make_sycl_function_wrapper(
         m_functor, indirectKernelMem);
-    sycl_direct_launch(functor_wrapper.get_functor());
+    sycl::event event = sycl_direct_launch(functor_wrapper.get_functor());
+    functor_wrapper.register_event(indirectKernelMem, event);
   }
 
   ParallelFor(const ParallelFor&) = delete;
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Reduce.hpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Reduce.hpp
index 03b7753f8e81ef5045b16cedd4206d85174c0033..75237b4c72a4dbfc1b7ebe201dda240128f62ced 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Reduce.hpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Reduce.hpp
@@ -46,14 +46,99 @@
 #define KOKKOS_SYCL_PARALLEL_REDUCE_HPP
 
 #include <Kokkos_Macros.hpp>
+
+#include <vector>
 #if defined(KOKKOS_ENABLE_SYCL)
+#include <Kokkos_Parallel_Reduce.hpp>
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 
 namespace Kokkos {
+
 namespace Impl {
 
+namespace SYCLReduction {
+template <class ValueJoin, class ValueOps, typename WorkTag, typename ValueType,
+          typename ReducerType, typename FunctorType, int dim>
+void workgroup_reduction(sycl::nd_item<dim>& item,
+                         sycl::local_ptr<ValueType> local_mem,
+                         ValueType* results_ptr,
+                         ValueType* device_accessible_result_ptr,
+                         const unsigned int value_count,
+                         const ReducerType& selected_reducer,
+                         const FunctorType& functor, bool final) {
+  const auto local_id = item.get_local_linear_id();
+  // FIXME_SYCL should be item.get_group().get_local_linear_range();
+  size_t wgroup_size = 1;
+  for (unsigned int i = 0; i < dim; ++i) wgroup_size *= item.get_local_range(i);
+
+  // Perform the actual workgroup reduction in each subgroup
+  // separately.
+  auto sg                = item.get_sub_group();
+  auto* result           = &local_mem[local_id * value_count];
+  const auto id_in_sg    = sg.get_local_id()[0];
+  const auto local_range = std::min(sg.get_local_range()[0], wgroup_size);
+  for (unsigned int stride = 1; stride < local_range; stride <<= 1) {
+    if (id_in_sg + stride < local_range)
+      ValueJoin::join(selected_reducer, result,
+                      &local_mem[(local_id + stride) * value_count]);
+    sg.barrier();
+  }
+  item.barrier(sycl::access::fence_space::local_space);
+
+  // Copy the subgroup results into the first positions of the
+  // reduction array.
+  if (id_in_sg == 0)
+    ValueOps::copy(functor, &local_mem[sg.get_group_id()[0] * value_count],
+                   result);
+  item.barrier(sycl::access::fence_space::local_space);
+
+  // Do the final reduction only using the first subgroup.
+  if (sg.get_group_id()[0] == 0) {
+    const auto n_subgroups = sg.get_group_range()[0];
+    auto* result_          = &local_mem[id_in_sg * value_count];
+    // In case the number of subgroups is larger than the range of
+    // the first subgroup, we first combine the items with a higher
+    // index.
+    for (unsigned int offset = local_range; offset < n_subgroups;
+         offset += local_range)
+      if (id_in_sg + offset < n_subgroups)
+        ValueJoin::join(selected_reducer, result_,
+                        &local_mem[(id_in_sg + offset) * value_count]);
+    sg.barrier();
+
+    // Then, we proceed as before.
+    for (unsigned int stride = 1; stride < local_range; stride <<= 1) {
+      if (id_in_sg + stride < n_subgroups)
+        ValueJoin::join(selected_reducer, result_,
+                        &local_mem[(id_in_sg + stride) * value_count]);
+      sg.barrier();
+    }
+
+    // Finally, we copy the workgroup results back to global memory
+    // to be used in the next iteration. If this is the last
+    // iteration, i.e., there is only one workgroup also call
+    // final() if necessary.
+    if (id_in_sg == 0) {
+      if (final) {
+        if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
+          FunctorFinal<FunctorType, WorkTag>::final(functor, &local_mem[0]);
+        if (device_accessible_result_ptr != nullptr)
+          ValueOps::copy(functor, &device_accessible_result_ptr[0],
+                         &local_mem[0]);
+        else
+          ValueOps::copy(functor, &results_ptr[0], &local_mem[0]);
+      } else
+        ValueOps::copy(functor,
+                       &results_ptr[(item.get_group_linear_id()) * value_count],
+                       &local_mem[0]);
+    }
+  }
+}
+
+}  // namespace SYCLReduction
+
 template <class FunctorType, class ReducerType, class... Traits>
 class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
                      Kokkos::Experimental::SYCL> {
@@ -76,19 +161,29 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
   ParallelReduce(
       const FunctorType& f, const Policy& p, const V& v,
       typename std::enable_if<Kokkos::is_view<V>::value, void*>::type = nullptr)
-      : m_functor(f), m_policy(p), m_result_ptr(v.data()) {}
+      : m_functor(f),
+        m_policy(p),
+        m_result_ptr(v.data()),
+        m_result_ptr_device_accessible(
+            MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                              typename V::memory_space>::accessible) {}
 
   ParallelReduce(const FunctorType& f, const Policy& p,
                  const ReducerType& reducer)
       : m_functor(f),
         m_policy(p),
         m_reducer(reducer),
-        m_result_ptr(reducer.view().data()) {}
+        m_result_ptr(reducer.view().data()),
+        m_result_ptr_device_accessible(
+            MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                              typename ReducerType::result_view_type::
+                                  memory_space>::accessible) {}
 
  private:
   template <typename PolicyType, typename Functor, typename Reducer>
-  void sycl_direct_launch(const PolicyType& policy, const Functor& functor,
-                          const Reducer& reducer) const {
+  sycl::event sycl_direct_launch(const PolicyType& policy,
+                                 const Functor& functor,
+                                 const Reducer& reducer) const {
     using ReducerConditional =
         Kokkos::Impl::if_c<std::is_same<InvalidType, ReducerType>::value,
                            FunctorType, ReducerType>;
@@ -121,18 +216,18 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
     const unsigned int value_count =
         FunctorValueTraits<ReducerTypeFwd, WorkTagFwd>::value_count(
             selected_reducer);
-    // FIXME_SYCL only use the first half
     const auto results_ptr = static_cast<pointer_type>(instance.scratch_space(
-        sizeof(value_type) * std::max(value_count, 1u) * init_size * 2));
-    // FIXME_SYCL without this we are running into a race condition
-    const auto results_ptr2 =
-        results_ptr + std::max(value_count, 1u) * init_size;
+        sizeof(value_type) * std::max(value_count, 1u) * init_size));
+    value_type* device_accessible_result_ptr =
+        m_result_ptr_device_accessible ? m_result_ptr : nullptr;
+
+    sycl::event last_reduction_event;
 
     // If size<=1 we only call init(), the functor and possibly final once
     // working with the global scratch memory but don't copy back to
     // m_result_ptr yet.
     if (size <= 1) {
-      q.submit([&](sycl::handler& cgh) {
+      auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) {
         const auto begin = policy.begin();
         cgh.single_task([=]() {
           const auto& selected_reducer = ReducerConditional::select(
@@ -149,9 +244,13 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
           if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
             FunctorFinal<FunctorType, WorkTag>::final(
                 static_cast<const FunctorType&>(functor), results_ptr);
+          if (device_accessible_result_ptr != nullptr)
+            ValueOps::copy(functor, &device_accessible_result_ptr[0],
+                           &results_ptr[0]);
         });
       });
-      space.fence();
+      q.submit_barrier(std::vector<sycl::event>{parallel_reduce_event});
+      last_reduction_event = parallel_reduce_event;
     }
 
     // Otherwise, we perform a reduction on the values in all workgroups
@@ -163,7 +262,7 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
       auto n_wgroups = ((size + values_per_thread - 1) / values_per_thread +
                         wgroup_size - 1) /
                        wgroup_size;
-      q.submit([&](sycl::handler& cgh) {
+      auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) {
         sycl::accessor<value_type, 1, sycl::access::mode::read_write,
                        sycl::access::target::local>
             local_mem(sycl::range<1>(wgroup_size) * std::max(value_count, 1u),
@@ -217,49 +316,15 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
               }
               item.barrier(sycl::access::fence_space::local_space);
 
-              // Perform the actual workgroup reduction. To achieve a better
-              // memory access pattern, we use sequential addressing and a
-              // reversed loop. If the workgroup size is 8, the first element
-              // contains all the values with index%4==0, after the second one
-              // the values with index%2==0 and after the third one index%1==0,
-              // i.e., all values.
-              for (unsigned int stride = wgroup_size / 2; stride > 0;
-                   stride >>= 1) {
-                const auto idx = local_id;
-                if (idx < stride) {
-                  ValueJoin::join(selected_reducer,
-                                  &local_mem[idx * value_count],
-                                  &local_mem[(idx + stride) * value_count]);
-                }
-                item.barrier(sycl::access::fence_space::local_space);
-              }
-
-              // Finally, we copy the workgroup results back to global memory to
-              // be used in the next iteration. If this is the last iteration,
-              // i.e., there is only one workgroup also call final() if
-              // necessary.
-              if (local_id == 0) {
-                ValueOps::copy(
-                    functor,
-                    &results_ptr2[(item.get_group_linear_id()) * value_count],
-                    &local_mem[0]);
-                if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
-                  if (n_wgroups <= 1)
-                    FunctorFinal<FunctorType, WorkTag>::final(
-                        static_cast<const FunctorType&>(functor),
-                        &results_ptr2[(item.get_group_linear_id()) *
-                                      value_count]);
-              }
+              SYCLReduction::workgroup_reduction<ValueJoin, ValueOps, WorkTag>(
+                  item, local_mem.get_pointer(), results_ptr,
+                  device_accessible_result_ptr, value_count, selected_reducer,
+                  static_cast<const FunctorType&>(functor), n_wgroups <= 1);
             });
       });
-      space.fence();
+      q.submit_barrier(std::vector<sycl::event>{parallel_reduce_event});
 
-      // FIXME_SYCL this is likely not necessary, see above
-      Kokkos::Impl::DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
-                             Kokkos::Experimental::SYCLDeviceUSMSpace>(
-          space, results_ptr, results_ptr2,
-          sizeof(*m_result_ptr) * value_count * n_wgroups);
-      space.fence();
+      last_reduction_event = parallel_reduce_event;
 
       first_run = false;
       size      = n_wgroups;
@@ -268,13 +333,17 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
     // At this point, the reduced value is written to the entry in results_ptr
     // and all that is left is to copy it back to the given result pointer if
     // necessary.
-    if (m_result_ptr) {
+    if (m_result_ptr && !m_result_ptr_device_accessible) {
       Kokkos::Impl::DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
                              Kokkos::Experimental::SYCLDeviceUSMSpace>(
           space, m_result_ptr, results_ptr,
           sizeof(*m_result_ptr) * value_count);
-      space.fence();
+      space.fence(
+          "Kokkos::Impl::ParallelReduce::sycl_direct_launch: fence due to "
+          "inaccessible reducer result location");
     }
+
+    return last_reduction_event;
   }
 
  public:
@@ -291,15 +360,18 @@ class ParallelReduce<FunctorType, Kokkos::RangePolicy<Traits...>, ReducerType,
     const auto reducer_wrapper = Experimental::Impl::make_sycl_function_wrapper(
         m_reducer, indirectReducerMem);
 
-    sycl_direct_launch(m_policy, functor_wrapper.get_functor(),
-                       reducer_wrapper.get_functor());
+    sycl::event event = sycl_direct_launch(
+        m_policy, functor_wrapper.get_functor(), reducer_wrapper.get_functor());
+    functor_wrapper.register_event(indirectKernelMem, event);
+    reducer_wrapper.register_event(indirectReducerMem, event);
   }
 
  private:
-  FunctorType m_functor;
-  Policy m_policy;
-  ReducerType m_reducer;
-  pointer_type m_result_ptr;
+  const FunctorType m_functor;
+  const Policy m_policy;
+  const ReducerType m_reducer;
+  const pointer_type m_result_ptr;
+  const bool m_result_ptr_device_accessible;
 };
 
 template <class FunctorType, class ReducerType, class... Traits>
@@ -347,7 +419,13 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
   ParallelReduce(
       const FunctorType& f, const Policy& p, const V& v,
       typename std::enable_if<Kokkos::is_view<V>::value, void*>::type = nullptr)
-      : m_functor(f), m_policy(p), m_space(p.space()), m_result_ptr(v.data()) {}
+      : m_functor(f),
+        m_policy(p),
+        m_space(p.space()),
+        m_result_ptr(v.data()),
+        m_result_ptr_device_accessible(
+            MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                              typename V::memory_space>::accessible) {}
 
   ParallelReduce(const FunctorType& f, const Policy& p,
                  const ReducerType& reducer)
@@ -355,12 +433,17 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
         m_policy(p),
         m_space(p.space()),
         m_reducer(reducer),
-        m_result_ptr(reducer.view().data()) {}
+        m_result_ptr(reducer.view().data()),
+        m_result_ptr_device_accessible(
+            MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                              typename ReducerType::result_view_type::
+                                  memory_space>::accessible) {}
 
  private:
   template <typename PolicyType, typename Functor, typename Reducer>
-  void sycl_direct_launch(const PolicyType& policy, const Functor& functor,
-                          const Reducer& reducer) const {
+  sycl::event sycl_direct_launch(const PolicyType& policy,
+                                 const Functor& functor,
+                                 const Reducer& reducer) const {
     using ReducerConditional =
         Kokkos::Impl::if_c<std::is_same<InvalidType, ReducerType>::value,
                            FunctorType, ReducerType>;
@@ -379,8 +462,8 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
         *m_space.impl_internal_space_instance();
     sycl::queue& q = *instance.m_queue;
 
-    const int nwork = m_policy.m_num_tiles;
-    const int block_size =
+    const typename Policy::index_type nwork = m_policy.m_num_tiles;
+    const typename Policy::index_type block_size =
         std::pow(2, std::ceil(std::log2(m_policy.m_prod_tile_dims)));
 
     const sycl::range<1> local_range(block_size);
@@ -402,12 +485,16 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
     // FIXME_SYCL without this we are running into a race condition
     const auto results_ptr2 =
         results_ptr + std::max(value_count, 1u) * init_size;
+    value_type* device_accessible_result_ptr =
+        m_result_ptr_device_accessible ? m_result_ptr : nullptr;
+
+    sycl::event last_reduction_event;
 
     // If size<=1 we only call init(), the functor and possibly final once
     // working with the global scratch memory but don't copy back to
     // m_result_ptr yet.
     if (size <= 1) {
-      q.submit([&](sycl::handler& cgh) {
+      auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) {
         cgh.single_task([=]() {
           const auto& selected_reducer = ReducerConditional::select(
               static_cast<const FunctorType&>(functor),
@@ -424,9 +511,13 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
           if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
             FunctorFinal<FunctorType, WorkTag>::final(
                 static_cast<const FunctorType&>(functor), results_ptr);
+          if (device_accessible_result_ptr)
+            ValueOps::copy(functor, &device_accessible_result_ptr[0],
+                           &results_ptr[0]);
         });
       });
-      m_space.fence();
+      q.submit_barrier(std::vector<sycl::event>{parallel_reduce_event});
+      last_reduction_event = parallel_reduce_event;
     }
 
     // Otherwise, we perform a reduction on the values in all workgroups
@@ -435,8 +526,8 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
     // value.
     bool first_run = true;
     while (size > 1) {
-      auto n_wgroups = (size + wgroup_size - 1) / wgroup_size;
-      q.submit([&](sycl::handler& cgh) {
+      auto n_wgroups             = (size + wgroup_size - 1) / wgroup_size;
+      auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) {
         sycl::accessor<value_type, 1, sycl::access::mode::read_write,
                        sycl::access::target::local>
             local_mem(sycl::range<1>(wgroup_size) * std::max(value_count, 1u),
@@ -498,47 +589,21 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
           }
           item.barrier(sycl::access::fence_space::local_space);
 
-          // Perform the actual workgroup reduction. To achieve a better
-          // memory access pattern, we use sequential addressing and a
-          // reversed loop. If the workgroup size is 8, the first element
-          // contains all the values with index%4==0, after the second one
-          // the values with index%2==0 and after the third one index%1==0,
-          // i.e., all values.
-          for (unsigned int stride = wgroup_size / 2; stride > 0;
-               stride >>= 1) {
-            const auto idx = local_id;
-            if (idx < stride) {
-              ValueJoin::join(selected_reducer, &local_mem[idx * value_count],
-                              &local_mem[(idx + stride) * value_count]);
-            }
-            item.barrier(sycl::access::fence_space::local_space);
-          }
-
-          // Finally, we copy the workgroup results back to global memory to
-          // be used in the next iteration. If this is the last iteration,
-          // i.e., there is only one workgroup also call final() if
-          // necessary.
-          if (local_id == 0) {
-            ValueOps::copy(
-                functor,
-                &results_ptr2[(item.get_group_linear_id()) * value_count],
-                &local_mem[0]);
-            if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
-              if (n_wgroups <= 1)
-                FunctorFinal<FunctorType, WorkTag>::final(
-                    static_cast<const FunctorType&>(functor),
-                    &results_ptr2[(item.get_group_linear_id()) * value_count]);
-          }
+          SYCLReduction::workgroup_reduction<ValueJoin, ValueOps, WorkTag>(
+              item, local_mem.get_pointer(), results_ptr2,
+              device_accessible_result_ptr, value_count, selected_reducer,
+              static_cast<const FunctorType&>(functor),
+              n_wgroups <= 1 && item.get_group_linear_id() == 0);
         });
       });
-      m_space.fence();
+      q.submit_barrier(std::vector<sycl::event>{parallel_reduce_event});
 
       // FIXME_SYCL this is likely not necessary, see above
-      Kokkos::Impl::DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
-                             Kokkos::Experimental::SYCLDeviceUSMSpace>(
-          m_space, results_ptr, results_ptr2,
-          sizeof(*m_result_ptr) * value_count * n_wgroups);
-      m_space.fence();
+      auto deep_copy_event =
+          q.memcpy(results_ptr, results_ptr2,
+                   sizeof(*m_result_ptr) * value_count * n_wgroups);
+      q.submit_barrier(std::vector<sycl::event>{deep_copy_event});
+      last_reduction_event = deep_copy_event;
 
       first_run = false;
       size      = n_wgroups;
@@ -547,19 +612,23 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
     // At this point, the reduced value is written to the entry in results_ptr
     // and all that is left is to copy it back to the given result pointer if
     // necessary.
-    if (m_result_ptr) {
+    if (m_result_ptr && !m_result_ptr_device_accessible) {
       Kokkos::Impl::DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
                              Kokkos::Experimental::SYCLDeviceUSMSpace>(
           m_space, m_result_ptr, results_ptr,
           sizeof(*m_result_ptr) * value_count);
-      m_space.fence();
+      m_space.fence(
+          "Kokkos::Impl::ParallelReduce::sycl_direct_launch: fence after deep "
+          "copying results back");
     }
+
+    return last_reduction_event;
   }
 
  public:
   template <typename Policy, typename Functor>
   static int max_tile_size_product(const Policy& policy, const Functor&) {
-    return policy.space().impl_internal_space_instance()->m_maxThreadsPerSM;
+    return policy.space().impl_internal_space_instance()->m_maxWorkgroupSize;
   }
 
   void execute() const {
@@ -575,16 +644,19 @@ class ParallelReduce<FunctorType, Kokkos::MDRangePolicy<Traits...>, ReducerType,
     const auto reducer_wrapper = Experimental::Impl::make_sycl_function_wrapper(
         m_reducer, indirectReducerMem);
 
-    sycl_direct_launch(m_policy, functor_wrapper.get_functor(),
-                       reducer_wrapper.get_functor());
+    sycl::event event = sycl_direct_launch(
+        m_policy, functor_wrapper.get_functor(), reducer_wrapper.get_functor());
+    functor_wrapper.register_event(indirectKernelMem, event);
+    reducer_wrapper.register_event(indirectReducerMem, event);
   }
 
  private:
-  FunctorType m_functor;
-  BarePolicy m_policy;
+  const FunctorType m_functor;
+  const BarePolicy m_policy;
   const Kokkos::Experimental::SYCL& m_space;
-  ReducerType m_reducer;
-  pointer_type m_result_ptr;
+  const ReducerType m_reducer;
+  const pointer_type m_result_ptr;
+  const bool m_result_ptr_device_accessible;
 };
 
 }  // namespace Impl
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Scan.hpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Scan.hpp
index 5eac6bf9da62b29b9d15697bc5061c00db504e0c..d5611c2159bbc4bf0bd6a29fb89a941f7560650a 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Scan.hpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Scan.hpp
@@ -47,6 +47,7 @@
 
 #include <Kokkos_Macros.hpp>
 #include <memory>
+#include <vector>
 #if defined(KOKKOS_ENABLE_SYCL)
 
 namespace Kokkos {
@@ -86,96 +87,99 @@ class ParallelScanSYCLBase {
   void scan_internal(sycl::queue& q, const Functor& functor,
                      pointer_type global_mem, std::size_t size) const {
     // FIXME_SYCL optimize
-    constexpr size_t wgroup_size = 32;
+    constexpr size_t wgroup_size = 128;
     auto n_wgroups               = (size + wgroup_size - 1) / wgroup_size;
+    pointer_type group_results   = global_mem + n_wgroups * wgroup_size;
 
-    // FIXME_SYCL The allocation should be handled by the execution space
-    auto deleter = [&q](value_type* ptr) { sycl::free(ptr, q); };
-    std::unique_ptr<value_type[], decltype(deleter)> group_results_memory(
-        static_cast<pointer_type>(sycl::malloc(sizeof(value_type) * n_wgroups,
-                                               q, sycl::usm::alloc::shared)),
-        deleter);
-    auto group_results = group_results_memory.get();
-
-    q.submit([&](sycl::handler& cgh) {
+    auto local_scans = q.submit([&](sycl::handler& cgh) {
       sycl::accessor<value_type, 1, sycl::access::mode::read_write,
                      sycl::access::target::local>
           local_mem(sycl::range<1>(wgroup_size), cgh);
 
-      // FIXME_SYCL we get wrong results without this, not sure why
-      sycl::stream out(1, 1, cgh);
       cgh.parallel_for(
           sycl::nd_range<1>(n_wgroups * wgroup_size, wgroup_size),
           [=](sycl::nd_item<1> item) {
-            const auto local_id  = item.get_local_linear_id();
-            const auto global_id = item.get_global_linear_id();
+            const auto local_id      = item.get_local_linear_id();
+            const auto global_id     = item.get_global_linear_id();
+            const auto global_offset = global_id - local_id;
 
             // Initialize local memory
             if (global_id < size)
-              ValueOps::copy(functor, &local_mem[local_id],
-                             &global_mem[global_id]);
+              local_mem[local_id] = global_mem[global_id];
             else
               ValueInit::init(functor, &local_mem[local_id]);
             item.barrier(sycl::access::fence_space::local_space);
 
-            // Perform workgroup reduction
-            for (size_t stride = 1; 2 * stride < wgroup_size + 1; stride *= 2) {
-              auto idx = 2 * stride * (local_id + 1) - 1;
-              if (idx < wgroup_size)
-                ValueJoin::join(functor, &local_mem[idx],
-                                &local_mem[idx - stride]);
-              item.barrier(sycl::access::fence_space::local_space);
+            // subgroup scans
+            auto sg                = item.get_sub_group();
+            const auto sg_group_id = sg.get_group_id()[0];
+            const int id_in_sg     = sg.get_local_id()[0];
+            for (int stride = wgroup_size / 2; stride > 0; stride >>= 1) {
+              auto tmp = sg.shuffle_up(local_mem[local_id], stride);
+              if (id_in_sg >= stride)
+                ValueJoin::join(functor, &local_mem[local_id], &tmp);
             }
 
-            if (local_id == 0) {
-              if (n_wgroups > 1)
-                ValueOps::copy(functor,
-                               &group_results[item.get_group_linear_id()],
-                               &local_mem[wgroup_size - 1]);
-              else
-                ValueInit::init(functor,
-                                &group_results[item.get_group_linear_id()]);
-              ValueInit::init(functor, &local_mem[wgroup_size - 1]);
-            }
+            const int local_range = sg.get_local_range()[0];
+            if (id_in_sg == local_range - 1)
+              global_mem[sg_group_id + global_offset] = local_mem[local_id];
+            local_mem[local_id] = sg.shuffle_up(local_mem[local_id], 1);
+            if (id_in_sg == 0) ValueInit::init(functor, &local_mem[local_id]);
+            item.barrier(sycl::access::fence_space::local_space);
 
-            // Add results to all items
-            for (size_t stride = wgroup_size / 2; stride > 0; stride /= 2) {
-              auto idx = 2 * stride * (local_id + 1) - 1;
-              if (idx < wgroup_size) {
-                value_type dummy;
-                ValueOps::copy(functor, &dummy, &local_mem[idx - stride]);
-                ValueOps::copy(functor, &local_mem[idx - stride],
-                               &local_mem[idx]);
-                ValueJoin::join(functor, &local_mem[idx], &dummy);
+            // scan subgroup results using the first subgroup
+            if (sg_group_id == 0) {
+              const int n_subgroups = sg.get_group_range()[0];
+              if (local_range < n_subgroups) Kokkos::abort("Not implemented!");
+
+              for (int stride = n_subgroups / 2; stride > 0; stride >>= 1) {
+                auto tmp =
+                    sg.shuffle_up(global_mem[id_in_sg + global_offset], stride);
+                if (id_in_sg >= stride) {
+                  if (id_in_sg < n_subgroups)
+                    ValueJoin::join(
+                        functor, &global_mem[id_in_sg + global_offset], &tmp);
+                  else
+                    global_mem[id_in_sg + global_offset] = tmp;
+                }
               }
-              item.barrier(sycl::access::fence_space::local_space);
             }
+            item.barrier(sycl::access::fence_space::local_space);
+
+            // add results to all subgroups
+            if (sg_group_id > 0)
+              ValueJoin::join(functor, &local_mem[local_id],
+                              &global_mem[sg_group_id - 1 + global_offset]);
+            item.barrier(sycl::access::fence_space::local_space);
+            if (n_wgroups > 1 && local_id == wgroup_size - 1)
+              group_results[item.get_group_linear_id()] =
+                  global_mem[sg_group_id + global_offset];
+            item.barrier(sycl::access::fence_space::local_space);
 
             // Write results to global memory
-            if (global_id < size)
-              ValueOps::copy(functor, &global_mem[global_id],
-                             &local_mem[local_id]);
+            if (global_id < size) global_mem[global_id] = local_mem[local_id];
           });
     });
-
-    if (n_wgroups > 1) scan_internal(q, functor, group_results, n_wgroups);
-    m_policy.space().fence();
-
-    q.submit([&](sycl::handler& cgh) {
-      cgh.parallel_for(sycl::nd_range<1>(n_wgroups * wgroup_size, wgroup_size),
-                       [=](sycl::nd_item<1> item) {
-                         const auto global_id = item.get_global_linear_id();
-                         if (global_id < size)
-                           ValueJoin::join(
-                               functor, &global_mem[global_id],
-                               &group_results[item.get_group_linear_id()]);
-                       });
-    });
-    m_policy.space().fence();
+    q.submit_barrier(std::vector<sycl::event>{local_scans});
+
+    if (n_wgroups > 1) {
+      scan_internal(q, functor, group_results, n_wgroups);
+      auto update_with_group_results = q.submit([&](sycl::handler& cgh) {
+        cgh.parallel_for(
+            sycl::nd_range<1>(n_wgroups * wgroup_size, wgroup_size),
+            [=](sycl::nd_item<1> item) {
+              const auto global_id = item.get_global_linear_id();
+              if (global_id < size)
+                ValueJoin::join(functor, &global_mem[global_id],
+                                &group_results[item.get_group_linear_id()]);
+            });
+      });
+      q.submit_barrier(std::vector<sycl::event>{update_with_group_results});
+    }
   }
 
   template <typename Functor>
-  void sycl_direct_launch(const Functor& functor) const {
+  sycl::event sycl_direct_launch(const Functor& functor) const {
     // Convenience references
     const Kokkos::Experimental::SYCL& space = m_policy.space();
     Kokkos::Experimental::Impl::SYCLInternal& instance =
@@ -185,7 +189,7 @@ class ParallelScanSYCLBase {
     const std::size_t len = m_policy.end() - m_policy.begin();
 
     // Initialize global memory
-    q.submit([&](sycl::handler& cgh) {
+    auto initialize_global_memory = q.submit([&](sycl::handler& cgh) {
       auto global_mem = m_scratch_space;
       auto begin      = m_policy.begin();
       cgh.parallel_for(sycl::range<1>(len), [=](sycl::item<1> item) {
@@ -197,29 +201,30 @@ class ParallelScanSYCLBase {
           functor(id, update, false);
         else
           functor(WorkTag(), id, update, false);
-        ValueOps::copy(functor, &global_mem[id], &update);
+        global_mem[id] = update;
       });
     });
-    space.fence();
+    q.submit_barrier(std::vector<sycl::event>{initialize_global_memory});
 
-    // Perform the actual exlcusive scan
+    // Perform the actual exclusive scan
     scan_internal(q, functor, m_scratch_space, len);
 
     // Write results to global memory
-    q.submit([&](sycl::handler& cgh) {
+    auto update_global_results = q.submit([&](sycl::handler& cgh) {
       auto global_mem = m_scratch_space;
       cgh.parallel_for(sycl::range<1>(len), [=](sycl::item<1> item) {
-        auto global_id = item.get_id();
+        auto global_id = item.get_id(0);
 
         value_type update = global_mem[global_id];
         if constexpr (std::is_same<WorkTag, void>::value)
           functor(global_id, update, true);
         else
           functor(WorkTag(), global_id, update, true);
-        ValueOps::copy(functor, &global_mem[global_id], &update);
+        global_mem[global_id] = update;
       });
     });
-    space.fence();
+    q.submit_barrier(std::vector<sycl::event>{update_global_results});
+    return update_global_results;
   }
 
  public:
@@ -227,28 +232,39 @@ class ParallelScanSYCLBase {
   void impl_execute(const PostFunctor& post_functor) {
     if (m_policy.begin() == m_policy.end()) return;
 
-    const auto& q = *m_policy.space().impl_internal_space_instance()->m_queue;
+    auto& instance        = *m_policy.space().impl_internal_space_instance();
     const std::size_t len = m_policy.end() - m_policy.begin();
 
-    // FIXME_SYCL The allocation should be handled by the execution space
-    // consider only storing one value per block and recreate initial results in
-    // the end before doing the final pass
-    auto deleter = [&q](value_type* ptr) { sycl::free(ptr, q); };
-    std::unique_ptr<value_type[], decltype(deleter)> result_memory(
-        static_cast<pointer_type>(sycl::malloc(sizeof(value_type) * len, q,
-                                               sycl::usm::alloc::shared)),
-        deleter);
-    m_scratch_space = result_memory.get();
+    // Compute the total amount of memory we will need. We emulate the recursive
+    // structure that is used to do the actual scan. Essentially, we need to
+    // allocate memory for the whole range and then recursively for the reduced
+    // group results until only one group is left.
+    std::size_t total_memory = 0;
+    {
+      size_t wgroup_size   = 128;
+      size_t n_nested_size = len;
+      size_t n_nested_wgroups;
+      do {
+        n_nested_wgroups = (n_nested_size + wgroup_size - 1) / wgroup_size;
+        n_nested_size    = n_nested_wgroups;
+        total_memory += sizeof(value_type) * n_nested_wgroups * wgroup_size;
+      } while (n_nested_wgroups > 1);
+      total_memory += sizeof(value_type) * wgroup_size;
+    }
+
+    // FIXME_SYCL consider only storing one value per block and recreate initial
+    // results in the end before doing the final pass
+    m_scratch_space =
+        static_cast<pointer_type>(instance.scratch_space(total_memory));
 
     Kokkos::Experimental::Impl::SYCLInternal::IndirectKernelMem&
-        indirectKernelMem = m_policy.space()
-                                .impl_internal_space_instance()
-                                ->m_indirectKernelMem;
+        indirectKernelMem = instance.m_indirectKernelMem;
 
     const auto functor_wrapper = Experimental::Impl::make_sycl_function_wrapper(
         m_functor, indirectKernelMem);
 
-    sycl_direct_launch(functor_wrapper.get_functor());
+    sycl::event event = sycl_direct_launch(functor_wrapper.get_functor());
+    functor_wrapper.register_event(indirectKernelMem, event);
     post_functor();
   }
 
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Team.hpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Team.hpp
index 738620926b5496b9710ce001b77c6fb625325320..9538bf708077cc50404e66e19e048d4341a19761 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Team.hpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Parallel_Team.hpp
@@ -47,8 +47,11 @@
 
 #include <Kokkos_Parallel.hpp>
 
+#include <SYCL/Kokkos_SYCL_Parallel_Reduce.hpp>  // workgroup_reduction
 #include <SYCL/Kokkos_SYCL_Team.hpp>
 
+#include <vector>
+
 namespace Kokkos {
 namespace Impl {
 template <typename... Properties>
@@ -63,8 +66,6 @@ class TeamPolicyInternal<Kokkos::Experimental::SYCL, Properties...>
   friend class TeamPolicyInternal;
 
  private:
-  static int constexpr MAX_WARP = 8;
-
   typename traits::execution_space m_space;
   int m_league_size;
   int m_team_size;
@@ -128,11 +129,18 @@ class TeamPolicyInternal<Kokkos::Experimental::SYCL, Properties...>
   }
   inline bool impl_auto_vector_length() const { return m_tune_vector_length; }
   inline bool impl_auto_team_size() const { return m_tune_team_size; }
+  // FIXME_SYCL This is correct in most cases, but not necessarily in case a
+  // custom sycl::queue is used to initialize the execution space.
   static int vector_length_max() {
-    // FIXME_SYCL provide a reasonable value
-    return 1;
+    std::vector<size_t> sub_group_sizes =
+        execution_space{}
+            .impl_internal_space_instance()
+            ->m_queue->get_device()
+            .template get_info<sycl::info::device::sub_group_sizes>();
+    return *std::max_element(sub_group_sizes.begin(), sub_group_sizes.end());
   }
 
+ private:
   static int verify_requested_vector_length(int requested_vector_length) {
     int test_vector_length =
         std::min(requested_vector_length, vector_length_max());
@@ -140,18 +148,14 @@ class TeamPolicyInternal<Kokkos::Experimental::SYCL, Properties...>
     // Allow only power-of-two vector_length
     if (!(is_integral_power_of_two(test_vector_length))) {
       int test_pow2 = 1;
-      for (int i = 0; i < 5; i++) {
-        test_pow2 = test_pow2 << 1;
-        if (test_pow2 > test_vector_length) {
-          break;
-        }
-      }
+      while (test_pow2 < test_vector_length) test_pow2 <<= 1;
       test_vector_length = test_pow2 >> 1;
     }
 
     return test_vector_length;
   }
 
+ public:
   static int scratch_size_max(int level) {
     return level == 0 ? 1024 * 32
                       :           // FIXME_SYCL arbitrarily setting this to 32kB
@@ -160,7 +164,9 @@ class TeamPolicyInternal<Kokkos::Experimental::SYCL, Properties...>
   inline void impl_set_vector_length(size_t size) { m_vector_length = size; }
   inline void impl_set_team_size(size_t size) { m_team_size = size; }
   int impl_vector_length() const { return m_vector_length; }
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
   KOKKOS_DEPRECATED int vector_length() const { return impl_vector_length(); }
+#endif
 
   int team_size() const { return m_team_size; }
 
@@ -206,7 +212,21 @@ class TeamPolicyInternal<Kokkos::Experimental::SYCL, Properties...>
         m_chunk_size(0),
         m_tune_team_size(bool(team_size_request <= 0)),
         m_tune_vector_length(bool(vector_length_request <= 0)) {
-    // FIXME_SYCL check paramters
+    // FIXME_SYCL Check that league size is permissible,
+    // https://github.com/intel/llvm/pull/4064
+
+    // Make sure total block size is permissible
+    if (m_team_size * m_vector_length >
+        static_cast<int>(
+            m_space.impl_internal_space_instance()->m_maxWorkgroupSize)) {
+      Impl::throw_runtime_exception(
+          std::string("Kokkos::TeamPolicy<SYCL> the team size is too large. "
+                      "Team size x vector length is " +
+                      std::to_string(m_team_size * m_vector_length) +
+                      " but must be smaller than ") +
+          std::to_string(
+              m_space.impl_internal_space_instance()->m_maxWorkgroupSize));
+    }
   }
 
   /** \brief  Specify league size, request team size */
@@ -311,8 +331,9 @@ class TeamPolicyInternal<Kokkos::Experimental::SYCL, Properties...>
          2 * sizeof(double) - m_team_scratch_size[0]) /
         (sizeof(double) + m_thread_scratch_size[0]);
     return std::min<int>(
-        m_space.impl_internal_space_instance()->m_maxWorkgroupSize,
-        max_threads_for_memory);
+               m_space.impl_internal_space_instance()->m_maxWorkgroupSize,
+               max_threads_for_memory) /
+           impl_vector_length();
   }
 
   template <class FunctorType>
@@ -335,8 +356,9 @@ class TeamPolicyInternal<Kokkos::Experimental::SYCL, Properties...>
         (sizeof(double) + sizeof(value_type) * value_count +
          m_thread_scratch_size[0]);
     return std::min<int>(
-        m_space.impl_internal_space_instance()->m_maxWorkgroupSize,
-        max_threads_for_memory);
+               m_space.impl_internal_space_instance()->m_maxWorkgroupSize,
+               max_threads_for_memory) /
+           impl_vector_length();
   }
 
   template <class FunctorType>
@@ -376,14 +398,15 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
   int m_scratch_size[2];
 
   template <typename Functor>
-  void sycl_direct_launch(const Policy& policy, const Functor& functor) const {
+  sycl::event sycl_direct_launch(const Policy& policy,
+                                 const Functor& functor) const {
     // Convenience references
     const Kokkos::Experimental::SYCL& space = policy.space();
     Kokkos::Experimental::Impl::SYCLInternal& instance =
         *space.impl_internal_space_instance();
     sycl::queue& q = *instance.m_queue;
 
-    q.submit([&](sycl::handler& cgh) {
+    auto parallel_for_event = q.submit([&](sycl::handler& cgh) {
       // FIXME_SYCL accessors seem to need a size greater than zero at least for
       // host queues
       sycl::accessor<char, 1, sycl::access::mode::read_write,
@@ -399,14 +422,22 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
 
       cgh.parallel_for(
           sycl::nd_range<2>(
-              sycl::range<2>(m_league_size * m_team_size, m_vector_size),
+              sycl::range<2>(m_team_size, m_league_size * m_vector_size),
               sycl::range<2>(m_team_size, m_vector_size)),
           [=](sycl::nd_item<2> item) {
+#ifdef KOKKOS_ENABLE_DEBUG
+            if (item.get_sub_group().get_local_range() %
+                    item.get_local_range(1) !=
+                0)
+              Kokkos::abort(
+                  "The sub_group size is not divisible by the vector_size. "
+                  "Choose a smaller vector_size!");
+#endif
             const member_type team_member(
                 team_scratch_memory_L0.get_pointer(), shmem_begin,
                 scratch_size[0],
                 static_cast<char*>(scratch_ptr[1]) +
-                    item.get_group(0) * scratch_size[1],
+                    item.get_group(1) * scratch_size[1],
                 scratch_size[1], item);
             if constexpr (std::is_same<work_tag, void>::value)
               functor(team_member);
@@ -414,7 +445,8 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
               functor(work_tag(), team_member);
           });
     });
-    space.fence();
+    q.submit_barrier(std::vector<sycl::event>{parallel_for_event});
+    return parallel_for_event;
   }
 
  public:
@@ -429,7 +461,9 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
     const auto functor_wrapper = Experimental::Impl::make_sycl_function_wrapper(
         m_functor, indirectKernelMem);
 
-    sycl_direct_launch(m_policy, functor_wrapper.get_functor());
+    sycl::event event =
+        sycl_direct_launch(m_policy, functor_wrapper.get_functor());
+    functor_wrapper.register_event(indirectKernelMem, event);
   }
 
   ParallelFor(FunctorType const& arg_functor, Policy const& arg_policy)
@@ -451,11 +485,10 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
     // FIXME_SYCL so far accessors used instead of these pointers
     // Functor's reduce memory, team scan memory, and team shared memory depend
     // upon team size.
-    const auto& space    = *m_policy.space().impl_internal_space_instance();
-    const sycl::queue& q = *space.m_queue;
-    m_scratch_ptr[0]     = nullptr;
-    m_scratch_ptr[1]     = sycl::malloc_device(
-        sizeof(char) * m_scratch_size[1] * m_league_size, q);
+    auto& space      = *m_policy.space().impl_internal_space_instance();
+    m_scratch_ptr[0] = nullptr;
+    m_scratch_ptr[1] = space.resize_team_scratch_space(
+        static_cast<ptrdiff_t>(m_scratch_size[1]) * m_league_size);
 
     if (static_cast<int>(space.m_maxShmemPerBlock) <
         m_shmem_size - m_shmem_begin) {
@@ -463,27 +496,17 @@ class ParallelFor<FunctorType, Kokkos::TeamPolicy<Properties...>,
       out << "Kokkos::Impl::ParallelFor<SYCL> insufficient shared memory! "
              "Requested "
           << m_shmem_size - m_shmem_begin << " bytes but maximum is "
-          << m_policy.space().impl_internal_space_instance()->m_maxShmemPerBlock
-          << '\n';
+          << space.m_maxShmemPerBlock << '\n';
       Kokkos::Impl::throw_runtime_exception(out.str());
     }
 
+    const auto max_team_size =
+        m_policy.team_size_max(arg_functor, ParallelForTag{});
     if (m_team_size > m_policy.team_size_max(arg_functor, ParallelForTag{}))
       Kokkos::Impl::throw_runtime_exception(
-          "Kokkos::Impl::ParallelFor<SYCL> requested too large team size.");
-  }
-
-  // FIXME_SYCL remove when managing m_scratch_ptr[1] in the execution space
-  // instance
-  ParallelFor(const ParallelFor&) = delete;
-  ParallelFor& operator=(const ParallelFor&) = delete;
-
-  ~ParallelFor() {
-    const Kokkos::Experimental::SYCL& space = m_policy.space();
-    Kokkos::Experimental::Impl::SYCLInternal& instance =
-        *space.impl_internal_space_instance();
-    sycl::queue& q = *instance.m_queue;
-    sycl::free(m_scratch_ptr[1], q);
+          "Kokkos::Impl::ParallelFor<SYCL> requested too large team size. The "
+          "maximal team_size is " +
+          std::to_string(max_team_size) + '!');
   }
 };
 
@@ -516,6 +539,7 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
   const Policy m_policy;
   const ReducerType m_reducer;
   const pointer_type m_result_ptr;
+  const bool m_result_ptr_device_accessible;
   // FIXME_SYCL avoid reallocating memory for reductions
   /*  size_type* m_scratch_space;
     size_type* m_scratch_flags;
@@ -529,8 +553,9 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
   const size_type m_vector_size;
 
   template <typename PolicyType, typename Functor, typename Reducer>
-  void sycl_direct_launch(const PolicyType& policy, const Functor& functor,
-                          const Reducer& reducer) const {
+  sycl::event sycl_direct_launch(const PolicyType& policy,
+                                 const Functor& functor,
+                                 const Reducer& reducer) const {
     using ReducerConditional =
         Kokkos::Impl::if_c<std::is_same<InvalidType, ReducerType>::value,
                            FunctorType, ReducerType>;
@@ -553,25 +578,25 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
     sycl::queue& q = *instance.m_queue;
 
     // FIXME_SYCL optimize
-    const size_t wgroup_size = m_team_size;
-    std::size_t size         = m_league_size * m_team_size;
+    const size_t wgroup_size = m_team_size * m_vector_size;
+    std::size_t size         = m_league_size * m_team_size * m_vector_size;
     const auto init_size =
         std::max<std::size_t>((size + wgroup_size - 1) / wgroup_size, 1);
     const unsigned int value_count =
         FunctorValueTraits<ReducerTypeFwd, WorkTagFwd>::value_count(
             selected_reducer);
-    // FIXME_SYCL only use the first half
     const auto results_ptr = static_cast<pointer_type>(instance.scratch_space(
-        sizeof(value_type) * std::max(value_count, 1u) * init_size * 2));
-    // FIXME_SYCL without this we are running into a race condition
-    const auto results_ptr2 =
-        results_ptr + std::max(value_count, 1u) * init_size;
+        sizeof(value_type) * std::max(value_count, 1u) * init_size));
+    value_type* device_accessible_result_ptr =
+        m_result_ptr_device_accessible ? m_result_ptr : nullptr;
+
+    sycl::event last_reduction_event;
 
     // If size<=1 we only call init(), the functor and possibly final once
     // working with the global scratch memory but don't copy back to
     // m_result_ptr yet.
     if (size <= 1) {
-      q.submit([&](sycl::handler& cgh) {
+      auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) {
         // FIXME_SYCL accessors seem to need a size greater than zero at least
         // for host queues
         sycl::accessor<char, 1, sycl::access::mode::read_write,
@@ -606,9 +631,13 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
               if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
                 FunctorFinal<FunctorType, WorkTag>::final(
                     static_cast<const FunctorType&>(functor), results_ptr);
+              if (device_accessible_result_ptr)
+                ValueOps::copy(functor, device_accessible_result_ptr,
+                               &results_ptr[0]);
             });
       });
-      space.fence();
+      q.submit_barrier(std::vector<sycl::event>{parallel_reduce_event});
+      last_reduction_event = parallel_reduce_event;
     }
 
     // Otherwise, we perform a reduction on the values in all workgroups
@@ -617,8 +646,8 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
     // value.
     bool first_run = true;
     while (size > 1) {
-      auto n_wgroups = (size + wgroup_size - 1) / wgroup_size;
-      q.submit([&](sycl::handler& cgh) {
+      auto n_wgroups             = (size + wgroup_size - 1) / wgroup_size;
+      auto parallel_reduce_event = q.submit([&](sycl::handler& cgh) {
         sycl::accessor<value_type, 1, sycl::access::mode::read_write,
                        sycl::access::target::local>
             local_mem(sycl::range<1>(wgroup_size) * std::max(value_count, 1u),
@@ -638,9 +667,17 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
 
         cgh.parallel_for(
             sycl::nd_range<2>(
-                sycl::range<2>(m_league_size * m_team_size, m_vector_size),
+                sycl::range<2>(m_team_size, m_league_size * m_vector_size),
                 sycl::range<2>(m_team_size, m_vector_size)),
             [=](sycl::nd_item<2> item) {
+#ifdef KOKKOS_ENABLE_DEBUG
+              if (first_run && item.get_sub_group().get_local_range() %
+                                       item.get_local_range(1) !=
+                                   0)
+                Kokkos::abort(
+                    "The sub_group size is not divisible by the vector_size. "
+                    "Choose a smaller vector_size!");
+#endif
               const auto local_id = item.get_local_linear_id();
               const auto global_id =
                   wgroup_size * item.get_group_linear_id() + local_id;
@@ -651,9 +688,7 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
               // In the first iteration, we call functor to initialize the local
               // memory. Otherwise, the local memory is initialized with the
               // results from the previous iteration that are stored in global
-              // memory. Note that we load values_per_thread values per thread
-              // and immediately combine them to avoid too many threads being
-              // idle in the actual workgroup reduction.
+              // memory.
               if (first_run) {
                 reference_type update = ValueInit::init(
                     selected_reducer, &local_mem[local_id * value_count]);
@@ -661,7 +696,7 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
                     team_scratch_memory_L0.get_pointer(), shmem_begin,
                     scratch_size[0],
                     static_cast<char*>(scratch_ptr[1]) +
-                        item.get_group(0) * scratch_size[1],
+                        item.get_group(1) * scratch_size[1],
                     scratch_size[1], item);
                 if constexpr (std::is_same<WorkTag, void>::value)
                   functor(team_member, update);
@@ -678,50 +713,18 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
               }
               item.barrier(sycl::access::fence_space::local_space);
 
-              // Perform the actual workgroup reduction. To achieve a better
-              // memory access pattern, we use sequential addressing and a
-              // reversed loop. If the workgroup size is 8, the first element
-              // contains all the values with index%4==0, after the second one
-              // the values with index%2==0 and after the third one index%1==0,
-              // i.e., all values.
-              for (unsigned int stride = wgroup_size / 2; stride > 0;
-                   stride >>= 1) {
-                const auto idx = local_id;
-                if (idx < stride) {
-                  ValueJoin::join(selected_reducer,
-                                  &local_mem[idx * value_count],
-                                  &local_mem[(idx + stride) * value_count]);
-                }
-                item.barrier(sycl::access::fence_space::local_space);
-              }
+              SYCLReduction::workgroup_reduction<ValueJoin, ValueOps, WorkTag>(
+                  item, local_mem.get_pointer(), results_ptr,
+                  device_accessible_result_ptr, value_count, selected_reducer,
+                  static_cast<const FunctorType&>(functor),
+                  n_wgroups <= 1 && item.get_group_linear_id() == 0);
 
-              // Finally, we copy the workgroup results back to global memory to
-              // be used in the next iteration. If this is the last iteration,
-              // i.e., there is only one workgroup also call final() if
-              // necessary.
-              if (local_id == 0) {
-                ValueOps::copy(
-                    functor,
-                    &results_ptr2[(item.get_group_linear_id()) * value_count],
-                    &local_mem[0]);
-                if constexpr (ReduceFunctorHasFinal<FunctorType>::value)
-                  if (n_wgroups <= 1 && item.get_group_linear_id() == 0) {
-                    FunctorFinal<FunctorType, WorkTag>::final(
-                        static_cast<const FunctorType&>(functor),
-                        &results_ptr2[(item.get_group_linear_id()) *
-                                      value_count]);
-                  }
-              }
+              // FIXME_SYCL not quite sure why this is necessary
+              item.barrier(sycl::access::fence_space::global_space);
             });
       });
-      space.fence();
-
-      // FIXME_SYCL this is likely not necessary, see above
-      Kokkos::Impl::DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
-                             Kokkos::Experimental::SYCLDeviceUSMSpace>(
-          space, results_ptr, results_ptr2,
-          sizeof(*m_result_ptr) * value_count * n_wgroups);
-      space.fence();
+      q.submit_barrier(std::vector<sycl::event>{parallel_reduce_event});
+      last_reduction_event = parallel_reduce_event;
 
       first_run = false;
       size      = n_wgroups;
@@ -730,13 +733,17 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
     // At this point, the reduced value is written to the entry in results_ptr
     // and all that is left is to copy it back to the given result pointer if
     // necessary.
-    if (m_result_ptr) {
+    if (m_result_ptr && !m_result_ptr_device_accessible) {
       Kokkos::Impl::DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
                              Kokkos::Experimental::SYCLDeviceUSMSpace>(
           space, m_result_ptr, results_ptr,
           sizeof(*m_result_ptr) * value_count);
-      space.fence();
+      space.fence(
+          "Kokkos::Impl::ParallelReduce<TeamPolicy,SYCL>: fence because "
+          "reduction can't access result storage location");
     }
+
+    return last_reduction_event;
   }
 
  public:
@@ -753,8 +760,10 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
     const auto reducer_wrapper = Experimental::Impl::make_sycl_function_wrapper(
         m_reducer, indirectReducerMem);
 
-    sycl_direct_launch(m_policy, functor_wrapper.get_functor(),
-                       reducer_wrapper.get_functor());
+    sycl::event event = sycl_direct_launch(
+        m_policy, functor_wrapper.get_functor(), reducer_wrapper.get_functor());
+    functor_wrapper.register_event(indirectKernelMem, event);
+    reducer_wrapper.register_event(indirectReducerMem, event);
   }
 
  private:
@@ -779,11 +788,10 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
     // FIXME_SYCL so far accessors used instead of these pointers
     // Functor's reduce memory, team scan memory, and team shared memory depend
     // upon team size.
-    const auto& space    = *m_policy.space().impl_internal_space_instance();
-    const sycl::queue& q = *space.m_queue;
-    m_scratch_ptr[0]     = nullptr;
-    m_scratch_ptr[1]     = sycl::malloc_device(
-        sizeof(char) * m_scratch_size[1] * m_league_size, q);
+    auto& space      = *m_policy.space().impl_internal_space_instance();
+    m_scratch_ptr[0] = nullptr;
+    m_scratch_ptr[1] = space.resize_team_scratch_space(
+        static_cast<ptrdiff_t>(m_scratch_size[1]) * m_league_size);
 
     if (static_cast<int>(space.m_maxShmemPerBlock) <
         m_shmem_size - m_shmem_begin) {
@@ -791,8 +799,7 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
       out << "Kokkos::Impl::ParallelFor<SYCL> insufficient shared memory! "
              "Requested "
           << m_shmem_size - m_shmem_begin << " bytes but maximum is "
-          << m_policy.space().impl_internal_space_instance()->m_maxShmemPerBlock
-          << '\n';
+          << space.m_maxShmemPerBlock << '\n';
       Kokkos::Impl::throw_runtime_exception(out.str());
     }
 
@@ -811,6 +818,9 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
         m_policy(arg_policy),
         m_reducer(InvalidType()),
         m_result_ptr(arg_result.data()),
+        m_result_ptr_device_accessible(
+            MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                              typename ViewType::memory_space>::accessible),
         m_league_size(arg_policy.league_size()),
         m_team_size(arg_policy.team_size()),
         m_vector_size(arg_policy.impl_vector_length()) {
@@ -823,6 +833,10 @@ class ParallelReduce<FunctorType, Kokkos::TeamPolicy<Properties...>,
         m_policy(arg_policy),
         m_reducer(reducer),
         m_result_ptr(reducer.view().data()),
+        m_result_ptr_device_accessible(
+            MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                              typename ReducerType::result_view_type::
+                                  memory_space>::accessible),
         m_league_size(arg_policy.league_size()),
         m_team_size(arg_policy.team_size()),
         m_vector_size(arg_policy.impl_vector_length()) {
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Space.cpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Space.cpp
index 75741438e295c543db2737e6943ea52e244d69db..6ec6204e711586b0d88d6882955d21bf830a5327 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Space.cpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Space.cpp
@@ -56,64 +56,22 @@
 /*--------------------------------------------------------------------------*/
 namespace Kokkos {
 namespace Impl {
-namespace {
-auto USM_memcpy(sycl::queue& q, void* dst, const void* src, size_t n) {
-  return q.memcpy(dst, src, n);
-}
-
-void USM_memcpy(Kokkos::Experimental::Impl::SYCLInternal& space, void* dst,
-                const void* src, size_t n) {
-  (void)USM_memcpy(*space.m_queue, dst, src, n);
-}
-
-void USM_memcpy(void* dst, const void* src, size_t n) {
-  Experimental::SYCL().fence();
-  auto event = USM_memcpy(
-      *Experimental::Impl::SYCLInternal::singleton().m_queue, dst, src, n);
-  Experimental::Impl::SYCLInternal::fence(event);
-}
-}  // namespace
-
-DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
-         Kokkos::Experimental::SYCLDeviceUSMSpace, Kokkos::Experimental::SYCL>::
-    DeepCopy(const Kokkos::Experimental::SYCL& instance, void* dst,
-             const void* src, size_t n) {
-  USM_memcpy(*instance.impl_internal_space_instance(), dst, src, n);
-}
 
-DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace,
-         Kokkos::Experimental::SYCLDeviceUSMSpace,
-         Kokkos::Experimental::SYCL>::DeepCopy(void* dst, const void* src,
-                                               size_t n) {
-  USM_memcpy(dst, src, n);
+void DeepCopySYCL(void* dst, const void* src, size_t n) {
+  Experimental::SYCL().fence("Kokkos::Impl::DeepCopySYCL: fence before memcpy");
+  Experimental::Impl::SYCLInternal::singleton().m_queue->memcpy(dst, src, n);
+  Experimental::SYCL().fence("Kokkos::Impl::DeepCopySYCL: fence after memcpy");
 }
 
-DeepCopy<Kokkos::HostSpace, Kokkos::Experimental::SYCLDeviceUSMSpace,
-         Kokkos::Experimental::SYCL>::DeepCopy(const Kokkos::Experimental::SYCL&
-                                                   instance,
-                                               void* dst, const void* src,
-                                               size_t n) {
-  USM_memcpy(*instance.impl_internal_space_instance(), dst, src, n);
+void DeepCopyAsyncSYCL(const Kokkos::Experimental::SYCL& instance, void* dst,
+                       const void* src, size_t n) {
+  instance.impl_internal_space_instance()->m_queue->memcpy(dst, src, n);
 }
 
-DeepCopy<Kokkos::HostSpace, Kokkos::Experimental::SYCLDeviceUSMSpace,
-         Kokkos::Experimental::SYCL>::DeepCopy(void* dst, const void* src,
-                                               size_t n) {
-  USM_memcpy(dst, src, n);
-}
-
-DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace, Kokkos::HostSpace,
-         Kokkos::Experimental::SYCL>::DeepCopy(const Kokkos::Experimental::SYCL&
-                                                   instance,
-                                               void* dst, const void* src,
-                                               size_t n) {
-  USM_memcpy(*instance.impl_internal_space_instance(), dst, src, n);
-}
-
-DeepCopy<Kokkos::Experimental::SYCLDeviceUSMSpace, Kokkos::HostSpace,
-         Kokkos::Experimental::SYCL>::DeepCopy(void* dst, const void* src,
-                                               size_t n) {
-  USM_memcpy(dst, src, n);
+void DeepCopyAsyncSYCL(void* dst, const void* src, size_t n) {
+  Experimental::Impl::SYCLInternal::singleton().m_queue->memcpy(dst, src, n);
+  Experimental::SYCL().fence(
+      "Kokkos::Impl::DeepCopyAsyncSYCL: fence after memcpy");
 }
 
 }  // namespace Impl
@@ -135,6 +93,11 @@ SYCLSharedUSMSpace::SYCLSharedUSMSpace()
 SYCLSharedUSMSpace::SYCLSharedUSMSpace(sycl::queue queue)
     : m_queue(std::move(queue)) {}
 
+SYCLHostUSMSpace::SYCLHostUSMSpace()
+    : m_queue(*SYCL().impl_internal_space_instance()->m_queue) {}
+SYCLHostUSMSpace::SYCLHostUSMSpace(sycl::queue queue)
+    : m_queue(std::move(queue)) {}
+
 void* allocate_sycl(
     const char* arg_label, const size_t arg_alloc_size,
     const size_t arg_logical_size, const Kokkos::Tools::SpaceHandle arg_handle,
@@ -184,6 +147,19 @@ void* SYCLSharedUSMSpace::allocate(const char* arg_label,
       sycl::usm::alloc::shared, m_queue);
 }
 
+void* SYCLHostUSMSpace::allocate(const size_t arg_alloc_size) const {
+  return allocate("[unlabeled]", arg_alloc_size);
+}
+void* SYCLHostUSMSpace::allocate(const char* arg_label,
+                                 const size_t arg_alloc_size,
+                                 const size_t arg_logical_size) const {
+  return allocate_sycl(
+      arg_label, arg_alloc_size, arg_logical_size,
+      Kokkos::Tools::make_space_handle(name()),
+      RawMemoryAllocationFailure::AllocationMechanism::SYCLMallocHost,
+      sycl::usm::alloc::host, m_queue);
+}
+
 void sycl_deallocate(const char* arg_label, void* const arg_alloc_ptr,
                      const size_t arg_alloc_size, const size_t arg_logical_size,
                      const Kokkos::Tools::SpaceHandle arg_handle,
@@ -195,6 +171,8 @@ void sycl_deallocate(const char* arg_label, void* const arg_alloc_ptr,
                                       reported_size);
   }
 
+  SYCL::impl_static_fence(
+      "Kokkos::Impl::sycl_deallocate: fence before deallocate");
   sycl::free(arg_alloc_ptr, queue);
 }
 
@@ -223,6 +201,19 @@ void SYCLSharedUSMSpace::deallocate(const char* arg_label,
                   Kokkos::Tools::make_space_handle(name()), m_queue);
 }
 
+void SYCLHostUSMSpace::deallocate(void* const arg_alloc_ptr,
+                                  const size_t arg_alloc_size) const {
+  deallocate("[unlabeled]", arg_alloc_ptr, arg_alloc_size);
+}
+
+void SYCLHostUSMSpace::deallocate(const char* arg_label,
+                                  void* const arg_alloc_ptr,
+                                  const size_t arg_alloc_size,
+                                  const size_t arg_logical_size) const {
+  sycl_deallocate(arg_label, arg_alloc_ptr, arg_alloc_size, arg_logical_size,
+                  Kokkos::Tools::make_space_handle(name()), m_queue);
+}
+
 }  // namespace Experimental
 }  // namespace Kokkos
 
@@ -235,6 +226,9 @@ SharedAllocationRecord<void, void> SharedAllocationRecord<
 
 SharedAllocationRecord<void, void> SharedAllocationRecord<
     Kokkos::Experimental::SYCLSharedUSMSpace, void>::s_root_record;
+
+SharedAllocationRecord<void, void> SharedAllocationRecord<
+    Kokkos::Experimental::SYCLHostUSMSpace, void>::s_root_record;
 #endif
 
 SharedAllocationRecord<Kokkos::Experimental::SYCLDeviceUSMSpace, void>::
@@ -282,6 +276,27 @@ SharedAllocationRecord<Kokkos::Experimental::SYCLSharedUSMSpace, void>::
                                                   arg_label);
 }
 
+SharedAllocationRecord<Kokkos::Experimental::SYCLHostUSMSpace, void>::
+    SharedAllocationRecord(
+        const Kokkos::Experimental::SYCLHostUSMSpace& arg_space,
+        const std::string& arg_label, const size_t arg_alloc_size,
+        const SharedAllocationRecord<void, void>::function_type arg_dealloc)
+    // Pass through allocated [ SharedAllocationHeader , user_memory ]
+    // Pass through deallocation function
+    : base_t(
+#ifdef KOKKOS_ENABLE_DEBUG
+          &SharedAllocationRecord<Kokkos::Experimental::SYCLHostUSMSpace,
+                                  void>::s_root_record,
+#endif
+          Impl::checked_allocation_with_header(arg_space, arg_label,
+                                               arg_alloc_size),
+          sizeof(SharedAllocationHeader) + arg_alloc_size, arg_dealloc),
+      m_space(arg_space) {
+
+  this->base_t::_fill_host_accessible_header_info(*base_t::m_alloc_ptr,
+                                                  arg_label);
+}
+
 }  // namespace Impl
 }  // namespace Kokkos
 
@@ -317,6 +332,17 @@ SharedAllocationRecord<Kokkos::Experimental::SYCLSharedUSMSpace,
                      alloc_size, alloc_size - sizeof(SharedAllocationHeader));
 }
 
+SharedAllocationRecord<Kokkos::Experimental::SYCLHostUSMSpace,
+                       void>::~SharedAllocationRecord() {
+  const char* label = nullptr;
+  if (Kokkos::Profiling::profileLibraryLoaded()) {
+    label = RecordBase::m_alloc_ptr->m_label;
+  }
+  const auto alloc_size = SharedAllocationRecord<void, void>::m_alloc_size;
+  m_space.deallocate(label, SharedAllocationRecord<void, void>::m_alloc_ptr,
+                     alloc_size, alloc_size - sizeof(SharedAllocationHeader));
+}
+
 //----------------------------------------------------------------------------
 
 }  // namespace Impl
@@ -339,6 +365,8 @@ template class SharedAllocationRecordCommon<
     Kokkos::Experimental::SYCLDeviceUSMSpace>;
 template class SharedAllocationRecordCommon<
     Kokkos::Experimental::SYCLSharedUSMSpace>;
+template class SharedAllocationRecordCommon<
+    Kokkos::Experimental::SYCLHostUSMSpace>;
 
 }  // namespace Impl
 }  // namespace Kokkos
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Team.hpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Team.hpp
index a30cf2109a60ccc5934bfc6ee834a831c539d485..c405ad31a5fb6d9bb7abee273b9ff10c474b134c 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Team.hpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_Team.hpp
@@ -92,14 +92,12 @@ class SYCLTeamMember {
     return m_item.get_group_linear_id();
   }
   KOKKOS_INLINE_FUNCTION int league_size() const {
-    // FIXME_SYCL needs to be revised for vector_length>1.
-    return m_item.get_group_range(0);
+    return m_item.get_group_range(1);
   }
   KOKKOS_INLINE_FUNCTION int team_rank() const {
-    return m_item.get_local_linear_id();
+    return m_item.get_local_id(0);
   }
   KOKKOS_INLINE_FUNCTION int team_size() const {
-    // FIXME_SYCL needs to be revised for vector_length>1.
     return m_item.get_local_range(0);
   }
   KOKKOS_INLINE_FUNCTION void team_barrier() const { m_item.barrier(); }
@@ -109,8 +107,17 @@ class SYCLTeamMember {
   //--------------------------------------------------------------------------
 
   template <class ValueType>
-  KOKKOS_INLINE_FUNCTION void team_broadcast(ValueType& val,
-                                             const int thread_id) const {
+  KOKKOS_INLINE_FUNCTION std::enable_if_t<std::is_arithmetic_v<ValueType>>
+  team_broadcast(ValueType& val, const int thread_id) const {
+    val = sycl::group_broadcast(m_item.get_group(), val,
+                                sycl::id<2>(thread_id, 0));
+  }
+
+  // FIXME_SYCL remove/adapt this overload once the Intel oneAPI implementation
+  // is conforming to the SYCL2020 standard (allowing trivially-copyable types)
+  template <class ValueType>
+  KOKKOS_INLINE_FUNCTION std::enable_if_t<!std::is_arithmetic_v<ValueType>>
+  team_broadcast(ValueType& val, const int thread_id) const {
     // Wait for shared data write until all threads arrive here
     m_item.barrier(sycl::access::fence_space::local_space);
     if (m_item.get_local_id(1) == 0 &&
@@ -119,7 +126,7 @@ class SYCLTeamMember {
     }
     // Wait for shared data read until root thread writes
     m_item.barrier(sycl::access::fence_space::local_space);
-    val = *static_cast<ValueType*>(m_team_reduce);
+    val = *(static_cast<ValueType*>(m_team_reduce));
   }
 
   template <class Closure, class ValueType>
@@ -294,35 +301,43 @@ class SYCLTeamMember {
   //----------------------------------------
 
   template <typename ReducerType>
-  KOKKOS_INLINE_FUNCTION static
+  KOKKOS_INLINE_FUNCTION
       typename std::enable_if<is_reducer<ReducerType>::value>::type
-      vector_reduce(ReducerType const& reducer) {
+      vector_reduce(ReducerType const& reducer) const {
     vector_reduce(reducer, reducer.reference());
   }
 
   template <typename ReducerType>
-  KOKKOS_INLINE_FUNCTION static
+  KOKKOS_INLINE_FUNCTION
       typename std::enable_if<is_reducer<ReducerType>::value>::type
-      vector_reduce(ReducerType const& /*reducer*/,
-                    typename ReducerType::value_type& /*value*/) {
-    // FIXME_SYCL
-    Kokkos::abort("Not implemented!");
-  }
+      vector_reduce(ReducerType const& reducer,
+                    typename ReducerType::value_type& value) const {
+    const auto tidx1   = m_item.get_local_id(1);
+    const auto grange1 = m_item.get_local_range(1);
 
-  //--------------------------------------------------------------------------
-  /**\brief  Global reduction across all blocks
-   *
-   *  Return !0 if reducer contains the final value
-   */
-  template <typename ReducerType>
-  KOKKOS_INLINE_FUNCTION static
-      typename std::enable_if<is_reducer<ReducerType>::value, int>::type
-      global_reduce(ReducerType const& /*reducer*/,
-                    int* const /*global_scratch_flags*/,
-                    void* const /*global_scratch_space*/, void* const /*shmem*/,
-                    int const /*shmem_size*/) {
-    // FIXME_SYCL
-    Kokkos::abort("Not implemented!");
+    const auto sg = m_item.get_sub_group();
+
+    if (grange1 == 1) return;
+
+    // Intra vector lane shuffle reduction:
+    typename ReducerType::value_type tmp(value);
+    typename ReducerType::value_type tmp2 = tmp;
+
+    for (int i = grange1; (i >>= 1);) {
+      tmp2 = sg.shuffle_down(tmp, i);
+      if (static_cast<int>(tidx1) < i) {
+        reducer.join(tmp, tmp2);
+      }
+    }
+
+    // Broadcast from root lane to all other lanes.
+    // Cannot use "butterfly" algorithm to avoid the broadcast
+    // because floating point summation is not associative
+    // and thus different threads could have different results.
+
+    tmp2  = sg.shuffle(tmp, (sg.get_local_id() / grange1) * grange1);
+    value = tmp2;
+    reducer.reference() = tmp2;
   }
 
   //----------------------------------------
@@ -489,7 +504,6 @@ KOKKOS_INLINE_FUNCTION void parallel_for(
     const Impl::TeamThreadRangeBoundariesStruct<iType, Impl::SYCLTeamMember>&
         loop_boundaries,
     const Closure& closure) {
-  // FIXME_SYCL Fix for vector_length>1.
   for (iType i = loop_boundaries.start +
                  loop_boundaries.member.item().get_local_id(0);
        i < loop_boundaries.end;
@@ -516,7 +530,6 @@ KOKKOS_INLINE_FUNCTION
   typename ReducerType::value_type value;
   reducer.init(value);
 
-  // FIXME_SYCL Fix for vector_length>1.
   for (iType i = loop_boundaries.start +
                  loop_boundaries.member.item().get_local_id(0);
        i < loop_boundaries.end;
@@ -546,7 +559,6 @@ KOKKOS_INLINE_FUNCTION
 
   reducer.init(reducer.reference());
 
-  // FIXME_SYCL Fix for vector_length>1.
   for (iType i = loop_boundaries.start +
                  loop_boundaries.member.item().get_local_id(0);
        i < loop_boundaries.end;
@@ -609,11 +621,14 @@ KOKKOS_INLINE_FUNCTION void parallel_for(
     const Impl::TeamVectorRangeBoundariesStruct<iType, Impl::SYCLTeamMember>&
         loop_boundaries,
     const Closure& closure) {
-  // FIXME_SYCL adapt for vector_length != 1
-  for (iType i = loop_boundaries.start +
-                 loop_boundaries.member.item().get_local_id(0);
-       i < loop_boundaries.end;
-       i += loop_boundaries.member.item().get_local_range(0))
+  const iType tidx0 = loop_boundaries.member.item().get_local_id(0);
+  const iType tidx1 = loop_boundaries.member.item().get_local_id(1);
+
+  const iType grange0 = loop_boundaries.member.item().get_local_range(0);
+  const iType grange1 = loop_boundaries.member.item().get_local_range(1);
+
+  for (iType i = loop_boundaries.start + tidx0 * grange1 + tidx1;
+       i < loop_boundaries.end; i += grange0 * grange1)
     closure(i);
 }
 
@@ -623,17 +638,20 @@ KOKKOS_INLINE_FUNCTION
     parallel_reduce(const Impl::TeamVectorRangeBoundariesStruct<
                         iType, Impl::SYCLTeamMember>& loop_boundaries,
                     const Closure& closure, const ReducerType& reducer) {
-  // FIXME_SYCL adapt for vector_length != 1
   typename ReducerType::value_type value;
   reducer.init(value);
 
-  for (iType i = loop_boundaries.start +
-                 loop_boundaries.member.item().get_local_id(0);
-       i < loop_boundaries.end;
-       i += loop_boundaries.member.item().get_local_range(0)) {
+  const iType tidx0 = loop_boundaries.member.item().get_local_id(0);
+  const iType tidx1 = loop_boundaries.member.item().get_local_id(1);
+
+  const iType grange0 = loop_boundaries.member.item().get_local_range(0);
+  const iType grange1 = loop_boundaries.member.item().get_local_range(1);
+
+  for (iType i = loop_boundaries.start + tidx0 * grange1 + tidx1;
+       i < loop_boundaries.end; i += grange0 * grange1)
     closure(i, value);
-  }
 
+  loop_boundaries.member.vector_reduce(reducer, value);
   loop_boundaries.member.team_reduce(reducer, value);
 }
 
@@ -643,20 +661,23 @@ KOKKOS_INLINE_FUNCTION
     parallel_reduce(const Impl::TeamVectorRangeBoundariesStruct<
                         iType, Impl::SYCLTeamMember>& loop_boundaries,
                     const Closure& closure, ValueType& result) {
-  // FIXME_SYCL adapt for vector_length != 1
   ValueType val;
   Kokkos::Sum<ValueType> reducer(val);
 
   reducer.init(reducer.reference());
 
-  for (iType i = loop_boundaries.start +
-                 loop_boundaries.member.item().get_local_id(0);
-       i < loop_boundaries.end;
-       i += loop_boundaries.member.item().get_local_range(0)) {
+  const iType tidx0 = loop_boundaries.member.item().get_local_id(0);
+  const iType tidx1 = loop_boundaries.member.item().get_local_id(1);
+
+  const iType grange0 = loop_boundaries.member.item().get_local_range(0);
+  const iType grange1 = loop_boundaries.member.item().get_local_range(1);
+
+  for (iType i = loop_boundaries.start + tidx0 * grange1 + tidx1;
+       i < loop_boundaries.end; i += grange0 * grange1)
     closure(i, val);
-  }
 
-  loop_boundaries.member.team_reduce(reducer, val);
+  loop_boundaries.member.vector_reduce(reducer);
+  loop_boundaries.member.team_reduce(reducer);
   result = reducer.reference();
 }
 
@@ -673,9 +694,14 @@ KOKKOS_INLINE_FUNCTION void parallel_for(
     const Impl::ThreadVectorRangeBoundariesStruct<iType, Impl::SYCLTeamMember>&
         loop_boundaries,
     const Closure& closure) {
-  // FIXME_SYC: adapt for vector_length!=1
-  for (auto i = loop_boundaries.start; i != loop_boundaries.end; ++i)
+  const iType tidx1   = loop_boundaries.member.item().get_local_id(1);
+  const iType grange1 = loop_boundaries.member.item().get_local_range(1);
+
+  for (iType i = loop_boundaries.start + tidx1; i < loop_boundaries.end;
+       i += grange1)
     closure(i);
+
+  loop_boundaries.member.item().get_sub_group().barrier();
 }
 
 //----------------------------------------------------------------------------
@@ -697,12 +723,16 @@ KOKKOS_INLINE_FUNCTION
     parallel_reduce(Impl::ThreadVectorRangeBoundariesStruct<
                         iType, Impl::SYCLTeamMember> const& loop_boundaries,
                     Closure const& closure, ReducerType const& reducer) {
-  // FIXME_SYCL adapt for vector_length != 1
   reducer.init(reducer.reference());
 
-  for (iType i = loop_boundaries.start; i < loop_boundaries.end; ++i) {
+  const iType tidx1   = loop_boundaries.member.item().get_local_id(1);
+  const iType grange1 = loop_boundaries.member.item().get_local_range(1);
+
+  for (iType i = loop_boundaries.start + tidx1; i < loop_boundaries.end;
+       i += grange1)
     closure(i, reducer.reference());
-  }
+
+  loop_boundaries.member.vector_reduce(reducer);
 }
 
 /** \brief  Intra-thread vector parallel_reduce.
@@ -722,12 +752,16 @@ KOKKOS_INLINE_FUNCTION
     parallel_reduce(Impl::ThreadVectorRangeBoundariesStruct<
                         iType, Impl::SYCLTeamMember> const& loop_boundaries,
                     Closure const& closure, ValueType& result) {
-  // FIXME_SYCL adapt for vector_length != 1
   result = ValueType();
 
-  for (iType i = loop_boundaries.start; i < loop_boundaries.end; ++i) {
+  const iType tidx1 = loop_boundaries.member.item().get_local_id(1);
+  const int grange1 = loop_boundaries.member.item().get_local_range(1);
+
+  for (iType i = loop_boundaries.start + tidx1; i < loop_boundaries.end;
+       i += grange1)
     closure(i, result);
-  }
+
+  loop_boundaries.member.vector_reduce(Kokkos::Sum<ValueType>(result));
 }
 
 //----------------------------------------------------------------------------
@@ -746,15 +780,59 @@ KOKKOS_INLINE_FUNCTION
     parallel_scan(const Impl::ThreadVectorRangeBoundariesStruct<
                       iType, Impl::SYCLTeamMember>& loop_boundaries,
                   const Closure& closure, const ReducerType& reducer) {
-  // FIXME_SYCL modify for vector_length!=1
   using value_type = typename Kokkos::Impl::FunctorAnalysis<
       Kokkos::Impl::FunctorPatternInterface::SCAN, void, Closure>::value_type;
 
   value_type accum;
   reducer.init(accum);
+  const value_type identity = accum;
+
+  // Loop through boundaries by vector-length chunks must scan at each iteration
+
+  // All thread "lanes" must loop the same number of times.
+  // Determine an loop end for all thread "lanes."
+  // Requires:
+  //   grange1 is power of two and thus
+  //     ( end % grange1 ) == ( end & ( grange1 - 1 ) )
+  //   1 <= grange1 <= sub_group size
+
+  const iType tidx1   = loop_boundaries.member.item().get_local_id(1);
+  const iType grange1 = loop_boundaries.member.item().get_local_range(1);
+
+  const int mask          = grange1 - 1;
+  const int rem           = loop_boundaries.end & mask;  // == end % grange1
+  const int end           = loop_boundaries.end + (rem ? grange1 - rem : 0);
+  const auto sg           = loop_boundaries.member.item().get_sub_group();
+  const int vector_offset = (sg.get_local_id() / grange1) * grange1;
+
+  for (int i = tidx1; i < end; i += grange1) {
+    value_type val = identity;
+
+    // First acquire per-lane contributions.
+    // This sets i's val to i-1's contribution to make the latter shfl_up an
+    // exclusive scan -- the final accumulation of i's val will be included in
+    // the second closure call later.
+    if (i < loop_boundaries.end && tidx1 > 0) closure(i - 1, val, false);
+
+    // Bottom up exclusive scan in triangular pattern where each SYCL thread is
+    // the root of a reduction tree from the zeroth "lane" to itself.
+    //  [t] += [t-1] if t >= 1
+    //  [t] += [t-2] if t >= 2
+    //  [t] += [t-4] if t >= 4
+    //  ...
+    for (int j = 1; j < static_cast<int>(grange1); j <<= 1) {
+      value_type tmp = sg.shuffle_up(val, j);
+      if (j <= static_cast<int>(tidx1)) {
+        reducer.join(val, tmp);
+      }
+    }
+
+    // Include accumulation
+    reducer.join(val, accum);
 
-  for (iType i = loop_boundaries.start; i < loop_boundaries.end; ++i) {
-    closure(i, accum, true);
+    // Update i's contribution into the val and add it to accum for next round
+    if (i < loop_boundaries.end) closure(i, val, true);
+    accum = sg.shuffle(val, mask + vector_offset);
   }
 }
 
@@ -792,21 +870,26 @@ template <class FunctorType>
 KOKKOS_INLINE_FUNCTION void single(
     const Impl::ThreadSingleStruct<Impl::SYCLTeamMember>& single_struct,
     const FunctorType& lambda) {
-  if (single_struct.team_member.team_rank() == 0) lambda();
+  if (single_struct.team_member.item().get_local_linear_id() == 0) lambda();
 }
 
 template <class FunctorType, class ValueType>
 KOKKOS_INLINE_FUNCTION void single(
     const Impl::VectorSingleStruct<Impl::SYCLTeamMember>& single_struct,
     const FunctorType& lambda, ValueType& val) {
-  if (single_struct.team_member.item().get_local_id(1) == 0) lambda(val);
+  const sycl::nd_item<2> item = single_struct.team_member.item();
+  const auto grange1          = item.get_local_range(1);
+  const auto sg               = item.get_sub_group();
+  if (item.get_local_id(1) == 0) lambda(val);
+  val = sg.shuffle(val, (sg.get_local_id() / grange1) * grange1);
 }
 
 template <class FunctorType, class ValueType>
 KOKKOS_INLINE_FUNCTION void single(
     const Impl::ThreadSingleStruct<Impl::SYCLTeamMember>& single_struct,
     const FunctorType& lambda, ValueType& val) {
-  if (single_struct.team_member.team_rank() == 0) lambda(val);
+  if (single_struct.team_member.item().get_local_linear_id() == 0) lambda(val);
+  single_struct.team_member.team_broadcast(val, 0);
 }
 
 }  // namespace Kokkos
diff --git a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_UniqueToken.hpp b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_UniqueToken.hpp
index 141a692f6090555cf129997a64bc9e99941f830d..d2820b3b3a34cdb933c4615260a73e1b82e7de34 100644
--- a/packages/kokkos/core/src/SYCL/Kokkos_SYCL_UniqueToken.hpp
+++ b/packages/kokkos/core/src/SYCL/Kokkos_SYCL_UniqueToken.hpp
@@ -89,7 +89,7 @@ class UniqueToken<SYCL, UniqueTokenScope::Global> {
     const Kokkos::pair<int, int> result =
         Kokkos::Impl::concurrent_bitset::acquire_bounded(
             m_buffer, m_count
-#if defined(KOKKOS_ARCH_INTEL_GEN)
+#ifdef KOKKOS_ARCH_INTEL_GPU
             ,
             Kokkos::Impl::clock_tic() % m_count
 #endif
diff --git a/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp b/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp
index 92bd671bd53bf89482aee39cdd34b3391e9a01a2..18ef97ae4650ff50e4ea4a51b74ab53c88970ca4 100644
--- a/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp
+++ b/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec.cpp
@@ -288,21 +288,46 @@ int ThreadsExec::in_parallel() {
   return s_current_function && (&s_threads_process != s_current_function_arg) &&
          (s_threads_process.m_pool_base || !is_process());
 }
+void ThreadsExec::fence() { internal_fence(Impl::fence_is_static::yes); }
+void ThreadsExec::fence(const std::string &name) {
+  internal_fence(name, Impl::fence_is_static::yes);
+}
+
+void ThreadsExec::internal_fence(Impl::fence_is_static is_static) {
+  internal_fence((is_static == Impl::fence_is_static::no)
+                     ? "Kokkos::ThreadsExec::fence: Unnamed Instance Fence"
+                     : "Kokkos::ThreadsExec::fence: Unnamed Global Fence",
+                 is_static);
+}
 
 // Wait for root thread to become inactive
-void ThreadsExec::fence() {
-  if (s_thread_pool_size[0]) {
-    // Wait for the root thread to complete:
-    Impl::spinwait_while_equal<int>(s_threads_exec[0]->m_pool_state,
-                                    ThreadsExec::Active);
-  }
+void ThreadsExec::internal_fence(const std::string &name,
+                                 Impl::fence_is_static is_static) {
+  const auto &fence_lam = [&]() {
+    if (s_thread_pool_size[0]) {
+      // Wait for the root thread to complete:
+      Impl::spinwait_while_equal<int>(s_threads_exec[0]->m_pool_state,
+                                      ThreadsExec::Active);
+    }
 
-  s_current_function     = nullptr;
-  s_current_function_arg = nullptr;
+    s_current_function     = nullptr;
+    s_current_function_arg = nullptr;
 
-  // Make sure function and arguments are cleared before
-  // potentially re-activating threads with a subsequent launch.
-  memory_fence();
+    // Make sure function and arguments are cleared before
+    // potentially re-activating threads with a subsequent launch.
+    memory_fence();
+  };
+  if (is_static == Impl::fence_is_static::yes) {
+    Kokkos::Tools::Experimental::Impl::profile_fence_event<Kokkos::Threads>(
+        name,
+        Kokkos::Tools::Experimental::SpecialSynchronizationCases::
+            GlobalDeviceSynchronization,
+        fence_lam);
+  } else {
+    Kokkos::Tools::Experimental::Impl::profile_fence_event<Kokkos::Threads>(
+        name, Kokkos::Tools::Experimental::Impl::DirectFenceIDHandle{1},
+        fence_lam);
+  }
 }
 
 /** \brief  Begin execution of the asynchronous functor */
@@ -769,7 +794,12 @@ void ThreadsExec::finalize() {
 namespace Kokkos {
 
 int Threads::concurrency() { return impl_thread_pool_size(0); }
-void Threads::fence() const { Impl::ThreadsExec::fence(); }
+void Threads::fence() const {
+  Impl::ThreadsExec::internal_fence(Impl::fence_is_static::no);
+}
+void Threads::fence(const std::string &name) const {
+  Impl::ThreadsExec::internal_fence(name, Impl::fence_is_static::no);
+}
 
 Threads &Threads::impl_instance(int) {
   static Threads t;
@@ -832,6 +862,9 @@ void ThreadsSpaceInitializer::finalize(const bool all_spaces) {
 }
 
 void ThreadsSpaceInitializer::fence() { Kokkos::Threads::impl_static_fence(); }
+void ThreadsSpaceInitializer::fence(const std::string &name) {
+  Kokkos::Threads::impl_static_fence(name);
+}
 
 void ThreadsSpaceInitializer::print_configuration(std::ostream &msg,
                                                   const bool detail) {
diff --git a/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp b/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp
index 1c8b3ac5f6a7685d2bec7d36b53fc657bf7ba1b9..4d9a72a03467977ed21867a90a84563c7254bba7 100644
--- a/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp
+++ b/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec.hpp
@@ -63,7 +63,6 @@
 
 namespace Kokkos {
 namespace Impl {
-
 class ThreadsExec {
  public:
   // Fan array has log_2(NT) reduction threads plus 2 scan threads
@@ -474,6 +473,12 @@ class ThreadsExec {
 
   static int in_parallel();
   static void fence();
+  static void fence(const std::string &);
+  static void internal_fence(
+      Impl::fence_is_static is_static = Impl::fence_is_static::yes);
+  static void internal_fence(
+      const std::string &,
+      Impl::fence_is_static is_static = Impl::fence_is_static::yes);
   static bool sleep();
   static bool wake();
 
@@ -635,7 +640,12 @@ inline void Threads::print_configuration(std::ostream &s, const bool detail) {
   Impl::ThreadsExec::print_configuration(s, detail);
 }
 
-inline void Threads::impl_static_fence() { Impl::ThreadsExec::fence(); }
+inline void Threads::impl_static_fence() {
+  Impl::ThreadsExec::internal_fence(Impl::fence_is_static::yes);
+}
+inline void Threads::impl_static_fence(const std::string &name) {
+  Impl::ThreadsExec::internal_fence(name, Impl::fence_is_static::yes);
+}
 } /* namespace Kokkos */
 
 //----------------------------------------------------------------------------
diff --git a/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec_base.cpp b/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec_base.cpp
index 40a09ed22ab1d6d73b62084549049521e0eb3150..e4eaeac78163efe48a2ddbd6d39920900b035c29 100644
--- a/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec_base.cpp
+++ b/packages/kokkos/core/src/Threads/Kokkos_ThreadsExec_base.cpp
@@ -100,8 +100,8 @@ bool ThreadsExec::spawn() {
 
   pthread_attr_t attr;
 
-  if (0 == pthread_attr_init(&attr) ||
-      0 == pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) ||
+  if (0 == pthread_attr_init(&attr) &&
+      0 == pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM) &&
       0 == pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
     pthread_t pt;
 
diff --git a/packages/kokkos/core/src/desul/.clang-format b/packages/kokkos/core/src/desul/.clang-format
new file mode 100644
index 0000000000000000000000000000000000000000..9d159247d518108410702980b90b13c2cfb4b84f
--- /dev/null
+++ b/packages/kokkos/core/src/desul/.clang-format
@@ -0,0 +1,2 @@
+DisableFormat: true
+SortIncludes: false
diff --git a/packages/kokkos/core/src/desul/atomics.hpp b/packages/kokkos/core/src/desul/atomics.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ab3fe25392faa70027cb19c2a02c18c570c5768b
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics.hpp
@@ -0,0 +1,19 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_HPP_
+#define DESUL_ATOMICS_HPP_
+
+#include "desul/atomics/Macros.hpp"
+
+#include "desul/atomics/Atomic_Ref.hpp"
+#include "desul/atomics/Compare_Exchange.hpp"
+#include "desul/atomics/Generic.hpp"
+#include "desul/atomics/Lock_Array.hpp"
+
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Atomic_Ref.hpp b/packages/kokkos/core/src/desul/atomics/Atomic_Ref.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..73cd01a7e6ff9c54b8b851193bf256124f399cfe
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Atomic_Ref.hpp
@@ -0,0 +1,541 @@
+/*
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMIC_REF_IMPL_HPP_
+#define DESUL_ATOMIC_REF_IMPL_HPP_
+
+#include <cstddef>
+#include <memory>
+#include <type_traits>
+
+#include "desul/atomics/Common.hpp"
+#include "desul/atomics/Generic.hpp"
+#include "desul/atomics/Macros.hpp"
+
+namespace desul {
+namespace Impl {
+
+// TODO current implementation is missing the following:
+// * member functions
+//   * wait
+//   * notify_one
+//   * notify_all
+
+template <typename T,
+          typename MemoryOrder,
+          typename MemoryScope,
+          bool = std::is_integral<T>{},
+          bool = std::is_floating_point<T>{}>
+struct basic_atomic_ref;
+
+// base class for non-integral, non-floating-point, non-pointer types
+template <typename T, typename MemoryOrder, typename MemoryScope>
+struct basic_atomic_ref<T, MemoryOrder, MemoryScope, false, false> {
+  static_assert(std::is_trivially_copyable<T>{}, "");
+
+ private:
+  T* _ptr;
+
+  // 1/2/4/8/16-byte types must be aligned to at least their size
+  static constexpr int _min_alignment = (sizeof(T) & (sizeof(T) - 1)) || sizeof(T) > 16
+                                            ? 0
+                                            : sizeof(T);
+
+ public:
+  using value_type = T;
+
+  static constexpr bool is_always_lock_free = atomic_always_lock_free(sizeof(T));
+
+  static constexpr std::size_t required_alignment = _min_alignment > alignof(T)
+                                                        ? _min_alignment
+                                                        : alignof(T);
+
+  basic_atomic_ref() = delete;
+  basic_atomic_ref& operator=(basic_atomic_ref const&) = delete;
+
+  basic_atomic_ref(basic_atomic_ref const&) = default;
+
+  explicit basic_atomic_ref(T& obj) : _ptr(std::addressof(obj)) {}
+
+  T operator=(T desired) const noexcept {
+    this->store(desired);
+    return desired;
+  }
+
+  operator T() const noexcept { return this->load(); }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION void store(T desired,
+                            _MemoryOrder order = _MemoryOrder()) const noexcept {
+    atomic_store(_ptr, desired, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION T load(_MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_load(_ptr, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION T exchange(T desired,
+                            _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_load(_ptr, desired, order, MemoryScope());
+  }
+
+  DESUL_FUNCTION bool is_lock_free() const noexcept {
+    return atomic_is_lock_free<sizeof(T), required_alignment>();
+  }
+
+  template <typename SuccessMemoryOrder, typename FailureMemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_weak(T& expected,
+                                            T desired,
+                                            SuccessMemoryOrder success,
+                                            FailureMemoryOrder failure) const noexcept {
+    return atomic_compare_exchange_weak(
+        _ptr, expected, desired, success, failure, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_weak(
+      T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return compare_exchange_weak(expected,
+                          desired,
+                          order,
+                          cmpexch_failure_memory_order<_MemoryOrder>(),
+                          MemoryScope());
+  }
+
+  template <typename SuccessMemoryOrder, typename FailureMemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_strong(
+      T& expected,
+      T desired,
+      SuccessMemoryOrder success,
+      FailureMemoryOrder failure) const noexcept {
+    return atomic_compare_exchange_strong(
+        _ptr, expected, desired, success, failure, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_strong(
+      T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return compare_exchange_strong(expected,
+                            desired,
+                            order,
+                            cmpexch_failure_memory_order<_MemoryOrder>(),
+                            MemoryScope());
+  }
+};
+
+// base class for atomic_ref<integral-type>
+template <typename T, typename MemoryOrder, typename MemoryScope>
+struct basic_atomic_ref<T, MemoryOrder, MemoryScope, true, false> {
+  static_assert(std::is_integral<T>{}, "");
+
+ private:
+  T* _ptr;
+
+ public:
+  using value_type = T;
+  using difference_type = value_type;
+
+  static constexpr bool is_always_lock_free = atomic_always_lock_free(sizeof(T));
+
+  static constexpr std::size_t required_alignment = sizeof(T) > alignof(T) ? sizeof(T)
+                                                                           : alignof(T);
+
+  basic_atomic_ref() = delete;
+  basic_atomic_ref& operator=(basic_atomic_ref const&) = delete;
+
+  explicit basic_atomic_ref(T& obj) : _ptr(&obj) {}
+
+  basic_atomic_ref(basic_atomic_ref const&) = default;
+
+  T operator=(T desired) const noexcept {
+    this->store(desired);
+    return desired;
+  }
+
+  operator T() const noexcept { return this->load(); }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION void store(T desired,
+                            _MemoryOrder order = _MemoryOrder()) const noexcept {
+    atomic_store(_ptr, desired, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION T load(_MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_load(_ptr, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION T exchange(T desired,
+                            _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_load(_ptr, desired, order, MemoryScope());
+  }
+
+  DESUL_FUNCTION bool is_lock_free() const noexcept {
+    return atomic_is_lock_free<sizeof(T), required_alignment>();
+  }
+
+  template <typename SuccessMemoryOrder, typename FailureMemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_weak(T& expected,
+                                            T desired,
+                                            SuccessMemoryOrder success,
+                                            FailureMemoryOrder failure) const noexcept {
+    return atomic_compare_exchange_weak(
+        _ptr, expected, desired, success, failure, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_weak(
+      T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return compare_exchange_weak(expected,
+                          desired,
+                          order,
+                          cmpexch_failure_memory_order<_MemoryOrder>(),
+                          MemoryScope());
+  }
+
+  template <typename SuccessMemoryOrder, typename FailureMemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_strong(
+      T& expected,
+      T desired,
+      SuccessMemoryOrder success,
+      FailureMemoryOrder failure) const noexcept {
+    return atomic_compare_exchange_strong(
+        _ptr, expected, desired, success, failure, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_strong(
+      T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return compare_exchange_strong(expected,
+                            desired,
+                            order,
+                            cmpexch_failure_memory_order<_MemoryOrder>(),
+                            MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION value_type
+  fetch_add(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_fetch_add(_ptr, arg, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION value_type
+  fetch_sub(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_fetch_sub(_ptr, arg, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION value_type
+  fetch_and(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_fetch_and(_ptr, arg, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION value_type
+  fetch_or(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_fetch_or(_ptr, arg, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION value_type
+  fetch_xor(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_fetch_xor(_ptr, arg, order, MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator++() const noexcept {
+    return atomic_add_fetch(_ptr, value_type(1), MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator++(int) const noexcept { return fetch_add(1); }
+
+  DESUL_FUNCTION value_type operator--() const noexcept {
+    return atomic_sub_fetch(_ptr, value_type(1), MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator--(int) const noexcept { return fetch_sub(1); }
+
+  DESUL_FUNCTION value_type operator+=(value_type arg) const noexcept {
+    atomic_add_fetch(_ptr, arg, MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator-=(value_type arg) const noexcept {
+    atomic_sub_fetch(_ptr, arg, MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator&=(value_type arg) const noexcept {
+    atomic_and_fetch(_ptr, arg, MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator|=(value_type arg) const noexcept {
+    atomic_or_fetch(_ptr, arg, MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator^=(value_type arg) const noexcept {
+    atomic_xor_fetch(_ptr, arg, MemoryOrder(), MemoryScope());
+  }
+};
+
+// base class for atomic_ref<floating-point-type>
+template <typename T, typename MemoryOrder, typename MemoryScope>
+struct basic_atomic_ref<T, MemoryOrder, MemoryScope, false, true> {
+  static_assert(std::is_floating_point<T>{}, "");
+
+ private:
+  T* _ptr;
+
+ public:
+  using value_type = T;
+  using difference_type = value_type;
+
+  static constexpr bool is_always_lock_free = atomic_always_lock_free(sizeof(T));
+
+  static constexpr std::size_t required_alignment = alignof(T);
+
+  basic_atomic_ref() = delete;
+  basic_atomic_ref& operator=(basic_atomic_ref const&) = delete;
+
+  explicit basic_atomic_ref(T& obj) : _ptr(&obj) {}
+
+  basic_atomic_ref(basic_atomic_ref const&) = default;
+
+  T operator=(T desired) const noexcept {
+    this->store(desired);
+    return desired;
+  }
+
+  operator T() const noexcept { return this->load(); }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION void store(T desired,
+                            _MemoryOrder order = _MemoryOrder()) const noexcept {
+    atomic_store(_ptr, desired, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION T load(_MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_load(_ptr, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION T exchange(T desired,
+                            _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_load(_ptr, desired, order, MemoryScope());
+  }
+
+  DESUL_FUNCTION bool is_lock_free() const noexcept {
+    return atomic_is_lock_free<sizeof(T), required_alignment>();
+  }
+
+  template <typename SuccessMemoryOrder, typename FailureMemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_weak(T& expected,
+                                            T desired,
+                                            SuccessMemoryOrder success,
+                                            FailureMemoryOrder failure) const noexcept {
+    return atomic_compare_exchange_weak(
+        _ptr, expected, desired, success, failure, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_weak(
+      T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return compare_exchange_weak(expected,
+                          desired,
+                          order,
+                          cmpexch_failure_memory_order<_MemoryOrder>(),
+                          MemoryScope());
+  }
+
+  template <typename SuccessMemoryOrder, typename FailureMemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_strong(
+      T& expected,
+      T desired,
+      SuccessMemoryOrder success,
+      FailureMemoryOrder failure) const noexcept {
+    return atomic_compare_exchange_strong(
+        _ptr, expected, desired, success, failure, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_strong(
+      T& expected, T desired, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return compare_exchange_strong(expected,
+                            desired,
+                            order,
+                            cmpexch_failure_memory_order<_MemoryOrder>(),
+                            MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION value_type
+  fetch_add(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_fetch_add(_ptr, arg, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION value_type
+  fetch_sub(value_type arg, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_fetch_sub(_ptr, arg, order, MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator+=(value_type arg) const noexcept {
+    atomic_add_fetch(_ptr, arg, MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator-=(value_type arg) const noexcept {
+    atomic_sub_fetch(_ptr, arg, MemoryOrder(), MemoryScope());
+  }
+};
+
+// base class for atomic_ref<pointer-type>
+template <typename T, typename MemoryOrder, typename MemoryScope>
+struct basic_atomic_ref<T*, MemoryOrder, MemoryScope, false, false> {
+ private:
+  T** _ptr;
+
+ public:
+  using value_type = T*;
+  using difference_type = std::ptrdiff_t;
+
+  static constexpr bool is_always_lock_free = atomic_always_lock_free(sizeof(T));
+
+  static constexpr std::size_t required_alignment = alignof(T*);
+
+  basic_atomic_ref() = delete;
+  basic_atomic_ref& operator=(basic_atomic_ref const&) = delete;
+
+  explicit basic_atomic_ref(T*& arg) : _ptr(std::addressof(arg)) {}
+
+  basic_atomic_ref(basic_atomic_ref const&) = default;
+
+  T* operator=(T* desired) const noexcept {
+    this->store(desired);
+    return desired;
+  }
+
+  operator T*() const noexcept { return this->load(); }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION void store(T* desired,
+                            _MemoryOrder order = _MemoryOrder()) const noexcept {
+    atomic_store(_ptr, desired, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION T* load(_MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_load(_ptr, order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION T* exchange(T* desired,
+                             _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_load(_ptr, desired, order, MemoryScope());
+  }
+
+  DESUL_FUNCTION bool is_lock_free() const noexcept {
+    return atomic_is_lock_free<sizeof(T*), required_alignment>();
+  }
+
+  template <typename SuccessMemoryOrder, typename FailureMemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_weak(T*& expected,
+                                            T* desired,
+                                            SuccessMemoryOrder success,
+                                            FailureMemoryOrder failure) const noexcept {
+    return atomic_compare_exchange_weak(
+        _ptr, expected, desired, success, failure, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_weak(
+      T*& expected, T* desired, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return compare_exchange_weak(expected,
+                          desired,
+                          order,
+                          cmpexch_failure_memory_order<_MemoryOrder>(),
+                          MemoryScope());
+  }
+
+  template <typename SuccessMemoryOrder, typename FailureMemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_strong(
+      T*& expected,
+      T* desired,
+      SuccessMemoryOrder success,
+      FailureMemoryOrder failure) const noexcept {
+    return atomic_compare_exchange_strong(
+        _ptr, expected, desired, success, failure, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION bool compare_exchange_strong(
+      T*& expected, T* desired, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return compare_exchange_strong(expected,
+                            desired,
+                            order,
+                            cmpexch_failure_memory_order<_MemoryOrder>(),
+                            MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION value_type
+  fetch_add(difference_type d, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_fetch_add(_ptr, _type_size(d), order, MemoryScope());
+  }
+
+  template <typename _MemoryOrder = MemoryOrder>
+  DESUL_FUNCTION value_type
+  fetch_sub(difference_type d, _MemoryOrder order = _MemoryOrder()) const noexcept {
+    return atomic_fetch_sub(_ptr, _type_size(d), order, MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator++() const noexcept {
+    return atomic_add_fetch(_ptr, _type_size(1), MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator++(int) const noexcept { return fetch_add(1); }
+
+  DESUL_FUNCTION value_type operator--() const noexcept {
+    return atomic_sub_fetch(_ptr, _type_size(1), MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator--(int) const noexcept { return fetch_sub(1); }
+
+  DESUL_FUNCTION value_type operator+=(difference_type d) const noexcept {
+    atomic_add_fetch(_ptr, _type_size(d), MemoryOrder(), MemoryScope());
+  }
+
+  DESUL_FUNCTION value_type operator-=(difference_type d) const noexcept {
+    atomic_sub_fetch(_ptr, _type_size(d), MemoryOrder(), MemoryScope());
+  }
+
+ private:
+  static constexpr std::ptrdiff_t _type_size(std::ptrdiff_t d) noexcept {
+    static_assert(std::is_object<T>{}, "");
+    return d * sizeof(T);
+  }
+};
+
+}  // namespace Impl
+
+template <typename T, typename MemoryOrder, typename MemoryScope>
+struct scoped_atomic_ref : Impl::basic_atomic_ref<T, MemoryOrder, MemoryScope> {
+  explicit scoped_atomic_ref(T& obj) noexcept
+      : Impl::basic_atomic_ref<T, MemoryOrder, MemoryScope>(obj) {}
+
+  scoped_atomic_ref& operator=(scoped_atomic_ref const&) = delete;
+
+  scoped_atomic_ref(scoped_atomic_ref const&) = default;
+
+  using Impl::basic_atomic_ref<T, MemoryOrder, MemoryScope>::operator=;
+};
+
+}  // namespace desul
+
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/CUDA.hpp b/packages/kokkos/core/src/desul/atomics/CUDA.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..32873a59776b07dea770c193e0034c1e82387246
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/CUDA.hpp
@@ -0,0 +1,453 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+#ifndef DESUL_ATOMICS_CUDA_HPP_
+#define DESUL_ATOMICS_CUDA_HPP_
+
+#ifdef DESUL_HAVE_CUDA_ATOMICS
+// When building with clang we need to include the device functions always
+// since clang must see a consistent overload set in both device and host compilation
+// but that means we need to know on the host what to make visible, i.e. we need
+// a host side compile knowledge of architecture.
+// We simply can say DESUL proper doesn't support clang CUDA build pre Volta,
+// Kokkos has that knowledge and so I use it here, allowing in Kokkos to use
+// clang with pre Volta as CUDA compiler
+#if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__>=700)) || \
+    (!defined(__NVCC__) && !defined(KOKKOS_ARCH_KEPLER) && !defined(KOKKOS_ARCH_MAXWELL) && !defined(KOKKOS_ARCH_PASCAL))
+#define DESUL_HAVE_CUDA_ATOMICS_ASM
+#include <desul/atomics/cuda/CUDA_asm.hpp>
+#endif
+
+#if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__<700)) || \
+    (!defined(__NVCC__) && !defined(DESUL_HAVE_CUDA_ATOMICS_ASM))
+namespace desul {
+namespace Impl {
+template<class T>
+struct is_cuda_atomic_integer_type {
+  static constexpr bool value = std::is_same<T,int>::value ||
+                                std::is_same<T,unsigned int>::value ||
+                                std::is_same<T,unsigned long long int>::value;
+};
+
+template<class T>
+struct is_cuda_atomic_add_type {
+  static constexpr bool value = is_cuda_atomic_integer_type<T>::value ||
+#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ >= 600)
+                                std::is_same<T,double>::value || 
+#endif
+                                std::is_same<T,float>::value;
+};
+
+template<class T>
+struct is_cuda_atomic_sub_type {
+  static constexpr bool value = std::is_same<T,int>::value ||
+                                std::is_same<T,unsigned int>::value;
+};
+} // Impl
+
+// Atomic Add
+template<class T>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_add_type<T>::value,T>::type
+atomic_fetch_add(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicAdd(dest,val);
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_add_type<T>::value,T>::type
+atomic_fetch_add(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicAdd(dest,val);
+  __threadfence();
+  return return_val;
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_add_type<T>::value,T>::type
+atomic_fetch_add(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_add(dest,val,MemoryOrder(),MemoryScopeDevice());
+}
+
+
+// Atomic Sub
+template<class T>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_sub_type<T>::value,T>::type
+atomic_fetch_sub(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicSub(dest,val);
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_sub_type<T>::value,T>::type
+atomic_fetch_sub(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicSub(dest,val);
+  __threadfence();
+  return return_val;
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_sub_type<T>::value,T>::type
+atomic_fetch_sub(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_sub(dest,val,MemoryOrder(),MemoryScopeDevice());
+}
+
+// Atomic Inc
+__device__ inline
+unsigned int atomic_fetch_inc(unsigned int* dest, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicInc(dest,val);
+}
+
+template<class MemoryOrder>
+__device__ inline
+unsigned int atomic_fetch_inc(unsigned int* dest, unsigned int val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  unsigned int return_val = atomicInc(dest,val);
+  __threadfence();
+  return return_val;
+}
+
+template<class MemoryOrder>
+__device__ inline
+unsigned int atomic_fetch_inc(unsigned int* dest, unsigned int val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_inc(dest,val,MemoryOrder(),MemoryScopeDevice());
+}
+
+// Atomic Inc
+__device__ inline
+unsigned int atomic_fetch_dec(unsigned int* dest, unsigned int val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicDec(dest,val);
+}
+
+template<class MemoryOrder>
+__device__ inline
+unsigned int atomic_fetch_dec(unsigned int* dest, unsigned int val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  unsigned int return_val = atomicDec(dest,val);
+  __threadfence();
+  return return_val;
+}
+
+template<class MemoryOrder>
+__device__ inline
+unsigned int atomic_fetch_dec(unsigned int* dest, unsigned int val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_dec(dest,val,MemoryOrder(),MemoryScopeDevice());
+}
+
+
+// Atomic Max
+template<class T>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_max(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicMax(dest,val);
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_max(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicMax(dest,val);
+  __threadfence();
+  return return_val;
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_max(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_max(dest,val,MemoryOrder(),MemoryScopeDevice());
+}
+
+// Atomic Min
+template<class T>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_min(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicMin(dest,val);
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_min(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicMin(dest,val);
+  __threadfence();
+  return return_val;
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_min(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_min(dest,val,MemoryOrder(),MemoryScopeDevice());
+}
+
+// Atomic And
+template<class T>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_and(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicAnd(dest,val);
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_and(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicAnd(dest,val);
+  __threadfence();
+  return return_val;
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_and(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_and(dest,val,MemoryOrder(),MemoryScopeDevice());
+}
+
+// Atomic XOR
+template<class T>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_xor(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicXor(dest,val);
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_xor(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicXor(dest,val);
+  __threadfence();
+  return return_val;
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_xor(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_xor(dest,val,MemoryOrder(),MemoryScopeDevice());
+}
+
+// Atomic OR
+template<class T>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_or(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicOr(dest,val);
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_or(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicOr(dest,val);
+  __threadfence();
+  return return_val;
+}
+
+template<class T, class MemoryOrder>
+__device__ inline
+typename std::enable_if<Impl::is_cuda_atomic_integer_type<T>::value,T>::type
+atomic_fetch_or(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_or(dest,val,MemoryOrder(),MemoryScopeDevice());
+}
+} // desul
+#endif
+
+#if !defined(__NVCC__)
+// Functions defined as device functions in CUDA which don't exist in the GCC overload set
+namespace desul {
+
+#if defined(DESUL_HAVE_CUDA_ATOMICS_ASM)
+  #define DESUL_IMPL_CUDA_HOST_ATOMIC_ADD(TYPE,ORDER,SCOPE) \
+    inline void atomic_add(TYPE* const dest, TYPE val, ORDER order, SCOPE scope) { \
+    (void) atomic_fetch_add(dest, val, order, scope); \
+  }
+  DESUL_IMPL_CUDA_HOST_ATOMIC_ADD(int32_t,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_ADD(long,MemoryOrderRelaxed,MemoryScopeDevice); // only for ASM?
+  DESUL_IMPL_CUDA_HOST_ATOMIC_ADD(unsigned int,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_ADD(unsigned long long,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_ADD(float,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_ADD(double,MemoryOrderRelaxed,MemoryScopeDevice);
+
+  #define DESUL_IMPL_CUDA_HOST_ATOMIC_SUB(TYPE,ORDER,SCOPE) \
+    inline void atomic_sub(TYPE* const dest, TYPE val, ORDER order, SCOPE scope) { \
+    (void) atomic_fetch_sub(dest, val, order, scope); \
+  }
+  DESUL_IMPL_CUDA_HOST_ATOMIC_SUB(int32_t,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_SUB(long,MemoryOrderRelaxed,MemoryScopeDevice); // only for ASM?
+  DESUL_IMPL_CUDA_HOST_ATOMIC_SUB(unsigned int,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_SUB(float,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_SUB(double,MemoryOrderRelaxed,MemoryScopeDevice);
+
+  #define DESUL_IMPL_CUDA_HOST_ATOMIC_INC(TYPE,ORDER,SCOPE) \
+    inline void atomic_inc(TYPE* const dest, ORDER order, SCOPE scope) { \
+    (void) atomic_fetch_inc(dest, order, scope); \
+  }
+  DESUL_IMPL_CUDA_HOST_ATOMIC_INC(unsigned int,MemoryOrderRelaxed,MemoryScopeDevice); // only for ASM?
+
+  #define DESUL_IMPL_CUDA_HOST_ATOMIC_DEC(TYPE,ORDER,SCOPE) \
+    inline void atomic_dec(TYPE* const dest, ORDER order, SCOPE scope) { \
+    (void) atomic_fetch_dec(dest, order, scope); \
+  }
+  DESUL_IMPL_CUDA_HOST_ATOMIC_DEC(unsigned,MemoryOrderRelaxed,MemoryScopeDevice); // only for ASM?
+#endif // DESUL_HAVE_CUDA_ATOMICS_ASM
+
+  #define DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_ADD(TYPE,ORDER,SCOPE) \
+    inline TYPE atomic_fetch_add(TYPE* const dest, TYPE val, ORDER order, SCOPE scope) { \
+      return Impl::atomic_fetch_oper(Impl::AddOper<TYPE, const TYPE>(),dest, val, order, scope); \
+  }
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_ADD(float,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_ADD(double,MemoryOrderRelaxed,MemoryScopeDevice);
+
+  #define DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_SUB(TYPE,ORDER,SCOPE) \
+    inline TYPE atomic_fetch_sub(TYPE* const dest, TYPE val, ORDER order, SCOPE scope) { \
+      return Impl::atomic_fetch_oper(Impl::SubOper<TYPE, const TYPE>(),dest, val, order, scope); \
+  }
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_SUB(float,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_SUB(double,MemoryOrderRelaxed,MemoryScopeDevice);
+
+
+  #define DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MAX(TYPE,ORDER,SCOPE) \
+    inline TYPE atomic_fetch_max(TYPE* const dest, TYPE val, ORDER order, SCOPE scope) { \
+      return Impl::atomic_fetch_oper(Impl::MaxOper<TYPE, const TYPE>(), dest, val, order, scope); \
+  }
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MAX(int,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MAX(long,MemoryOrderRelaxed,MemoryScopeDevice); // only for ASM?
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MAX(unsigned int,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MAX(unsigned long,MemoryOrderRelaxed,MemoryScopeDevice);
+//  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MAX(unsigned long long,MemoryOrderRelaxed,MemoryScopeDevice);
+
+  #define DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MIN(TYPE,ORDER,SCOPE) \
+    inline TYPE atomic_fetch_min(TYPE* const dest, TYPE val, ORDER order, SCOPE scope) { \
+      return Impl::atomic_fetch_oper(Impl::MinOper<TYPE, const TYPE>(), dest, val, order, scope); \
+  }
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MIN(int,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MIN(long,MemoryOrderRelaxed,MemoryScopeDevice); // only for ASM?
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MIN(unsigned int,MemoryOrderRelaxed,MemoryScopeDevice);
+  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MIN(unsigned long,MemoryOrderRelaxed,MemoryScopeDevice);
+//  DESUL_IMPL_CUDA_HOST_ATOMIC_FETCH_MIN(unsigned long long,MemoryOrderRelaxed,MemoryScopeDevice);
+//  inline void atomic_fetch_max(int32_t* const dest, int32_t val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+
+}
+
+// Functions defined int the GCC overload set but not in the device overload set
+namespace desul {
+  __device__ inline
+  unsigned long long atomic_fetch_add(unsigned long long* const dest, unsigned long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::AddOper<unsigned long long, const unsigned long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_fetch_add(long long* const dest, long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::AddOper<long long, const long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_fetch_add(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::AddOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_fetch_sub(long long* const dest, long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::SubOper<long long, const long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_fetch_sub(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::SubOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_fetch_max(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::MaxOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_fetch_min(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::MinOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_fetch_or(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::OrOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_fetch_or(long long* const dest, long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::OrOper<long long, const long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_fetch_xor(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::XorOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_fetch_xor(long long* const dest, long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::XorOper<long long, const long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_fetch_and(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::AndOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_fetch_and(long long* const dest, long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_fetch_oper(Impl::AndOper<long long, const long long>(), dest, val, order, scope);
+  }
+
+
+  __device__ inline
+  unsigned long long atomic_add_fetch(unsigned long long* const dest, unsigned long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::AddOper<unsigned long long, const unsigned long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_add_fetch(long long* const dest, long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::AddOper<long long, const long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_add_fetch(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::AddOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_sub_fetch(long long* const dest, long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::SubOper<long long, const long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_sub_fetch(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::SubOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_or_fetch(long long* const dest, long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::OrOper<long long, const long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_or_fetch(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::OrOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_xor_fetch(long long* const dest, long long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::XorOper<long long, const long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_xor_fetch(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::XorOper<long, const long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long long atomic_and_fetch(long long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::AndOper<long long, const long long>(), dest, val, order, scope);
+  }
+  __device__ inline
+  long atomic_and_fetch(long* const dest, long val, MemoryOrderRelaxed order, MemoryScopeDevice scope) {
+    return Impl::atomic_oper_fetch(Impl::AndOper<long, const long>(), dest, val, order, scope);
+  }
+}
+#endif
+#endif  // DESUL_HAVE_CUDA_ATOMICS
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Common.hpp b/packages/kokkos/core/src/desul/atomics/Common.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f1dccc6c52318f58b6fb1ed792ed614a8351458c
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Common.hpp
@@ -0,0 +1,199 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_COMMON_HPP_
+#define DESUL_ATOMICS_COMMON_HPP_
+#include "desul/atomics/Macros.hpp"
+#include <cstdint>
+#include <atomic>
+#include <type_traits>
+
+namespace desul {
+struct alignas(16) Dummy16ByteValue {
+  int64_t value1;
+  int64_t value2;
+  bool operator!=(Dummy16ByteValue v) const {
+    return (value1 != v.value1) || (value2 != v.value2);
+  }
+  bool operator==(Dummy16ByteValue v) const {
+    return (value1 == v.value1) && (value2 == v.value2);
+  }
+};
+}  // namespace desul
+
+// MemoryOrder Tags
+
+namespace desul {
+// Memory order sequential consistent
+struct MemoryOrderSeqCst {};
+// Memory order acquire release
+struct MemoryOrderAcqRel {};
+// Memory order acquire
+struct MemoryOrderAcquire {};
+// Memory order release
+struct MemoryOrderRelease {};
+// Memory order relaxed
+struct MemoryOrderRelaxed {};
+}  // namespace desul
+
+// Memory Scope Tags
+
+namespace desul {
+// Entire machine scope (e.g. for global arrays)
+struct MemoryScopeSystem {};
+// Node level
+struct MemoryScopeNode {};
+// Device or socket scope (i.e. a CPU socket, a single GPU)
+struct MemoryScopeDevice {};
+// Core scoped (i.e. a shared Level 1 cache)
+struct MemoryScopeCore {};
+}  // namespace desul
+
+#ifndef __ATOMIC_RELAXED
+#define __ATOMIC_RELAXED 0
+#define __ATOMIC_CONSUME 1
+#define __ATOMIC_ACQUIRE 2
+#define __ATOMIC_RELEASE 3
+#define __ATOMIC_ACQ_REL 4
+#define __ATOMIC_SEQ_CST 5
+#endif
+
+namespace desul {
+template <class MemoryOrderDesul>
+struct GCCMemoryOrder;
+
+template <>
+struct GCCMemoryOrder<MemoryOrderRelaxed> {
+  static constexpr int value = __ATOMIC_RELAXED;
+};
+
+template <>
+struct GCCMemoryOrder<MemoryOrderAcquire> {
+  static constexpr int value = __ATOMIC_ACQUIRE;
+};
+
+template <>
+struct GCCMemoryOrder<MemoryOrderRelease> {
+  static constexpr int value = __ATOMIC_RELEASE;
+};
+
+template <>
+struct GCCMemoryOrder<MemoryOrderAcqRel> {
+  static constexpr int value = __ATOMIC_ACQ_REL;
+};
+
+template <>
+struct GCCMemoryOrder<MemoryOrderSeqCst> {
+  static constexpr int value = __ATOMIC_SEQ_CST;
+};
+
+template <class MemoryOrderDesul>
+struct CXXMemoryOrder;
+
+template <>
+struct CXXMemoryOrder<MemoryOrderRelaxed> {
+  static constexpr std::memory_order value = std::memory_order_relaxed;
+};
+
+template <>
+struct CXXMemoryOrder<MemoryOrderAcquire> {
+  static constexpr std::memory_order value = std::memory_order_acquire;
+};
+
+template <>
+struct CXXMemoryOrder<MemoryOrderRelease> {
+  static constexpr std::memory_order value = std::memory_order_release;
+};
+
+template <>
+struct CXXMemoryOrder<MemoryOrderAcqRel> {
+  static constexpr std::memory_order value = std::memory_order_acq_rel;
+};
+
+template <>
+struct CXXMemoryOrder<MemoryOrderSeqCst> {
+  static constexpr std::memory_order value = std::memory_order_seq_cst;
+};
+
+namespace Impl {
+template <typename MemoryOrder>
+struct CmpExchFailureOrder {
+  using memory_order = std::conditional_t<
+      std::is_same<MemoryOrder, MemoryOrderAcqRel>{},
+      MemoryOrderAcquire,
+      std::conditional_t<std::is_same<MemoryOrder, MemoryOrderRelease>{},
+                         MemoryOrderRelaxed,
+                         MemoryOrder>>;
+};
+template <typename MemoryOrder>
+using cmpexch_failure_memory_order =
+    typename CmpExchFailureOrder<MemoryOrder>::memory_order;
+}  // namespace Impl
+
+}
+
+// We should in principle use std::numeric_limits, but that requires constexpr function support on device
+// Currently that is still considered experimetal on CUDA and sometimes not reliable.
+namespace desul {
+namespace Impl {
+template<class T>
+struct numeric_limits_max;
+
+template<>
+struct numeric_limits_max<uint32_t> {
+  static constexpr uint32_t value = 0xffffffffu;
+};
+template<>
+struct numeric_limits_max<uint64_t> {
+  static constexpr uint64_t value = 0xfffffffflu;
+};
+
+constexpr bool atomic_always_lock_free(std::size_t size) {
+  return size == 4 || size == 8
+#if defined(DESUL_HAVE_16BYTE_COMPARE_AND_SWAP)
+         || size == 16
+#endif
+      ;
+}
+
+template <std::size_t Size, std::size_t Align>
+DESUL_INLINE_FUNCTION bool atomic_is_lock_free() noexcept {
+  return Size == 4 || Size == 8
+#if defined(DESUL_HAVE_16BYTE_COMPARE_AND_SWAP)
+         || Size == 16
+#endif
+      ;
+}
+
+template<std::size_t N>
+struct atomic_compare_exchange_type;
+
+template<>
+struct atomic_compare_exchange_type<4> {
+  using type = int32_t;
+};
+
+template<>
+struct atomic_compare_exchange_type<8> {
+  using type = int64_t;
+};
+
+template<>
+struct atomic_compare_exchange_type<16> {
+  using type = Dummy16ByteValue;
+};
+
+template<class T>
+struct dont_deduce_this_parameter { using type = T; };
+
+template<class T>
+using dont_deduce_this_parameter_t = typename dont_deduce_this_parameter<T>::type;
+
+}
+}
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Compare_Exchange.hpp b/packages/kokkos/core/src/desul/atomics/Compare_Exchange.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b8289d75b8e70a1097207418a5a0f435913cded
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Compare_Exchange.hpp
@@ -0,0 +1,35 @@
+/*
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_COMPARE_EXCHANGE_HPP_
+#define DESUL_ATOMICS_COMPARE_EXCHANGE_HPP_
+
+#include "desul/atomics/Macros.hpp"
+
+#ifdef DESUL_HAVE_GCC_ATOMICS
+#include "desul/atomics/Compare_Exchange_GCC.hpp"
+#endif
+#ifdef DESUL_HAVE_MSVC_ATOMICS
+#include "desul/atomics/Compare_Exchange_MSVC.hpp"
+#endif
+#ifdef DESUL_HAVE_SERIAL_ATOMICS
+#include "desul/atomics/Compare_Exchange_Serial.hpp"
+#endif
+#ifdef DESUL_HAVE_CUDA_ATOMICS
+#include "desul/atomics/Compare_Exchange_CUDA.hpp"
+#endif
+#ifdef DESUL_HAVE_HIP_ATOMICS
+#include "desul/atomics/Compare_Exchange_HIP.hpp"
+#endif
+#ifdef DESUL_HAVE_OPENMP_ATOMICS
+#include "desul/atomics/Compare_Exchange_OpenMP.hpp"
+#endif
+#ifdef DESUL_HAVE_SYCL_ATOMICS
+#include "desul/atomics/Compare_Exchange_SYCL.hpp"
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Compare_Exchange_CUDA.hpp b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_CUDA.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..aab0d943eb659f9c0f860fef1293753bcf5c52be
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_CUDA.hpp
@@ -0,0 +1,267 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_COMPARE_EXCHANGE_CUDA_HPP_
+#define DESUL_ATOMICS_COMPARE_EXCHANGE_CUDA_HPP_
+#include "desul/atomics/Common.hpp"
+#include "desul/atomics/Lock_Array_Cuda.hpp"
+
+#ifdef DESUL_HAVE_CUDA_ATOMICS
+namespace desul {
+// Only include if compiling device code, or the CUDA compiler is not NVCC (i.e. Clang)
+// atomic_thread_fence implementation
+#if defined(__CUDA_ARCH__) || !defined(__NVCC__)
+__device__ inline void atomic_thread_fence(MemoryOrderRelease, MemoryScopeDevice) {
+  __threadfence();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeDevice) {
+  __threadfence();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeDevice) {
+  __threadfence();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeDevice) {
+  __threadfence();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderRelease, MemoryScopeCore) {
+  __threadfence_block();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeCore) {
+  __threadfence_block();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeCore) {
+  __threadfence_block();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeCore) {
+  __threadfence_block();
+}
+#if (__CUDA_ARCH__>=600) || !defined(__NVCC__)
+__device__ inline void atomic_thread_fence(MemoryOrderRelease, MemoryScopeNode) {
+  __threadfence_system();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeNode) {
+  __threadfence_system();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeNode) {
+  __threadfence_system();
+}
+__device__ inline void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeNode) {
+  __threadfence_system();
+}
+#endif
+#endif
+}
+
+// Compare Exchange for PRE Volta, not supported with CLANG as CUDA compiler, since we do NOT have a way
+// of having the code included for clang only when the CC is smaller than 700
+// But on Clang the device side symbol list must be independent of __CUDA_ARCH__
+#if defined(__CUDA_ARCH__) && (__CUDA_ARCH__ < 700) || \
+(!defined(__NVCC__) && (defined(KOKKOS_ENABLE_KEPLER) || defined(KOKKOS_ENABLE_MAXWELL) || defined(KOKKOS_ENABLE_PASCAL)))
+namespace desul {
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderRelaxed, MemoryScope) {
+  static_assert(sizeof(unsigned int) == 4, "this function assumes an unsigned int is 32-bit");
+  unsigned int return_val = atomicCAS(reinterpret_cast<unsigned int*>(dest),
+                                      reinterpret_cast<unsigned int&>(compare),
+                                      reinterpret_cast<unsigned int&>(value));
+  return reinterpret_cast<T&>(return_val);
+}
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderRelaxed, MemoryScope) {
+  static_assert(sizeof(unsigned long long int) == 8, "this function assumes an unsigned long long  is 64-bit");
+  unsigned long long int return_val =
+      atomicCAS(reinterpret_cast<unsigned long long int*>(dest),
+                reinterpret_cast<unsigned long long int&>(compare),
+                reinterpret_cast<unsigned long long int&>(value));
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderRelease, MemoryScope) {
+  T return_val = atomic_compare_exchange(dest, compare, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(),MemoryScope());
+  return return_val;
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderAcquire, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(),MemoryScope());
+  T return_val = atomic_compare_exchange(dest, compare, value, MemoryOrderRelaxed(), MemoryScope());
+  return return_val;
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderAcqRel, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(),MemoryScope());
+  T return_val = atomic_compare_exchange(dest, compare, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(),MemoryScope());
+  return return_val;
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrderRelaxed, MemoryScope) {
+  static_assert(sizeof(unsigned int) == 4, "this function assumes an unsigned int is 32-bit");
+  unsigned int return_val = atomicExch(reinterpret_cast<unsigned int*>(dest),
+                                       reinterpret_cast<unsigned int&>(value));
+  return reinterpret_cast<T&>(return_val);
+}
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 8, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrderRelaxed, MemoryScope) {
+  static_assert(sizeof(unsigned long long int) == 8, "this function assumes an unsigned long long  is 64-bit");
+  unsigned long long int return_val =
+      atomicExch(reinterpret_cast<unsigned long long int*>(dest),
+                 reinterpret_cast<unsigned long long int&>(value));
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrderRelease, MemoryScope) {
+  T return_val = atomic_exchange(dest, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(),MemoryScope());
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrderAcquire, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(),MemoryScope());
+  T return_val = atomic_exchange(dest, value, MemoryOrderRelaxed(), MemoryScope());
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrderAcqRel, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(),MemoryScope());
+  T return_val = atomic_exchange(dest, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(),MemoryScope());
+  return reinterpret_cast<T&>(return_val);
+}
+}  // namespace desul
+#endif
+
+// Including CUDA ptx based exchange atomics
+// When building with clang we need to include the device functions always
+// since clang must see a consistent overload set in both device and host compilation
+// but that means we need to know on the host what to make visible, i.e. we need
+// a host side compile knowledge of architecture.
+// We simply can say DESUL proper doesn't support clang CUDA build pre Volta,
+// Kokkos has that knowledge and so I use it here, allowing in Kokkos to use
+// clang with pre Volta as CUDA compiler
+#if (defined(__CUDA_ARCH__) && (__CUDA_ARCH__>=700)) || \
+     (!defined(__NVCC__) && !defined(KOKKOS_ARCH_KEPLER) && !defined(KOKKOS_ARCH_MAXWELL) && !defined(KOKKOS_ARCH_PASCAL))
+#include <desul/atomics/cuda/CUDA_asm_exchange.hpp>
+#endif
+
+// SeqCst is not directly supported by PTX, need the additional fences:
+
+#if defined(__CUDA_ARCH__) || !defined(__NVCC__)
+namespace desul {
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrderSeqCst, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(),MemoryScope());
+  T return_val = atomic_exchange(dest,value,MemoryOrderRelaxed(),MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(),MemoryScope());
+  return return_val;
+}
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 8, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrderSeqCst, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(),MemoryScope());
+  T return_val = atomic_exchange(dest,value,MemoryOrderRelaxed(),MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(),MemoryScope());
+  return return_val;
+}
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderSeqCst, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(),MemoryScope());
+  T return_val = atomic_compare_exchange(dest,compare,value,MemoryOrderRelaxed(),MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(),MemoryScope());
+  return return_val;
+}
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderSeqCst, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(),MemoryScope());
+  T return_val = atomic_compare_exchange(dest,compare,value,MemoryOrderRelaxed(),MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(),MemoryScope());
+  return return_val;
+}
+}
+#endif
+
+#if defined(__CUDA_ARCH__) || !defined(__NVCC__)
+namespace desul {
+template <typename T, class MemoryOrder, class MemoryScope>
+__device__ typename std::enable_if<(sizeof(T) != 8) && (sizeof(T) != 4), T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrder, MemoryScope scope) {
+  // This is a way to avoid dead lock in a warp or wave front
+  T return_val;
+  int done = 0;
+  unsigned int mask = DESUL_IMPL_ACTIVEMASK;
+  unsigned int active = DESUL_IMPL_BALLOT_MASK(mask, 1);
+  unsigned int done_active = 0;
+  while (active != done_active) {
+    if (!done) {
+      if (Impl::lock_address_cuda((void*)dest, scope)) {
+        if(std::is_same<MemoryOrder,MemoryOrderSeqCst>::value) atomic_thread_fence(MemoryOrderRelease(),scope);
+        atomic_thread_fence(MemoryOrderAcquire(),scope);
+        return_val = *dest;
+        if(return_val == compare) {
+          *dest = value;
+          atomic_thread_fence(MemoryOrderRelease(),scope);
+        }
+        Impl::unlock_address_cuda((void*)dest, scope);
+        done = 1;
+      }
+    }
+    done_active = DESUL_IMPL_BALLOT_MASK(mask, done);
+  }
+  return return_val;
+}
+template <typename T, class MemoryOrder, class MemoryScope>
+__device__ typename std::enable_if<(sizeof(T) != 8) && (sizeof(T) != 4), T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrder, MemoryScope scope) {
+  // This is a way to avoid dead lock in a warp or wave front
+  T return_val;
+  int done = 0;
+  unsigned int mask = DESUL_IMPL_ACTIVEMASK;
+  unsigned int active = DESUL_IMPL_BALLOT_MASK(mask, 1);
+  unsigned int done_active = 0;
+  while (active != done_active) {
+    if (!done) {
+      if (Impl::lock_address_cuda((void*)dest, scope)) {
+        if(std::is_same<MemoryOrder,MemoryOrderSeqCst>::value) atomic_thread_fence(MemoryOrderRelease(),scope);
+        atomic_thread_fence(MemoryOrderAcquire(),scope);
+        return_val = *dest;
+        *dest = value;
+        atomic_thread_fence(MemoryOrderRelease(),scope);
+        Impl::unlock_address_cuda((void*)dest, scope);
+        done = 1;
+      }
+    }
+    done_active = DESUL_IMPL_BALLOT_MASK(mask, done);
+  }
+  return return_val;
+}
+}
+#endif
+
+
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Compare_Exchange_GCC.hpp b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_GCC.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..418bea0b8b72f42883cc582bd58a5a170f738fea
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_GCC.hpp
@@ -0,0 +1,91 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_COMPARE_EXCHANGE_GCC_HPP_
+#define DESUL_ATOMICS_COMPARE_EXCHANGE_GCC_HPP_
+#include "desul/atomics/Common.hpp"
+
+#ifdef DESUL_HAVE_GCC_ATOMICS
+#if !defined(DESUL_HAVE_16BYTE_COMPARE_AND_SWAP) && !defined(__CUDACC__)
+// This doesn't work in WSL??
+//#define DESUL_HAVE_16BYTE_COMPARE_AND_SWAP
+#endif
+namespace desul {
+
+namespace Impl {
+template<class T>
+struct atomic_exchange_available_gcc {
+  constexpr static bool value =
+#ifndef DESUL_HAVE_LIBATOMIC
+    ((sizeof(T)==4 && alignof(T)==4) ||
+#ifdef DESUL_HAVE_16BYTE_COMPARE_AND_SWAP
+     (sizeof(T)==16 && alignof(T)==16) ||
+#endif
+     (sizeof(T)==8 && alignof(T)==8)) &&
+#endif
+    std::is_trivially_copyable<T>::value;
+};
+} //namespace Impl
+
+#if defined(__clang__) && (__clang_major__>=7) && !defined(__APPLE__)
+// Disable warning for large atomics on clang 7 and up (checked with godbolt)
+// error: large atomic operation may incur significant performance penalty [-Werror,-Watomic-alignment]
+// https://godbolt.org/z/G7YhqhbG6
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Watomic-alignment"
+#endif
+template<class MemoryOrder, class MemoryScope>
+void atomic_thread_fence(MemoryOrder, MemoryScope) {
+  __atomic_thread_fence(GCCMemoryOrder<MemoryOrder>::value);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+std::enable_if_t<Impl::atomic_exchange_available_gcc<T>::value, T>
+atomic_exchange(
+    T* dest, T value, MemoryOrder, MemoryScope) {
+  T return_val;
+  __atomic_exchange(
+     dest, &value, &return_val, GCCMemoryOrder<MemoryOrder>::value);
+  return return_val;
+}
+
+// Failure mode for atomic_compare_exchange_n cannot be RELEASE nor ACQREL so
+// Those two get handled separatly.
+template <typename T, class MemoryOrder, class MemoryScope>
+std::enable_if_t<Impl::atomic_exchange_available_gcc<T>::value, T>
+atomic_compare_exchange(
+    T* dest, T compare, T value, MemoryOrder, MemoryScope) {
+  (void)__atomic_compare_exchange(
+      dest, &compare, &value, false, GCCMemoryOrder<MemoryOrder>::value, GCCMemoryOrder<MemoryOrder>::value);
+  return compare;
+}
+
+template <typename T, class MemoryScope>
+std::enable_if_t<Impl::atomic_exchange_available_gcc<T>::value, T>
+atomic_compare_exchange(
+    T* dest, T compare, T value, MemoryOrderRelease, MemoryScope) {
+  (void)__atomic_compare_exchange(
+      dest, &compare, &value, false, __ATOMIC_RELEASE, __ATOMIC_RELAXED);
+  return compare;
+}
+
+template <typename T, class MemoryScope>
+std::enable_if_t<Impl::atomic_exchange_available_gcc<T>::value, T>
+atomic_compare_exchange(
+    T* dest, T compare, T value, MemoryOrderAcqRel, MemoryScope) {
+  (void)__atomic_compare_exchange(
+      dest, &compare, &value, false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE);
+  return compare;
+}
+
+#if defined(__clang__) && (__clang_major__>=7) && !defined(__APPLE__)
+#pragma GCC diagnostic pop
+#endif
+}  // namespace desul
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Compare_Exchange_HIP.hpp b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_HIP.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d6bf04a7e6d25449934f4813c936bf37ce9bb07b
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_HIP.hpp
@@ -0,0 +1,253 @@
+/*
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_COMPARE_EXCHANGE_HIP_HPP_
+#define DESUL_ATOMICS_COMPARE_EXCHANGE_HIP_HPP_
+#include "desul/atomics/Common.hpp"
+#include "desul/atomics/Lock_Array_HIP.hpp"
+
+#ifdef DESUL_HAVE_HIP_ATOMICS
+namespace desul {
+#if defined(__HIP_DEVICE_COMPILE__)
+inline __device__ void atomic_thread_fence(MemoryOrderRelease, MemoryScopeDevice) {
+  __threadfence();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeDevice) {
+  __threadfence();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeDevice) {
+  __threadfence();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeDevice) {
+  __threadfence();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderRelease, MemoryScopeCore) {
+  __threadfence_block();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeCore) {
+  __threadfence_block();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeCore) {
+  __threadfence_block();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeCore) {
+  __threadfence_block();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderRelease, MemoryScopeNode) {
+  __threadfence_system();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeNode) {
+  __threadfence_system();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeNode) {
+  __threadfence_system();
+}
+
+inline __device__ void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeNode) {
+  __threadfence_system();
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderRelaxed, MemoryScope) {
+  static_assert(sizeof(unsigned int) == 4,
+                "this function assumes an unsigned int is 32-bit");
+  unsigned int return_val = atomicCAS(reinterpret_cast<unsigned int*>(dest),
+                                      reinterpret_cast<unsigned int&>(compare),
+                                      reinterpret_cast<unsigned int&>(value));
+  return reinterpret_cast<T&>(return_val);
+}
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderRelaxed, MemoryScope) {
+  static_assert(sizeof(unsigned long long int) == 8,
+                "this function assumes an unsigned long long  is 64-bit");
+  unsigned long long int return_val =
+      atomicCAS(reinterpret_cast<unsigned long long int*>(dest),
+                reinterpret_cast<unsigned long long int&>(compare),
+                reinterpret_cast<unsigned long long int&>(value));
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type
+atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderRelease, MemoryScope) {
+  T return_val = atomic_compare_exchange(
+      dest, compare, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(), MemoryScope());
+  return return_val;
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type
+atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderAcquire, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(), MemoryScope());
+  T return_val = atomic_compare_exchange(
+      dest, compare, value, MemoryOrderRelaxed(), MemoryScope());
+  return return_val;
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type
+atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderAcqRel, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(), MemoryScope());
+  T return_val = atomic_compare_exchange(
+      dest, compare, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(), MemoryScope());
+  return return_val;
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrderRelaxed, MemoryScope) {
+  static_assert(sizeof(unsigned int) == 4,
+                "this function assumes an unsigned int is 32-bit");
+  unsigned int return_val = atomicExch(reinterpret_cast<unsigned int*>(dest),
+                                       reinterpret_cast<unsigned int&>(value));
+  return reinterpret_cast<T&>(return_val);
+}
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 8, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrderRelaxed, MemoryScope) {
+  static_assert(sizeof(unsigned long long int) == 8,
+                "this function assumes an unsigned long long  is 64-bit");
+  unsigned long long int return_val =
+      atomicExch(reinterpret_cast<unsigned long long int*>(dest),
+                 reinterpret_cast<unsigned long long int&>(value));
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type
+atomic_exchange(T* const dest, T compare, T value, MemoryOrderRelease, MemoryScope) {
+  T return_val = atomic_compare_exchange(
+      dest, compare, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(), MemoryScope());
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type
+atomic_exchange(
+    T* const dest, T /*compare*/, T value, MemoryOrderAcquire, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(), MemoryScope());
+  T return_val = atomic_exchange(dest, value, MemoryOrderRelaxed(), MemoryScope());
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type
+atomic_exchange(T* const dest, T value, MemoryOrderAcqRel, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(), MemoryScope());
+  T return_val = atomic_exchange(dest, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(), MemoryScope());
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4 || sizeof(T) == 8, T>::type
+atomic_exchange(T* const dest, T value, MemoryOrderSeqCst, MemoryScope) {
+          atomic_thread_fence(MemoryOrderAcquire(), MemoryScope());
+            T return_val = atomic_exchange(dest, value, MemoryOrderRelaxed(), MemoryScope());
+              atomic_thread_fence(MemoryOrderRelease(), MemoryScope());
+                return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 4, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderSeqCst, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(), MemoryScope());
+  T return_val = atomic_compare_exchange(
+      dest, compare, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(), MemoryScope());
+  return return_val;
+}
+
+template <typename T, class MemoryScope>
+__device__ typename std::enable_if<sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderSeqCst, MemoryScope) {
+  atomic_thread_fence(MemoryOrderAcquire(), MemoryScope());
+  T return_val = atomic_compare_exchange(
+      dest, compare, value, MemoryOrderRelaxed(), MemoryScope());
+  atomic_thread_fence(MemoryOrderRelease(), MemoryScope());
+  return return_val;
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION __device__
+    typename std::enable_if<(sizeof(T) != 8) && (sizeof(T) != 4), T>::type
+    atomic_compare_exchange(
+        T* const dest, T compare, T value, MemoryOrder, MemoryScope scope) {
+  // This is a way to avoid dead lock in a warp or wave front
+  T return_val;
+  int done = 0;
+  unsigned long long int active = DESUL_IMPL_BALLOT_MASK(1);
+  unsigned long long int done_active = 0;
+  while (active != done_active) {
+    if (!done) {
+      if (Impl::lock_address_hip((void*)dest, scope)) {
+        if (std::is_same<MemoryOrder, MemoryOrderSeqCst>::value)
+          atomic_thread_fence(MemoryOrderRelease(), scope);
+        atomic_thread_fence(MemoryOrderAcquire(), scope);
+        return_val = *dest;
+        if (return_val == compare) {
+          *dest = value;
+          atomic_thread_fence(MemoryOrderRelease(), scope);
+        }
+        Impl::unlock_address_hip((void*)dest, scope);
+        done = 1;
+      }
+    }
+    done_active = DESUL_IMPL_BALLOT_MASK(done);
+  }
+  return return_val;
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION __device__
+    typename std::enable_if<(sizeof(T) != 8) && (sizeof(T) != 4), T>::type
+    atomic_exchange(T* const dest, T value, MemoryOrder, MemoryScope scope) {
+  // This is a way to avoid dead lock in a warp or wave front
+  T return_val;
+  int done = 0;
+  unsigned long long int active = DESUL_IMPL_BALLOT_MASK(1);
+  unsigned long long int done_active = 0;
+  while (active != done_active) {
+    if (!done) {
+      if (Impl::lock_address_hip((void*)dest, scope)) {
+        if (std::is_same<MemoryOrder, MemoryOrderSeqCst>::value)
+          atomic_thread_fence(MemoryOrderRelease(), scope);
+        atomic_thread_fence(MemoryOrderAcquire(), scope);
+        return_val = *dest;
+        *dest = value;
+        atomic_thread_fence(MemoryOrderRelease(), scope);
+        Impl::unlock_address_hip((void*)dest, scope);
+        done = 1;
+      }
+    }
+    done_active = DESUL_IMPL_BALLOT_MASK(done);
+  }
+  return return_val;
+}
+#endif
+}  // namespace desul
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Compare_Exchange_MSVC.hpp b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_MSVC.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c96cb031714f63b5039ade535077c7511838ffbd
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_MSVC.hpp
@@ -0,0 +1,201 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_COMPARE_EXCHANGE_MSVC_HPP_
+#define DESUL_ATOMICS_COMPARE_EXCHANGE_MSVC_HPP_
+#include "desul/atomics/Common.hpp"
+#include <type_traits>
+#ifdef DESUL_HAVE_MSVC_ATOMICS
+
+#ifndef DESUL_HAVE_16BYTE_COMPARE_AND_SWAP
+#define DESUL_HAVE_16BYTE_COMPARE_AND_SWAP
+#endif
+
+namespace desul {
+
+template<class T, class MemoryOrder, class MemoryScope>
+T atomic_exchange(T* const, T val, MemoryOrder, MemoryScope) { return val;}
+
+
+template<class MemoryOrder, class MemoryScope>
+void atomic_thread_fence(MemoryOrder, MemoryScope) {
+  std::atomic_thread_fence(CXXMemoryOrder<MemoryOrder>::value);
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 1, T>::type atomic_exchange(
+    T* const dest, T val, MemoryOrderRelaxed, MemoryScope) {
+  char return_val =
+      _InterlockedExchange8((char*)dest, *((char*)&val));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 2, T>::type atomic_exchange(
+    T* const dest, T val, MemoryOrderRelaxed, MemoryScope) {
+  short return_val =
+      _InterlockedExchange16((short*)dest, *((short*)&val));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 4, T>::type atomic_exchange(
+    T* const dest, T val, MemoryOrderRelaxed, MemoryScope) {
+  long return_val =
+      _InterlockedExchange((long*)dest, *((long*)&val));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 8, T>::type atomic_exchange(
+    T* const dest, T val, MemoryOrderRelaxed, MemoryScope) {
+  __int64 return_val = _InterlockedExchange64(
+      (__int64*)dest, *((__int64*)&val));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 1, T>::type atomic_exchange(
+    T* const dest, T val, MemoryOrderSeqCst, MemoryScope) {
+  char return_val =
+      _InterlockedExchange8((char*)dest, *((char*)&val));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 2, T>::type atomic_exchange(
+    T* const dest, T val, MemoryOrderSeqCst, MemoryScope) {
+  short return_val =
+      _InterlockedExchange16((short*)dest, *((short*)&val));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 4, T>::type atomic_exchange(
+    T* const dest, T val, MemoryOrderSeqCst, MemoryScope) {
+  long return_val =
+      _InterlockedExchange((long*)dest, *((long*)&val));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 8, T>::type atomic_exchange(
+    T* const dest, T val, MemoryOrderSeqCst, MemoryScope) {
+  __int64 return_val = _InterlockedExchange64(
+      (__int64*)dest, *((__int64*)&val));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 1, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderRelaxed, MemoryScope) {
+  char return_val =
+      _InterlockedCompareExchange8((char*)dest, *((char*)&val), *((char*)&compare));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 2, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderRelaxed, MemoryScope) {
+  short return_val =
+      _InterlockedCompareExchange16((short*)dest, *((short*)&val), *((short*)&compare));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 4, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderRelaxed, MemoryScope) {
+  long return_val =
+      _InterlockedCompareExchange((long*)dest, *((long*)&val), *((long*)&compare));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderRelaxed, MemoryScope) {
+  __int64 return_val = _InterlockedCompareExchange64(
+      (__int64*)dest, *((__int64*)&val), *((__int64*)&compare));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 16, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderRelaxed, MemoryScope) {
+  Dummy16ByteValue* val16 = reinterpret_cast<Dummy16ByteValue*>(&val);
+  (void)_InterlockedCompareExchange128(reinterpret_cast<__int64*>(dest),
+                                       val16->value2,
+                                       val16->value1,
+                                       (reinterpret_cast<__int64*>(&compare)));
+  return compare;
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 1, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderSeqCst, MemoryScope) {
+  char return_val =
+      _InterlockedCompareExchange8((char*)dest, *((char*)&val), *((char*)&compare));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 2, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderSeqCst, MemoryScope) {
+  short return_val =
+      _InterlockedCompareExchange16((short*)dest, *((short*)&val), *((short*)&compare));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 4, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderSeqCst, MemoryScope) {
+  long return_val =
+      _InterlockedCompareExchange((long*)dest, *((long*)&val), *((long*)&compare));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderSeqCst, MemoryScope) {
+  __int64 return_val = _InterlockedCompareExchange64(
+      (__int64*)dest, *((__int64*)&val), *((__int64*)&compare));
+  return *(reinterpret_cast<T*>(&return_val));
+}
+
+template <typename T, class MemoryScope>
+typename std::enable_if<sizeof(T) == 16, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T val, MemoryOrderSeqCst, MemoryScope) {
+  Dummy16ByteValue* val16 = reinterpret_cast<Dummy16ByteValue*>(&val);
+  (void)_InterlockedCompareExchange128(reinterpret_cast<__int64*>(dest),
+                                       val16->value2,
+                                       val16->value1,
+                                       (reinterpret_cast<__int64*>(&compare)));
+  return compare;
+}
+
+
+template <typename T, class MemoryOrder, class MemoryScope>
+typename std::enable_if<(sizeof(T) != 1 && sizeof(T) != 4 && sizeof(T) != 8 && sizeof(T) != 16), T>::type atomic_compare_exchange(
+     T* const dest, T compare, T val, MemoryOrder, MemoryScope scope) {
+  while (!Impl::lock_address((void*)dest, scope)) {}
+  if (std::is_same<MemoryOrder, MemoryOrderSeqCst>::value)
+          atomic_thread_fence(MemoryOrderRelease(), scope);
+  atomic_thread_fence(MemoryOrderAcquire(),scope);
+  T return_val = *dest;
+  if(return_val == compare) {
+    *dest = val;
+    atomic_thread_fence(MemoryOrderRelease(),scope);
+  }
+
+  Impl::unlock_address((void*)dest, scope);
+  return return_val;
+}
+
+}  // namespace desul
+
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Compare_Exchange_OpenMP.hpp b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_OpenMP.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a1d1c9124991d01640ca70243e9033e4c528e6cf
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_OpenMP.hpp
@@ -0,0 +1,145 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+#ifndef DESUL_ATOMICS_COMPARE_EXCHANGE_OPENMP_HPP_
+#define DESUL_ATOMICS_COMPARE_EXCHANGE_OPENMP_HPP_
+#include "desul/atomics/Common.hpp"
+#include <cstdio>
+#include <omp.h>
+
+namespace desul
+{
+namespace Impl
+{
+static constexpr bool omp_on_host() { return true; }
+
+#pragma omp begin declare variant match(device = {kind(host)})
+static constexpr bool omp_on_host() { return true; }
+#pragma omp end declare variant
+
+#pragma omp begin declare variant match(device = {kind(nohost)})
+static constexpr bool omp_on_host() { return false; }
+#pragma omp end declare variant
+} // namespace Impl
+} // namespace desul
+
+#ifdef DESUL_HAVE_OPENMP_ATOMICS
+namespace desul {
+
+#if _OPENMP > 201800
+// atomic_thread_fence for Core Scope
+inline void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeCore) {
+  // There is no seq_cst flush in OpenMP, isn't it the same anyway for fence?
+  #pragma omp flush acq_rel
+}
+inline void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeCore) {
+  #pragma omp flush acq_rel
+}
+inline void atomic_thread_fence(MemoryOrderRelease, MemoryScopeCore) {
+  #pragma omp flush release
+}
+inline void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeCore) {
+  #pragma omp flush acquire
+}
+// atomic_thread_fence for Device Scope
+inline void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeDevice) {
+  // There is no seq_cst flush in OpenMP, isn't it the same anyway for fence?
+  #pragma omp flush acq_rel
+}
+inline void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeDevice) {
+  #pragma omp flush acq_rel
+}
+inline void atomic_thread_fence(MemoryOrderRelease, MemoryScopeDevice) {
+  #pragma omp flush release
+}
+inline void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeDevice) {
+  #pragma omp flush acquire
+}
+#else
+// atomic_thread_fence for Core Scope
+inline void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeCore) {
+  #pragma omp flush
+}
+inline void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeCore) {
+  #pragma omp flush
+}
+inline void atomic_thread_fence(MemoryOrderRelease, MemoryScopeCore) {
+  #pragma omp flush
+}
+inline void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeCore) {
+  #pragma omp flush
+}
+// atomic_thread_fence for Device Scope
+inline void atomic_thread_fence(MemoryOrderSeqCst, MemoryScopeDevice) {
+  #pragma omp flush
+}
+inline void atomic_thread_fence(MemoryOrderAcqRel, MemoryScopeDevice) {
+  #pragma omp flush
+}
+inline void atomic_thread_fence(MemoryOrderRelease, MemoryScopeDevice) {
+  #pragma omp flush
+}
+inline void atomic_thread_fence(MemoryOrderAcquire, MemoryScopeDevice) {
+  #pragma omp flush
+}
+#endif
+
+template <typename T, class MemoryOrder, class MemoryScope>
+T atomic_exchange(
+    T* dest, T value, MemoryOrder, MemoryScope) {
+  T return_val;
+  if(!std::is_same<MemoryOrder,MemoryOrderRelaxed>::value)
+    atomic_thread_fence(MemoryOrderAcquire(),MemoryScope());
+  T& x = *dest;
+  #pragma omp atomic capture
+  { return_val = x; x = value; }
+  if(!std::is_same<MemoryOrder,MemoryOrderRelaxed>::value)
+    atomic_thread_fence(MemoryOrderRelease(),MemoryScope());
+  return return_val;
+}
+
+// OpenMP doesn't have compare exchange, so we use build-ins and rely on testing that this works
+// Note that means we test this in OpenMPTarget offload regions!
+template <typename T, class MemoryOrder, class MemoryScope>
+std::enable_if_t<Impl::atomic_always_lock_free(sizeof(T)),T> atomic_compare_exchange(
+    T* dest, T compare, T value, MemoryOrder, MemoryScope) {
+  using cas_t = typename Impl::atomic_compare_exchange_type<sizeof(T)>::type;
+  cas_t retval = __sync_val_compare_and_swap(
+     reinterpret_cast<volatile cas_t*>(dest), 
+     reinterpret_cast<cas_t&>(compare), 
+     reinterpret_cast<cas_t&>(value));
+  return reinterpret_cast<T&>(retval);
+}
+// Make 16 byte cas work on host at least (is_initial_device check, note this requires C++17)
+#if __cplusplus>=201703L
+
+#if defined(__clang__) && (__clang_major__>=7)
+// Disable warning for large atomics on clang 7 and up (checked with godbolt)
+// error: large atomic operation may incur significant performance penalty [-Werror,-Watomic-alignment]
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Watomic-alignment"
+#endif
+
+template <typename T, class MemoryOrder, class MemoryScope>
+std::enable_if_t<!Impl::atomic_always_lock_free(sizeof(T)) && (sizeof(T)==16),T> atomic_compare_exchange(
+    T* dest, T compare, T value, MemoryOrder, MemoryScope) {
+  if constexpr (desul::Impl::omp_on_host()) {
+    (void)__atomic_compare_exchange(
+      dest, &compare, &value, false, GCCMemoryOrder<MemoryOrder>::value, GCCMemoryOrder<MemoryOrder>::value);
+    return compare;
+  } else {
+    return value;
+  }
+}
+#if defined(__clang__) && (__clang_major__>=7)
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+}  // namespace desul
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Compare_Exchange_SYCL.hpp b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_SYCL.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a8fd2ebbe2beef39e4cd8dff5797b722e8d17582
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_SYCL.hpp
@@ -0,0 +1,102 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_COMPARE_EXCHANGE_SYCL_HPP_
+#define DESUL_ATOMICS_COMPARE_EXCHANGE_SYCL_HPP_
+#include "desul/atomics/Common.hpp"
+#include "desul/atomics/SYCLConversions.hpp"
+#include <CL/sycl.hpp>
+
+
+#ifdef DESUL_HAVE_SYCL_ATOMICS
+
+namespace desul {
+
+template<class MemoryOrder, class MemoryScope>
+inline void atomic_thread_fence(MemoryOrder, MemoryScope) {
+  DESUL_SYCL_NAMESPACE::atomic_fence(DesulToSYCLMemoryOrder<MemoryOrder>::value,
+                                     DesulToSYCLMemoryScope<MemoryScope>::value);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+typename std::enable_if<sizeof(T) == 4, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrder, MemoryScope) {
+  static_assert(sizeof(unsigned int) == 4, "this function assumes an unsigned int is 32-bit");
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    unsigned int, 
+    DesulToSYCLMemoryOrder<MemoryOrder>::value, 
+    DesulToSYCLMemoryScope<MemoryScope>::value, 
+    sycl::access::address_space::global_device_space> 
+  dest_ref(*reinterpret_cast<unsigned int*>(dest));
+  dest_ref.compare_exchange_strong(*reinterpret_cast<unsigned int*>(&compare), 
+                                   *reinterpret_cast<unsigned int*>(&value));
+  return compare;
+}
+template <typename T, class MemoryOrder, class MemoryScope>
+typename std::enable_if<sizeof(T) == 8, T>::type atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrder, MemoryScope) {
+  static_assert(sizeof(unsigned long long int) == 8, "this function assumes an unsigned long long  is 64-bit");
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    unsigned long long int, 
+    DesulToSYCLMemoryOrder<MemoryOrder>::value,
+    DesulToSYCLMemoryScope<MemoryScope>::value, 
+    sycl::access::address_space::global_device_space> 
+  dest_ref(*reinterpret_cast<unsigned long long int*>(dest));
+  dest_ref.compare_exchange_strong(*reinterpret_cast<unsigned long long int*>(&compare),
+                                   *reinterpret_cast<unsigned long long int*>(&value));
+  return compare;
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+typename std::enable_if<sizeof(T) == 4, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrder, MemoryScope) {
+  static_assert(sizeof(unsigned int) == 4, "this function assumes an unsigned int is 32-bit");
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    unsigned int, 
+    DesulToSYCLMemoryOrder<MemoryOrder>::value, 
+    DesulToSYCLMemoryScope<MemoryScope>::value,  
+    sycl::access::address_space::global_device_space> 
+  dest_ref(*reinterpret_cast<unsigned int*>(dest));
+  unsigned int return_val = dest_ref.exchange(*reinterpret_cast<unsigned int*>(&value));
+  return reinterpret_cast<T&>(return_val);
+}
+template <typename T, class MemoryOrder, class MemoryScope>
+typename std::enable_if<sizeof(T) == 8, T>::type atomic_exchange(
+    T* const dest, T value, MemoryOrder, MemoryScope) {
+  static_assert(sizeof(unsigned long long int) == 8, "this function assumes an unsigned long long  is 64-bit");
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    unsigned long long int,
+    DesulToSYCLMemoryOrder<MemoryOrder>::value,
+    DesulToSYCLMemoryScope<MemoryScope>::value,
+    sycl::access::address_space::global_device_space>
+  dest_ref(*reinterpret_cast<unsigned long long int*>(dest));
+  unsigned long long int return_val =
+      dest_ref.exchange(reinterpret_cast<unsigned long long int&>(value));
+  return reinterpret_cast<T&>(return_val);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+typename std::enable_if<(sizeof(T) != 8) && (sizeof(T) != 4), T>::type atomic_compare_exchange(
+    T* const /*dest*/, T compare, T /*value*/, MemoryOrder, MemoryScope) {
+  // FIXME_SYCL not implemented
+  assert(false);
+  return compare;  
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+typename std::enable_if<(sizeof(T) != 8) && (sizeof(T) != 4), T>::type atomic_exchange(
+    T* const /*dest*/, T value, MemoryOrder, MemoryScope) {
+  // FIXME_SYCL not implemented
+  assert(false);
+  return value;
+}
+
+}
+
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Compare_Exchange_Serial.hpp b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_Serial.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..be7b46d5fa0540f20abfb8903f20a3e2f7d80e5a
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Compare_Exchange_Serial.hpp
@@ -0,0 +1,45 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+#ifndef DESUL_ATOMICS_COMPARE_EXCHANGE_SERIAL_HPP_
+#define DESUL_ATOMICS_COMPARE_EXCHANGE_SERIAL_HPP_
+
+#ifdef DESUL_HAVE_SERIAL_ATOMICS
+namespace desul {
+template<class MemoryScope>
+void atomic_thread_fence(MemoryOrderAcquire, MemoryScope) {
+}
+
+template<class MemoryScope>
+void atomic_thread_fence(MemoryOrderRelease, MemoryScope) {
+}
+
+template <typename T, class MemoryScope>
+T atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderRelaxed, MemoryScope) {
+  T old = *dest;
+  if (old == compare) {
+    *dest = value;
+  } else {
+    old = compare;
+  }
+  return compare;
+}
+template <typename T, class MemoryScope>
+T atomic_compare_exchange(
+    T* const dest, T compare, T value, MemoryOrderSeqCst, MemoryScope) {
+  T old = *dest;
+  if (old == compare) {
+    *dest = value;
+  } else {
+    old = compare;
+  }
+  return compare;
+}
+}  // namespace desul
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/GCC.hpp b/packages/kokkos/core/src/desul/atomics/GCC.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cd0c2bea1180662969e0af8abee5d23a1b7334ca
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/GCC.hpp
@@ -0,0 +1,131 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+#ifndef DESUL_ATOMICS_GCC_HPP_
+#define DESUL_ATOMICS_GCC_HPP_
+
+#ifdef DESUL_HAVE_GCC_ATOMICS
+
+#include<type_traits>
+/*
+Built - in Function : type __atomic_add_fetch(type * ptr, type val, int memorder)
+Built - in Function : type __atomic_sub_fetch(type * ptr, type val, int memorder)
+Built - in Function : type __atomic_and_fetch(type * ptr, type val, int memorder)
+Built - in Function : type __atomic_xor_fetch(type * ptr, type val, int memorder)
+Built - in Function : type __atomic_or_fetch(type * ptr, type val, int memorder)
+Built - in Function : type __atomic_nand_fetch(type * ptr, type val, int memorder)
+*/
+
+#define DESUL_GCC_INTEGRAL_OP_ATOMICS(MEMORY_ORDER, MEMORY_SCOPE)                 \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_fetch_add(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_fetch_add(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);  \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_fetch_sub(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_fetch_sub(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);  \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_fetch_and(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_fetch_and(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);  \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_fetch_or(   \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_fetch_or(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);   \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_fetch_xor(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_fetch_xor(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);  \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_fetch_nand( \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_fetch_nand(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value); \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_add_fetch(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_add_fetch(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);  \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_sub_fetch(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_sub_fetch(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);  \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_and_fetch(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_and_fetch(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);  \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_or_fetch(   \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_or_fetch(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);   \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_xor_fetch(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_xor_fetch(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value);  \
+  }                                                                               \
+  template <typename T>                                                           \
+  typename std::enable_if<std::is_integral<T>::value, T>::type atomic_nand_fetch( \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+    return __atomic_nand_fetch(dest, value, GCCMemoryOrder<MEMORY_ORDER>::value); \
+  }
+
+namespace desul {
+DESUL_GCC_INTEGRAL_OP_ATOMICS(MemoryOrderRelaxed, MemoryScopeNode)
+DESUL_GCC_INTEGRAL_OP_ATOMICS(MemoryOrderRelaxed, MemoryScopeDevice)
+DESUL_GCC_INTEGRAL_OP_ATOMICS(MemoryOrderRelaxed, MemoryScopeCore)
+DESUL_GCC_INTEGRAL_OP_ATOMICS(MemoryOrderSeqCst, MemoryScopeNode)
+DESUL_GCC_INTEGRAL_OP_ATOMICS(MemoryOrderSeqCst, MemoryScopeDevice)
+DESUL_GCC_INTEGRAL_OP_ATOMICS(MemoryOrderSeqCst, MemoryScopeCore)
+
+template <typename T, class MemoryOrder, class MemoryScope>
+std::enable_if_t<!Impl::atomic_exchange_available_gcc<T>::value, T>
+atomic_exchange(T* const dest,
+                  Impl::dont_deduce_this_parameter_t<const T> val,
+                  MemoryOrder /*order*/,
+                  MemoryScope scope) {
+  // Acquire a lock for the address
+  while (!Impl::lock_address((void*)dest, scope)) {}
+
+  atomic_thread_fence(MemoryOrderAcquire(),scope);
+  T return_val = *dest;
+  *dest = val;
+  atomic_thread_fence(MemoryOrderRelease(),scope);
+  Impl::unlock_address((void*)dest, scope);
+  return return_val;
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+std::enable_if_t<!Impl::atomic_exchange_available_gcc<T>::value, T>
+atomic_compare_exchange(T* const dest,
+                  Impl::dont_deduce_this_parameter_t<const T> compare,
+                  Impl::dont_deduce_this_parameter_t<const T> val,
+                  MemoryOrder /*order*/,
+                  MemoryScope scope) {
+  // Acquire a lock for the address
+  while (!Impl::lock_address((void*)dest, scope)) {}
+
+  atomic_thread_fence(MemoryOrderAcquire(),scope);
+  T return_val = *dest;
+  if(return_val == compare) {
+    *dest = val;
+    atomic_thread_fence(MemoryOrderRelease(),scope);
+  }
+  Impl::unlock_address((void*)dest, scope);
+  return return_val;
+}
+}  // namespace desul
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Generic.hpp b/packages/kokkos/core/src/desul/atomics/Generic.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9d5e87ece29f2c444522a91e4635598872f5b71f
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Generic.hpp
@@ -0,0 +1,690 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_GENERIC_HPP_
+#define DESUL_ATOMICS_GENERIC_HPP_
+
+#include <type_traits>
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wstrict-aliasing"
+#include "desul/atomics/Common.hpp"
+#include "desul/atomics/Compare_Exchange.hpp"
+#include "desul/atomics/Lock_Array.hpp"
+#include "desul/atomics/Macros.hpp"
+// Combination operands to be used in an Compare and Exchange based atomic
+// operation
+namespace desul {
+namespace Impl {
+
+template <class Scalar1, class Scalar2>
+struct MaxOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) {
+    return (val1 > val2 ? val1 : val2);
+  }
+  DESUL_FORCEINLINE_FUNCTION
+  static constexpr bool check_early_exit(Scalar1 const& val1, Scalar2 const& val2) {
+    return val1 > val2;
+  }
+};
+
+template <class Scalar1, class Scalar2>
+struct MinOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) {
+    return (val1 < val2 ? val1 : val2);
+  }
+  DESUL_FORCEINLINE_FUNCTION
+  static constexpr bool check_early_exit(Scalar1 const& val1, Scalar2 const& val2) {
+    return val1 < val2;
+  }
+};
+
+// This exit early optimization causes weird compiler errors with MSVC 2019
+#ifndef DESUL_HAVE_MSVC_ATOMICS
+template <typename Op, typename Scalar1, typename Scalar2, typename = bool>
+struct may_exit_early : std::false_type {};
+
+template <typename Op, typename Scalar1, typename Scalar2>
+struct may_exit_early<Op,
+                      Scalar1,
+                      Scalar2,
+                      decltype(Op::check_early_exit(std::declval<Scalar1 const&>(),
+                                                    std::declval<Scalar2 const&>()))>
+    : std::true_type {};
+
+template <typename Op, typename Scalar1, typename Scalar2>
+constexpr DESUL_FUNCTION typename std::enable_if<may_exit_early<Op, Scalar1, Scalar2>::value, bool>::type
+check_early_exit(Op const&, Scalar1 const& val1, Scalar2 const& val2) {
+  return Op::check_early_exit(val1, val2);
+}
+
+template <typename Op, typename Scalar1, typename Scalar2>
+constexpr DESUL_FUNCTION typename std::enable_if<!may_exit_early<Op, Scalar1, Scalar2>::value, bool>::type
+check_early_exit(Op const&, Scalar1 const&, Scalar2 const&) {
+  return false;
+}
+#endif
+
+template <class Scalar1, class Scalar2>
+struct AddOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 + val2; }
+};
+
+template <class Scalar1, class Scalar2>
+struct SubOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 - val2; }
+};
+
+template <class Scalar1, class Scalar2>
+struct MulOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 * val2; }
+};
+
+template <class Scalar1, class Scalar2>
+struct DivOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 / val2; }
+};
+
+template <class Scalar1, class Scalar2>
+struct ModOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 % val2; }
+};
+
+template <class Scalar1, class Scalar2>
+struct AndOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 & val2; }
+};
+
+template <class Scalar1, class Scalar2>
+struct OrOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 | val2; }
+};
+
+template <class Scalar1, class Scalar2>
+struct XorOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) { return val1 ^ val2; }
+};
+
+template <class Scalar1, class Scalar2>
+struct NandOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) {
+    return ~(val1 & val2);
+  }
+};
+
+template <class Scalar1, class Scalar2>
+struct LShiftOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) {
+    return val1 << val2;
+  }
+};
+
+template <class Scalar1, class Scalar2>
+struct RShiftOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2& val2) {
+    return val1 >> val2;
+  }
+};
+
+template <class Scalar1, class Scalar2>
+struct StoreOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1&, const Scalar2& val2) { return val2; }
+};
+
+template <class Scalar1, class Scalar2>
+struct LoadOper {
+  DESUL_FORCEINLINE_FUNCTION
+  static Scalar1 apply(const Scalar1& val1, const Scalar2&) { return val1; }
+};
+
+
+template <class Oper, typename T, class MemoryOrder, class MemoryScope,
+  // equivalent to:
+  //   requires atomic_always_lock_free(sizeof(T))
+  std::enable_if_t<atomic_always_lock_free(sizeof(T)), int> = 0
+>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_oper(const Oper& op,
+                  T* const dest,
+                  dont_deduce_this_parameter_t<const T> val,
+                  MemoryOrder order,
+                  MemoryScope scope) {
+  using cas_t = typename atomic_compare_exchange_type<sizeof(T)>::type;
+  cas_t oldval = reinterpret_cast<cas_t&>(*dest);
+  cas_t assume = oldval;
+
+  do {
+#ifndef DESUL_HAVE_MSVC_ATOMICS
+    if (Impl::check_early_exit(op, reinterpret_cast<T&>(oldval), val)) return reinterpret_cast<T&>(oldval);
+#endif
+    assume = oldval;
+    T newval = op.apply(reinterpret_cast<T&>(assume), val);
+    oldval = desul::atomic_compare_exchange(
+        reinterpret_cast<cas_t*>(dest), assume, reinterpret_cast<cas_t&>(newval), order, scope);
+  } while (assume != oldval);
+
+  return reinterpret_cast<T&>(oldval);
+}
+
+template <class Oper, typename T, class MemoryOrder, class MemoryScope,
+  // equivalent to:
+  //   requires atomic_always_lock_free(sizeof(T))
+  std::enable_if_t<atomic_always_lock_free(sizeof(T)), int> = 0
+>
+DESUL_INLINE_FUNCTION T
+atomic_oper_fetch(const Oper& op,
+                  T* const dest,
+                  dont_deduce_this_parameter_t<const T> val,
+                  MemoryOrder order,
+                  MemoryScope scope) {
+  using cas_t = typename atomic_compare_exchange_type<sizeof(T)>::type;
+  cas_t oldval = reinterpret_cast<cas_t&>(*dest);
+  T newval = val;
+  cas_t assume = oldval;
+  do {
+#ifndef DESUL_HAVE_MSVC_ATOMICS
+    if (Impl::check_early_exit(op, reinterpret_cast<T&>(oldval), val)) return reinterpret_cast<T&>(oldval);
+#endif
+    assume = oldval;
+    newval = op.apply(reinterpret_cast<T&>(assume), val);
+    oldval = desul::atomic_compare_exchange(
+        reinterpret_cast<cas_t*>(dest), assume, reinterpret_cast<cas_t&>(newval), order, scope);
+  } while (assume != oldval);
+
+  return newval;
+}
+
+template <class Oper, typename T, class MemoryOrder, class MemoryScope,
+  // equivalent to:
+  //   requires !atomic_always_lock_free(sizeof(T))
+  std::enable_if_t<!atomic_always_lock_free(sizeof(T)), int> = 0
+>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_oper(const Oper& op,
+                  T* const dest,
+                  dont_deduce_this_parameter_t<const T> val,
+                  MemoryOrder /*order*/,
+                  MemoryScope scope) {
+#if defined(DESUL_HAVE_FORWARD_PROGRESS)
+  // Acquire a lock for the address
+  while (!Impl::lock_address((void*)dest, scope)) {}
+
+  atomic_thread_fence(MemoryOrderAcquire(),scope);
+  T return_val = *dest;
+  *dest = op.apply(return_val, val);
+  atomic_thread_fence(MemoryOrderRelease(),scope);
+  Impl::unlock_address((void*)dest, scope);
+  return return_val;
+#elif defined(DESUL_HAVE_GPU_LIKE_PROGRESS)
+  // This is a way to avoid dead lock in a warp or wave front
+  T return_val;
+  int done = 0;
+#ifdef __HIPCC__
+  unsigned long long int active = DESUL_IMPL_BALLOT_MASK(1);
+  unsigned long long int done_active = 0;
+  while (active != done_active) {
+    if (!done) {
+      if (Impl::lock_address_hip((void*)dest, scope)) {
+        atomic_thread_fence(MemoryOrderAcquire(), scope);
+        return_val = *dest;
+        *dest = op.apply(return_val, val);
+        atomic_thread_fence(MemoryOrderRelease(), scope);
+        Impl::unlock_address_hip((void*)dest, scope);
+        done = 1;
+      }
+    }
+    done_active = DESUL_IMPL_BALLOT_MASK(done);
+  }
+  return return_val;
+// FIXME_SYCL not implemented
+#elif defined(__SYCL_DEVICE_ONLY__)
+  (void) op;
+  (void) dest;
+  (void) scope;
+  (void) return_val;
+  (void) done;
+
+  assert(false);
+  return val;
+#else
+  unsigned int mask = DESUL_IMPL_ACTIVEMASK;
+  unsigned int active = DESUL_IMPL_BALLOT_MASK(mask, 1);
+  unsigned int done_active = 0;
+  while (active != done_active) {
+    if (!done) {
+      if (Impl::lock_address_cuda((void*)dest, scope)) {
+        atomic_thread_fence(MemoryOrderAcquire(),scope);
+        return_val = *dest;
+        *dest = op.apply(return_val, val);
+        atomic_thread_fence(MemoryOrderRelease(),scope);
+        Impl::unlock_address_cuda((void*)dest, scope);
+        done = 1;
+      }
+    }
+    done_active = DESUL_IMPL_BALLOT_MASK(mask, done);
+  }
+  return return_val;
+#endif
+#else
+  static_assert(false, "Unimplemented lock based attomic\n");
+  return val;
+#endif
+}
+
+template <class Oper, typename T, class MemoryOrder, class MemoryScope,
+  // equivalent to:
+  //   requires !atomic_always_lock_free(sizeof(T))
+  std::enable_if_t<!atomic_always_lock_free(sizeof(T)), int> = 0
+>
+DESUL_INLINE_FUNCTION T
+atomic_oper_fetch(const Oper& op,
+                  T* const dest,
+                  dont_deduce_this_parameter_t<const T> val,
+                  MemoryOrder /*order*/,
+                  MemoryScope scope) {
+#if defined(DESUL_HAVE_FORWARD_PROGRESS)
+  // Acquire a lock for the address
+  while (!Impl::lock_address((void*)dest, scope)) {}
+
+  atomic_thread_fence(MemoryOrderAcquire(),scope);
+  T return_val = op.apply(*dest, val);
+  *dest = return_val;
+  atomic_thread_fence(MemoryOrderRelease(),scope);
+  Impl::unlock_address((void*)dest, scope);
+  return return_val;
+#elif defined(DESUL_HAVE_GPU_LIKE_PROGRESS)
+  // This is a way to avoid dead lock in a warp or wave front
+  T return_val;
+  int done = 0;
+#ifdef __HIPCC__
+  unsigned long long int active = DESUL_IMPL_BALLOT_MASK(1);
+  unsigned long long int done_active = 0;
+  while (active != done_active) {
+    if (!done) {
+      if (Impl::lock_address_hip((void*)dest, scope)) {
+        atomic_thread_fence(MemoryOrderAcquire(), scope);
+        return_val = op.apply(*dest, val);
+        *dest = return_val;
+        atomic_thread_fence(MemoryOrderRelease(), scope);
+        Impl::unlock_address_hip((void*)dest, scope);
+        done = 1;
+      }
+    }
+    done_active = DESUL_IMPL_BALLOT_MASK(done);
+  }
+  return return_val;
+  // FIXME_SYCL not implemented
+#elif defined(__SYCL_DEVICE_ONLY__)
+  (void) op;
+  (void) dest;
+  (void) scope;
+  (void) done;
+
+  assert(false);
+  return val;
+#else
+  unsigned int mask = DESUL_IMPL_ACTIVEMASK;
+  unsigned int active = DESUL_IMPL_BALLOT_MASK(mask, 1);
+  unsigned int done_active = 0;
+  while (active != done_active) {
+    if (!done) {
+      if (Impl::lock_address_cuda((void*)dest, scope)) {
+        atomic_thread_fence(MemoryOrderAcquire(),scope);
+        return_val = op.apply(*dest, val);
+        *dest = return_val;
+        atomic_thread_fence(MemoryOrderRelease(),scope);
+        Impl::unlock_address_cuda((void*)dest, scope);
+        done = 1;
+      }
+    }
+    done_active = DESUL_IMPL_BALLOT_MASK(mask, done);
+  }
+  return return_val;
+#endif
+#else
+  static_assert(false, "Unimplemented lock based atomic\n");
+  return val;
+#endif
+}
+
+}  // namespace Impl
+}  // namespace desul
+
+namespace desul {
+
+// Fetch_Oper atomics: return value before operation
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_add(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::AddOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_sub(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::SubOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_max(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::MaxOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_min(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::MinOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_mul(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::MulOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_div(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::DivOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_mod(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::ModOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_and(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::AndOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_or(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::OrOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_xor(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::XorOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_fetch_nand(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::NandOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T atomic_fetch_lshift(T* const dest,
+                                            const unsigned int val,
+                                            MemoryOrder order,
+                                            MemoryScope scope) {
+  return Impl::atomic_fetch_oper(
+      Impl::LShiftOper<T, const unsigned int>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T atomic_fetch_rshift(T* const dest,
+                                            const unsigned int val,
+                                            MemoryOrder order,
+                                            MemoryScope scope) {
+  return Impl::atomic_fetch_oper(
+      Impl::RShiftOper<T, const unsigned int>(), dest, val, order, scope);
+}
+
+// Oper Fetch atomics: return value after operation
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_add_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::AddOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_sub_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::SubOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_max_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::MaxOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_min_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::MinOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_mul_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::MulOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_div_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::DivOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_mod_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::ModOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_and_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::AndOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_or_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::OrOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_xor_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::XorOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_nand_fetch(T* const dest, const T val, MemoryOrder order, MemoryScope scope) {
+  return Impl::atomic_oper_fetch(Impl::NandOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T atomic_lshift_fetch(T* const dest,
+                                            const unsigned int val,
+                                            MemoryOrder order,
+                                            MemoryScope scope) {
+  return Impl::atomic_oper_fetch(
+      Impl::LShiftOper<T, const unsigned int>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T atomic_rshift_fetch(T* const dest,
+                                            const unsigned int val,
+                                            MemoryOrder order,
+                                            MemoryScope scope) {
+  return Impl::atomic_oper_fetch(
+      Impl::RShiftOper<T, const unsigned int>(), dest, val, order, scope);
+}
+
+// Other atomics
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T atomic_load(const T* const dest,
+                                    MemoryOrder order,
+                                    MemoryScope scope) {
+  return Impl::atomic_fetch_oper(Impl::LoadOper<T, const T>(), const_cast<T*>(dest), T(), order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION void atomic_store(T* const dest,
+                                        const T val,
+                                        MemoryOrder order,
+                                        MemoryScope scope) {
+  (void)Impl::atomic_fetch_oper(Impl::StoreOper<T, const T>(), dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION void atomic_add(T* const dest,
+                                      const T val,
+                                      MemoryOrder order,
+                                      MemoryScope scope) {
+  (void)atomic_fetch_add(dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION void atomic_sub(T* const dest,
+                                      const T val,
+                                      MemoryOrder order,
+                                      MemoryScope scope) {
+  (void)atomic_fetch_sub(dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION void atomic_mul(T* const dest,
+                                      const T val,
+                                      MemoryOrder order,
+                                      MemoryScope scope) {
+  (void)atomic_fetch_mul(dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION void atomic_div(T* const dest,
+                                      const T val,
+                                      MemoryOrder order,
+                                      MemoryScope scope) {
+  (void)atomic_fetch_div(dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION void atomic_min(T* const dest,
+                                      const T val,
+                                      MemoryOrder order,
+                                      MemoryScope scope) {
+  (void)atomic_fetch_min(dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION void atomic_max(T* const dest,
+                                      const T val,
+                                      MemoryOrder order,
+                                      MemoryScope scope) {
+  (void)atomic_fetch_max(dest, val, order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_inc_fetch(T* const dest, MemoryOrder order, MemoryScope scope) {
+  return atomic_add_fetch(dest, T(1), order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T
+atomic_dec_fetch(T* const dest, MemoryOrder order, MemoryScope scope) {
+  return atomic_sub_fetch(dest, T(1), order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T atomic_fetch_inc(T* const dest,
+                                         MemoryOrder order,
+                                         MemoryScope scope) {
+  return atomic_fetch_add(dest, T(1), order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION T atomic_fetch_dec(T* const dest,
+                                         MemoryOrder order,
+                                         MemoryScope scope) {
+  return atomic_fetch_sub(dest, T(1), order, scope);
+}
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION void atomic_inc(T* const dest,
+                                         MemoryOrder order,
+                                         MemoryScope scope) {
+  return atomic_add(dest, T(1), order, scope);
+}
+
+template <typename T, class MemoryOrder, class MemoryScope>
+DESUL_INLINE_FUNCTION void atomic_dec(T* const dest,
+                                         MemoryOrder order,
+                                         MemoryScope scope) {
+  return atomic_sub(dest, T(1), order, scope);
+}
+
+// FIXME
+template <typename T,
+          class SuccessMemoryOrder,
+          class FailureMemoryOrder,
+          class MemoryScope>
+DESUL_INLINE_FUNCTION bool atomic_compare_exchange_strong(
+    T* const dest,
+    T& expected,
+    T desired,
+    SuccessMemoryOrder success,
+    FailureMemoryOrder /*failure*/,
+    MemoryScope scope) {
+  T const old = atomic_compare_exchange(dest, expected, desired, success, scope);
+  if (old != expected) {
+    expected = old;
+    return false;
+  } else {
+    return true;
+  }
+}
+
+template <typename T,
+          class SuccessMemoryOrder,
+          class FailureMemoryOrder,
+          class MemoryScope>
+DESUL_INLINE_FUNCTION bool atomic_compare_exchange_weak(T* const dest,
+                                                        T& expected,
+                                                        T desired,
+                                                        SuccessMemoryOrder success,
+                                                        FailureMemoryOrder failure,
+                                                        MemoryScope scope) {
+  return atomic_compare_exchange_strong(
+      dest, expected, desired, success, failure, scope);
+}
+
+}  // namespace desul
+
+#include <desul/atomics/SYCL.hpp>
+#include <desul/atomics/CUDA.hpp>
+#include <desul/atomics/GCC.hpp>
+#include <desul/atomics/HIP.hpp>
+#include <desul/atomics/OpenMP.hpp>
+#pragma GCC diagnostic pop
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/HIP.hpp b/packages/kokkos/core/src/desul/atomics/HIP.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..16c1f510b7a2627408ccea374004d280997e96df
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/HIP.hpp
@@ -0,0 +1,338 @@
+/*
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+#ifndef DESUL_ATOMICS_HIP_HPP_
+#define DESUL_ATOMICS_HIP_HPP_
+
+#ifdef __HIP_DEVICE_COMPILE__
+namespace desul {
+namespace Impl {
+template <typename T>
+struct is_hip_atomic_integer_type {
+  static constexpr bool value = std::is_same<T, int>::value ||
+                                std::is_same<T, unsigned int>::value ||
+                                std::is_same<T, unsigned long long int>::value;
+};
+
+template <typename T>
+struct is_hip_atomic_add_type {
+  static constexpr bool value = is_hip_atomic_integer_type<T>::value ||
+                                std::is_same<T, double>::value ||
+                                std::is_same<T, float>::value;
+};
+
+template <typename T>
+struct is_hip_atomic_sub_type {
+  static constexpr bool value =
+      std::is_same<T, int>::value || std::is_same<T, unsigned int>::value;
+};
+}  // namespace Impl
+
+// Atomic Add
+template <typename T>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_add_type<T>::value, T>::type
+    atomic_fetch_add(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicAdd(dest, val);
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_add_type<T>::value, T>::type
+    atomic_fetch_add(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicAdd(dest, val);
+  __threadfence();
+
+  return return_val;
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_add_type<T>::value, T>::type
+    atomic_fetch_add(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_add(dest, val, MemoryOrder(), MemoryScopeDevice());
+}
+
+// Atomic Sub
+template <typename T>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_sub_type<T>::value, T>::type
+    atomic_fetch_sub(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicSub(dest, val);
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_sub_type<T>::value, T>::type
+    atomic_fetch_sub(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicSub(dest, val);
+  __threadfence();
+  return return_val;
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_sub_type<T>::value, T>::type
+    atomic_fetch_sub(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_sub(dest, val, MemoryOrder(), MemoryScopeDevice());
+}
+
+// Atomic Inc
+__device__ inline unsigned int atomic_fetch_inc(unsigned int* dest,
+                                                unsigned int val,
+                                                MemoryOrderRelaxed,
+                                                MemoryScopeDevice) {
+  return atomicInc(dest, val);
+}
+
+template <typename MemoryOrder>
+__device__ inline unsigned int atomic_fetch_inc(unsigned int* dest,
+                                                unsigned int val,
+                                                MemoryOrder,
+                                                MemoryScopeDevice) {
+  __threadfence();
+  unsigned int return_val = atomicInc(dest, val);
+  __threadfence();
+  return return_val;
+}
+
+template <typename MemoryOrder>
+__device__ inline unsigned int atomic_fetch_inc(unsigned int* dest,
+                                                unsigned int val,
+                                                MemoryOrder,
+                                                MemoryScopeCore) {
+  return atomic_fetch_inc(dest, val, MemoryOrder(), MemoryScopeDevice());
+}
+
+// Atomic Dec
+__device__ inline unsigned int atomic_fetch_dec(unsigned int* dest,
+                                                unsigned int val,
+                                                MemoryOrderRelaxed,
+                                                MemoryScopeDevice) {
+  return atomicDec(dest, val);
+}
+
+template <typename MemoryOrder>
+__device__ inline unsigned int atomic_fetch_dec(unsigned int* dest,
+                                                unsigned int val,
+                                                MemoryOrder,
+                                                MemoryScopeDevice) {
+  __threadfence();
+  unsigned int return_val = atomicDec(dest, val);
+  __threadfence();
+  return return_val;
+}
+
+template <typename MemoryOrder>
+__device__ inline unsigned int atomic_fetch_dec(unsigned int* dest,
+                                                unsigned int val,
+                                                MemoryOrder,
+                                                MemoryScopeCore) {
+  return atomic_fetch_dec(dest, val, MemoryOrder(), MemoryScopeDevice());
+}
+
+// Atomic Max
+template <typename T>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_max(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicMax(dest, val);
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_max(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicMax(dest, val);
+  __threadfence();
+  return return_val;
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_max(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_max(dest, val, MemoryOrder(), MemoryScopeDevice());
+}
+
+// Atomic Min
+template <typename T>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_min(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicMin(dest, val);
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_min(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicMin(dest, val);
+  __threadfence();
+  return return_val;
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_min(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_min(dest, val, MemoryOrder(), MemoryScopeDevice());
+}
+
+// Atomic And
+template <typename T>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_and(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicAnd(dest, val);
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_and(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicAnd(dest, val);
+  __threadfence();
+  return return_val;
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_and(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_and(dest, val, MemoryOrder(), MemoryScopeDevice());
+}
+
+// Atomic XOR
+template <typename T>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_xor(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicXor(dest, val);
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_xor(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicXor(dest, val);
+  __threadfence();
+  return return_val;
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_xor(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_xor(dest, val, MemoryOrder(), MemoryScopeDevice());
+}
+
+// Atomic OR
+template <typename T>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_or(T* dest, T val, MemoryOrderRelaxed, MemoryScopeDevice) {
+  return atomicOr(dest, val);
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_or(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  __threadfence();
+  T return_val = atomicOr(dest, val);
+  __threadfence();
+  return return_val;
+}
+
+template <typename T, typename MemoryOrder>
+__device__ inline
+    typename std::enable_if<Impl::is_hip_atomic_integer_type<T>::value, T>::type
+    atomic_fetch_or(T* dest, T val, MemoryOrder, MemoryScopeCore) {
+  return atomic_fetch_or(dest, val, MemoryOrder(), MemoryScopeDevice());
+}
+
+}
+
+#define DESUL_HIP_GCC_INTEGRAL_OP_ATOMICS_COMPATIBILITY(MEMORY_ORDER, MEMORY_SCOPE)                 \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_add_type<T>::value, T>::type atomic_fetch_add(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_fetch_oper(Impl::AddOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_sub_type<T>::value, T>::type atomic_fetch_sub(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_fetch_oper(Impl::SubOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_integer_type<T>::value, T>::type atomic_fetch_and(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_fetch_oper(Impl::AndOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_integer_type<T>::value, T>::type atomic_fetch_or(   \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_fetch_oper(Impl::OrOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_integer_type<T>::value, T>::type atomic_fetch_xor(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_fetch_oper(Impl::XorOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_integer_type<T>::value, T>::type atomic_fetch_nand( \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_fetch_oper(Impl::NandOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_add_type<T>::value, T>::type atomic_add_fetch(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_oper_fetch(Impl::AddOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_sub_type<T>::value, T>::type atomic_sub_fetch(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_oper_fetch(Impl::SubOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_integer_type<T>::value, T>::type atomic_and_fetch(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_oper_fetch(Impl::AndOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_integer_type<T>::value, T>::type atomic_or_fetch(   \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_oper_fetch(Impl::OrOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_integer_type<T>::value, T>::type atomic_xor_fetch(  \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_oper_fetch(Impl::XorOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }                                                                               \
+  template <typename T>                                                           \
+  __device__ typename std::enable_if<std::is_integral<T>::value && !Impl::is_hip_atomic_integer_type<T>::value, T>::type atomic_nand_fetch( \
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       \
+       return Impl::atomic_oper_fetch(Impl::NandOper<T, const T>(), dest, value, MEMORY_ORDER(), MEMORY_SCOPE()); \
+  }
+namespace desul {
+DESUL_HIP_GCC_INTEGRAL_OP_ATOMICS_COMPATIBILITY(MemoryOrderRelaxed, MemoryScopeNode)
+DESUL_HIP_GCC_INTEGRAL_OP_ATOMICS_COMPATIBILITY(MemoryOrderRelaxed, MemoryScopeDevice)
+DESUL_HIP_GCC_INTEGRAL_OP_ATOMICS_COMPATIBILITY(MemoryOrderRelaxed, MemoryScopeCore)
+DESUL_HIP_GCC_INTEGRAL_OP_ATOMICS_COMPATIBILITY(MemoryOrderSeqCst, MemoryScopeNode)
+DESUL_HIP_GCC_INTEGRAL_OP_ATOMICS_COMPATIBILITY(MemoryOrderSeqCst, MemoryScopeDevice)
+DESUL_HIP_GCC_INTEGRAL_OP_ATOMICS_COMPATIBILITY(MemoryOrderSeqCst, MemoryScopeCore)
+}  // namespace desul
+
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Lock_Array.hpp b/packages/kokkos/core/src/desul/atomics/Lock_Array.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8fd0e8bbd7718a1097d898b01b20aa71ff515f2f
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Lock_Array.hpp
@@ -0,0 +1,75 @@
+/*
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_LOCK_ARRAY_HPP_
+#define DESUL_ATOMICS_LOCK_ARRAY_HPP_
+
+#include "desul/atomics/Compare_Exchange.hpp"
+#include "desul/atomics/Lock_Array_Cuda.hpp"
+#include "desul/atomics/Lock_Array_HIP.hpp"
+#include "desul/atomics/Macros.hpp"
+
+namespace desul {
+namespace Impl {
+struct host_locks__ {
+  static constexpr uint32_t HOST_SPACE_ATOMIC_MASK = 0xFFFF;
+  static constexpr uint32_t HOST_SPACE_ATOMIC_XOR_MASK = 0x5A39;
+  template <typename is_always_void = void>
+  static int32_t* get_host_locks_() {
+    static int32_t HOST_SPACE_ATOMIC_LOCKS_DEVICE[HOST_SPACE_ATOMIC_MASK + 1] = {0};
+    return HOST_SPACE_ATOMIC_LOCKS_DEVICE;
+  }
+  static inline int32_t* get_host_lock_(void* ptr) {
+    return &get_host_locks_()[((uint64_t(ptr) >> 2) & HOST_SPACE_ATOMIC_MASK) ^
+                              HOST_SPACE_ATOMIC_XOR_MASK];
+  }
+};
+
+inline void init_lock_arrays() {
+  static bool is_initialized = false;
+  if (!is_initialized) {
+    host_locks__::get_host_locks_();
+    is_initialized = true;
+  }
+
+#ifdef DESUL_HAVE_CUDA_ATOMICS
+  init_lock_arrays_cuda();
+#endif
+
+#ifdef DESUL_HAVE_HIP_ATOMICS
+  init_lock_arrays_hip();
+#endif
+}
+
+inline void finalize_lock_arrays() {
+#ifdef DESUL_HAVE_CUDA_ATOMICS
+  finalize_lock_arrays_cuda();
+#endif
+
+#ifdef DESUL_HAVE_HIP_ATOMICS
+  finalize_lock_arrays_hip();
+#endif
+}
+template <typename MemoryScope>
+inline bool lock_address(void* ptr, MemoryScope ms) {
+  return 0 == atomic_exchange(host_locks__::get_host_lock_(ptr),
+                                      int32_t(1),
+                                      MemoryOrderSeqCst(),
+                                      ms);
+}
+template <typename MemoryScope>
+void unlock_address(void* ptr, MemoryScope ms) {
+  (void)atomic_exchange(host_locks__::get_host_lock_(ptr),
+                                int32_t(0),
+                                MemoryOrderSeqCst(),
+                                ms);
+}
+}  // namespace Impl
+}  // namespace desul
+
+#endif  // DESUL_ATOMICS_LOCK_ARRAY_HPP_
diff --git a/packages/kokkos/core/src/desul/atomics/Lock_Array_Cuda.hpp b/packages/kokkos/core/src/desul/atomics/Lock_Array_Cuda.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..de99185349043dc6e0f13c7e57c14dbc080deb9e
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Lock_Array_Cuda.hpp
@@ -0,0 +1,172 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_LOCK_ARRAY_CUDA_HPP_
+#define DESUL_ATOMICS_LOCK_ARRAY_CUDA_HPP_
+
+#include "desul/atomics/Macros.hpp"
+#include "desul/atomics/Common.hpp"
+
+#ifdef DESUL_HAVE_CUDA_ATOMICS
+
+#include <cstdint>
+
+namespace desul {
+namespace Impl {
+
+#ifdef __CUDA_ARCH__
+#define DESUL_IMPL_BALLOT_MASK(m, x) __ballot_sync(m, x)
+#define DESUL_IMPL_ACTIVEMASK __activemask()
+#else
+#define DESUL_IMPL_BALLOT_MASK(m, x) m==0?0:1
+#define DESUL_IMPL_ACTIVEMASK 0
+#endif
+
+/// \brief This global variable in Host space is the central definition
+///        of these arrays.
+extern int32_t* CUDA_SPACE_ATOMIC_LOCKS_DEVICE_h;
+extern int32_t* CUDA_SPACE_ATOMIC_LOCKS_NODE_h;
+
+
+/// \brief After this call, the g_host_cuda_lock_arrays variable has
+///        valid, initialized arrays.
+///
+/// This call is idempotent.
+/// The function is templated to make it a weak symbol to deal with Kokkos/RAJA
+///   snapshotted version while also linking against pure Desul
+template<typename /*AlwaysInt*/ = int>
+void init_lock_arrays_cuda();
+
+/// \brief After this call, the g_host_cuda_lock_arrays variable has
+///        all null pointers, and all array memory has been freed.
+///
+/// This call is idempotent.
+/// The function is templated to make it a weak symbol to deal with Kokkos/RAJA
+///   snappshotted version while also linking against pure Desul
+template<typename T = int>
+void finalize_lock_arrays_cuda();
+
+}  // namespace Impl
+}  // namespace desul
+
+#if defined(__CUDACC__)
+
+namespace desul {
+namespace Impl {
+
+/// \brief This global variable in CUDA space is what kernels use
+///        to get access to the lock arrays.
+///
+/// When relocatable device code is enabled, there can be one single
+/// instance of this global variable for the entire executable,
+/// whose definition will be in Kokkos_Cuda_Locks.cpp (and whose declaration
+/// here must then be extern.
+/// This one instance will be initialized by initialize_host_cuda_lock_arrays
+/// and need not be modified afterwards.
+///
+/// When relocatable device code is disabled, an instance of this variable
+/// will be created in every translation unit that sees this header file
+/// (we make this clear by marking it static, meaning no other translation
+///  unit can link to it).
+/// Since the Kokkos_Cuda_Locks.cpp translation unit cannot initialize the
+/// instances in other translation units, we must update this CUDA global
+/// variable based on the Host global variable prior to running any kernels
+/// that will use it.
+/// That is the purpose of the KOKKOS_ENSURE_CUDA_LOCK_ARRAYS_ON_DEVICE macro.
+__device__
+#ifdef __CUDACC_RDC__
+    __constant__ extern
+#endif
+    int32_t* CUDA_SPACE_ATOMIC_LOCKS_DEVICE;
+
+__device__
+#ifdef __CUDACC_RDC__
+    __constant__ extern
+#endif
+    int32_t* CUDA_SPACE_ATOMIC_LOCKS_NODE;
+
+#define CUDA_SPACE_ATOMIC_MASK 0x1FFFF
+
+/// \brief Acquire a lock for the address
+///
+/// This function tries to acquire the lock for the hash value derived
+/// from the provided ptr. If the lock is successfully acquired the
+/// function returns true. Otherwise it returns false.
+__device__ inline bool lock_address_cuda(void* ptr, desul::MemoryScopeDevice) {
+  size_t offset = size_t(ptr);
+  offset = offset >> 2;
+  offset = offset & CUDA_SPACE_ATOMIC_MASK;
+  return (0 == atomicExch(&desul::Impl::CUDA_SPACE_ATOMIC_LOCKS_DEVICE[offset], 1));
+}
+__device__ inline bool lock_address_cuda(void* ptr, desul::MemoryScopeNode) {
+  size_t offset = size_t(ptr);
+  offset = offset >> 2;
+  offset = offset & CUDA_SPACE_ATOMIC_MASK;
+  return (0 == atomicExch(&desul::Impl::CUDA_SPACE_ATOMIC_LOCKS_NODE[offset], 1));
+}
+
+/// \brief Release lock for the address
+///
+/// This function releases the lock for the hash value derived
+/// from the provided ptr. This function should only be called
+/// after previously successfully acquiring a lock with
+/// lock_address.
+__device__ inline void unlock_address_cuda(void* ptr, desul::MemoryScopeDevice) {
+  size_t offset = size_t(ptr);
+  offset = offset >> 2;
+  offset = offset & CUDA_SPACE_ATOMIC_MASK;
+  atomicExch(&desul::Impl::CUDA_SPACE_ATOMIC_LOCKS_DEVICE[offset], 0);
+}
+__device__ inline void unlock_address_cuda(void* ptr, desul::MemoryScopeNode) {
+  size_t offset = size_t(ptr);
+  offset = offset >> 2;
+  offset = offset & CUDA_SPACE_ATOMIC_MASK;
+  atomicExch(&desul::Impl::CUDA_SPACE_ATOMIC_LOCKS_NODE[offset], 0);
+}
+
+}  // namespace Impl
+}  // namespace desul
+
+// Make lock_array_copied an explicit translation unit scope thingy
+namespace desul {
+namespace Impl {
+namespace {
+static int lock_array_copied = 0;
+inline int eliminate_warning_for_lock_array() { return lock_array_copied; }
+}  // namespace
+}  // namespace Impl
+}  // namespace desul
+/* It is critical that this code be a macro, so that it will
+   capture the right address for desul::Impl::CUDA_SPACE_ATOMIC_LOCKS_DEVICE
+   putting this in an inline function will NOT do the right thing! */
+#define DESUL_IMPL_COPY_CUDA_LOCK_ARRAYS_TO_DEVICE()                       \
+  {                                                                        \
+    if (::desul::Impl::lock_array_copied == 0) {                           \
+      cudaMemcpyToSymbol(::desul::Impl::CUDA_SPACE_ATOMIC_LOCKS_DEVICE,    \
+                         &::desul::Impl::CUDA_SPACE_ATOMIC_LOCKS_DEVICE_h, \
+                         sizeof(int32_t*));                                \
+      cudaMemcpyToSymbol(::desul::Impl::CUDA_SPACE_ATOMIC_LOCKS_NODE,    \
+                         &::desul::Impl::CUDA_SPACE_ATOMIC_LOCKS_NODE_h, \
+                         sizeof(int32_t*));                                \
+    }                                                                      \
+    ::desul::Impl::lock_array_copied = 1;                                  \
+  }
+
+
+#endif /* defined( __CUDACC__ ) */
+
+#endif /* defined( KOKKOS_ENABLE_CUDA ) */
+
+#if defined(__CUDACC_RDC__) || (!defined(__CUDACC__))
+#define DESUL_ENSURE_CUDA_LOCK_ARRAYS_ON_DEVICE()
+#else
+#define DESUL_ENSURE_CUDA_LOCK_ARRAYS_ON_DEVICE() \
+  DESUL_IMPL_COPY_CUDA_LOCK_ARRAYS_TO_DEVICE()
+#endif
+
+#endif /* #ifndef KOKKOS_CUDA_LOCKS_HPP_ */
diff --git a/packages/kokkos/core/src/desul/atomics/Lock_Array_HIP.hpp b/packages/kokkos/core/src/desul/atomics/Lock_Array_HIP.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9e6f5e59800b6778bf2c0592f0104526a730ac00
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Lock_Array_HIP.hpp
@@ -0,0 +1,170 @@
+/*
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATORMICS_LOCK_ARRAY_HIP_HPP_
+#define DESUL_ATORMICS_LOCK_ARRAY_HIP_HPP_
+
+#include "desul/atomics/Common.hpp"
+#include "desul/atomics/Macros.hpp"
+
+#ifdef DESUL_HAVE_HIP_ATOMICS
+
+#include <hip/hip_runtime.h>
+
+#include <cstdint>
+
+namespace desul {
+namespace Impl {
+
+#ifdef __HIP_DEVICE_COMPILE__
+#define DESUL_IMPL_BALLOT_MASK(x) __ballot(x)
+#endif
+
+/**
+ * \brief This global variable in Host space is the central definition of these
+ * arrays.
+ */
+extern int32_t* HIP_SPACE_ATOMIC_LOCKS_DEVICE_h;
+extern int32_t* HIP_SPACE_ATOMIC_LOCKS_NODE_h;
+
+/// \brief After this call, the g_host_cuda_lock_arrays variable has
+///        valid, initialized arrays.
+///
+/// This call is idempotent.
+/// The function is templated to make it a weak symbol to deal with Kokkos/RAJA
+///   snappshotted version while also linking against pure Desul
+template<typename T = int>
+void init_lock_arrays_hip();
+
+/// \brief After this call, the g_host_cuda_lock_arrays variable has
+///        all null pointers, and all array memory has been freed.
+///
+/// This call is idempotent.
+/// The function is templated to make it a weak symbol to deal with Kokkos/RAJA
+///   snappshotted version while also linking against pure Desul
+template<typename T = int>
+void finalize_lock_arrays_hip();
+}  // namespace Impl
+}  // namespace desul
+
+#ifdef __HIPCC__
+namespace desul {
+namespace Impl {
+
+/**
+ * \brief This global variable in HIP space is what kernels use to get access
+ * to the lock arrays.
+ *
+ * When relocatable device code is enabled, there can be one single instance of
+ * this global variable for the entire executable, whose definition will be in
+ * Kokkos_HIP_Locks.cpp (and whose declaration here must then be extern.  This
+ * one instance will be initialized by initialize_host_hip_lock_arrays and need
+ * not be modified afterwards.
+ *
+ * When relocatable device code is disabled, an instance of this variable will
+ * be created in every translation unit that sees this header file (we make this
+ * clear by marking it static, meaning no other translation unit can link to
+ * it). Since the Kokkos_HIP_Locks.cpp translation unit cannot initialize the
+ * instances in other translation units, we must update this CUDA global
+ * variable based on the Host global variable prior to running any kernels that
+ * will use it.  That is the purpose of the
+ * KOKKOS_ENSURE_HIP_LOCK_ARRAYS_ON_DEVICE macro.
+ */
+__device__
+#ifdef DESUL_HIP_RDC
+    __constant__ extern
+#endif
+    int32_t* HIP_SPACE_ATOMIC_LOCKS_DEVICE;
+
+__device__
+#ifdef DESUL_HIP_RDC
+    __constant__ extern
+#endif
+    int32_t* HIP_SPACE_ATOMIC_LOCKS_NODE;
+
+#define HIP_SPACE_ATOMIC_MASK 0x1FFFF
+
+/// \brief Acquire a lock for the address
+///
+/// This function tries to acquire the lock for the hash value derived
+/// from the provided ptr. If the lock is successfully acquired the
+/// function returns true. Otherwise it returns false.
+__device__ inline bool lock_address_hip(void* ptr, desul::MemoryScopeDevice) {
+  size_t offset = size_t(ptr);
+  offset = offset >> 2;
+  offset = offset & HIP_SPACE_ATOMIC_MASK;
+  return (0 == atomicExch(&desul::Impl::HIP_SPACE_ATOMIC_LOCKS_DEVICE[offset], 1));
+}
+
+__device__ inline bool lock_address_hip(void* ptr, desul::MemoryScopeNode) {
+  size_t offset = size_t(ptr);
+  offset = offset >> 2;
+  offset = offset & HIP_SPACE_ATOMIC_MASK;
+  return (0 == atomicExch(&desul::Impl::HIP_SPACE_ATOMIC_LOCKS_NODE[offset], 1));
+}
+
+/**
+ * \brief Release lock for the address
+ *
+ * This function releases the lock for the hash value derived from the provided
+ * ptr. This function should only be called after previously successfully
+ * acquiring a lock with lock_address.
+ */
+__device__ inline void unlock_address_hip(void* ptr, desul::MemoryScopeDevice) {
+  size_t offset = size_t(ptr);
+  offset = offset >> 2;
+  offset = offset & HIP_SPACE_ATOMIC_MASK;
+  atomicExch(&desul::Impl::HIP_SPACE_ATOMIC_LOCKS_DEVICE[offset], 0);
+}
+
+__device__ inline void unlock_address_hip(void* ptr, desul::MemoryScopeNode) {
+  size_t offset = size_t(ptr);
+  offset = offset >> 2;
+  offset = offset & HIP_SPACE_ATOMIC_MASK;
+  atomicExch(&desul::Impl::HIP_SPACE_ATOMIC_LOCKS_NODE[offset], 0);
+}
+#endif
+}  // namespace Impl
+}  // namespace desul
+
+// Make lock_array_copied an explicit translation unit scope thing
+namespace desul {
+namespace Impl {
+namespace {
+static int lock_array_copied = 0;
+inline int eliminate_warning_for_lock_array() { return lock_array_copied; }
+}  // namespace
+}  // namespace Impl
+}  // namespace desul
+
+/* It is critical that this code be a macro, so that it will
+   capture the right address for g_device_hip_lock_arrays!
+   putting this in an inline function will NOT do the right thing! */
+#define DESUL_IMPL_COPY_HIP_LOCK_ARRAYS_TO_DEVICE()                               \
+  {                                                                               \
+    if (::desul::Impl::lock_array_copied == 0) {                                  \
+      (void) hipMemcpyToSymbol(HIP_SYMBOL(::desul::Impl::HIP_SPACE_ATOMIC_LOCKS_DEVICE), \
+                        &::desul::Impl::HIP_SPACE_ATOMIC_LOCKS_DEVICE_h,          \
+                        sizeof(int32_t*));                                        \
+      (void) hipMemcpyToSymbol(HIP_SYMBOL(::desul::Impl::HIP_SPACE_ATOMIC_LOCKS_NODE),   \
+                        &::desul::Impl::HIP_SPACE_ATOMIC_LOCKS_NODE_h,            \
+                        sizeof(int32_t*));                                        \
+    }                                                                             \
+    ::desul::Impl::lock_array_copied = 1;                                         \
+  }
+
+#endif
+
+#if defined(DESUL_HIP_RDC) || (!defined(__HIPCC__))
+#define DESUL_ENSURE_HIP_LOCK_ARRAYS_ON_DEVICE()
+#else
+#define DESUL_ENSURE_HIP_LOCK_ARRAYS_ON_DEVICE() \
+  DESUL_IMPL_COPY_HIP_LOCK_ARRAYS_TO_DEVICE()
+#endif
+
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/Macros.hpp b/packages/kokkos/core/src/desul/atomics/Macros.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..db9962e03bd84052a4d61a89cf39892e40051b89
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/Macros.hpp
@@ -0,0 +1,62 @@
+/*
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_MACROS_HPP_
+#define DESUL_ATOMICS_MACROS_HPP_
+
+// Macros
+
+#if defined(__GNUC__) && \
+    (!defined(__CUDA_ARCH__) || !defined(__NVCC__)) && \
+    (!defined(__HIP_DEVICE_COMPILE) || !defined(__HIP_PLATFORM_HCC__)) && \
+    !defined(__SYCL_DEVICE_ONLY__) && \
+    !defined(DESUL_HAVE_OPENMP_ATOMICS) && \
+    !defined(DESUL_HAVE_SERIAL_ATOMICS)
+#define DESUL_HAVE_GCC_ATOMICS
+#endif
+
+#ifdef _MSC_VER
+#define DESUL_HAVE_MSVC_ATOMICS
+#endif
+
+#ifdef __CUDACC__
+#define DESUL_HAVE_CUDA_ATOMICS
+#endif
+
+#ifdef __HIPCC__
+#define DESUL_HAVE_HIP_ATOMICS
+#endif
+
+#ifdef __SYCL_DEVICE_ONLY__
+#define DESUL_HAVE_SYCL_ATOMICS
+#ifdef __clang__
+#define DESUL_SYCL_NAMESPACE sycl::ONEAPI
+#else
+#define DESUL_SYCL_NAMESPACE sycl
+#endif
+#endif
+
+#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) || defined(__SYCL_DEVICE_ONLY__)
+#define DESUL_HAVE_GPU_LIKE_PROGRESS
+#endif
+
+#if defined(DESUL_HAVE_CUDA_ATOMICS) || defined(DESUL_HAVE_HIP_ATOMICS)
+#define DESUL_FORCEINLINE_FUNCTION inline __host__ __device__
+#define DESUL_INLINE_FUNCTION inline __host__ __device__
+#define DESUL_FUNCTION __host__ __device__
+#else
+#define DESUL_FORCEINLINE_FUNCTION inline
+#define DESUL_INLINE_FUNCTION inline
+#define DESUL_FUNCTION
+#endif
+
+#if !defined(DESUL_HAVE_GPU_LIKE_PROGRESS)
+#define DESUL_HAVE_FORWARD_PROGRESS
+#endif
+
+#endif  // DESUL_ATOMICS_MACROS_HPP_
diff --git a/packages/kokkos/core/src/desul/atomics/OpenMP.hpp b/packages/kokkos/core/src/desul/atomics/OpenMP.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3fa22c36aca37e9e91e5b08aac9b3e61b8256ebc
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/OpenMP.hpp
@@ -0,0 +1,15 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+#ifndef DESUL_ATOMICS_OPENMP_HPP_
+#define DESUL_ATOMICS_OPENMP_HPP_
+
+#ifdef DESUL_HAVE_OPENMP_ATOMICS
+
+#include<desul/atomics/openmp/OpenMP_40.hpp>
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/SYCL.hpp b/packages/kokkos/core/src/desul/atomics/SYCL.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..44e2dc0ec4ea843d6b6b4e9896b27fc63df6baad
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/SYCL.hpp
@@ -0,0 +1,143 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+#ifndef DESUL_ATOMICS_SYCL_HPP_
+#define DESUL_ATOMICS_SYCL_HPP_
+
+#ifdef DESUL_HAVE_SYCL_ATOMICS
+#include "desul/atomics/Common.hpp"
+
+namespace desul {
+namespace Impl {
+template<class T>
+struct is_sycl_atomic_type {
+  static constexpr bool value = std::is_same<T, int>::value ||
+                                std::is_same<T, unsigned int>::value ||
+				std::is_same<T, long>::value ||
+				std::is_same<T, unsigned long>::value ||
+				std::is_same<T, long long>::value ||
+                                std::is_same<T, unsigned long long int>::value ||
+				std::is_same<T, float>::value ||
+				std::is_same<T, double>::value;
+};
+} // Impl
+
+// Atomic Add
+template<class T, class MemoryOrder/*, class MemoryScope*/>
+inline
+typename std::enable_if<Impl::is_sycl_atomic_type<T>::value,T>::type
+atomic_fetch_add(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    T, 
+    DesulToSYCLMemoryOrder<MemoryOrder>::value, 
+    DesulToSYCLMemoryScope<MemoryScopeDevice>::value,  
+    sycl::access::address_space::global_device_space> 
+  dest_ref(*dest);
+  return dest_ref.fetch_add(val);
+}
+
+// Atomic Sub 
+template<class T, class MemoryOrder/*, class MemoryScope*/>
+inline
+typename std::enable_if<Impl::is_sycl_atomic_type<T>::value,T>::type
+atomic_fetch_sub(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    T,
+    DesulToSYCLMemoryOrder<MemoryOrder>::value,
+    DesulToSYCLMemoryScope<MemoryScopeDevice>::value,
+    sycl::access::address_space::global_device_space>
+  dest_ref(*dest);
+  return dest_ref.fetch_sub(val);
+}
+
+// Atomic Inc
+template<class MemoryOrder/*, class MemoryScope*/>
+inline
+unsigned int atomic_fetch_inc(unsigned int* dest, unsigned int val, MemoryOrder memory_order, MemoryScopeDevice memory_scope) {
+  return atomic_fetch_add(dest, val, memory_order, memory_scope);
+}
+
+// Atomic Dec
+template<class MemoryOrder/*, class MemoryScope*/>
+inline
+unsigned int atomic_fetch_dec(unsigned int* dest, unsigned int val, MemoryOrder memory_order, MemoryScopeDevice memory_scope) {
+  return atomic_fetch_sub(dest, val, memory_order, memory_scope);
+}
+
+// Atomic Max
+template<class T, class MemoryOrder/*, class MemoryScope*/>
+inline
+typename std::enable_if<Impl::is_sycl_atomic_type<T>::value,T>::type
+atomic_fetch_max(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    T,
+    DesulToSYCLMemoryOrder<MemoryOrder>::value,
+    DesulToSYCLMemoryScope<MemoryScopeDevice>::value,
+    sycl::access::address_space::global_device_space>
+  dest_ref(*dest);
+  return dest_ref.fetch_max(val);
+}
+
+// Atomic Min
+template<class T, class MemoryOrder/*, class MemoryScope*/>
+inline
+typename std::enable_if<Impl::is_sycl_atomic_type<T>::value,T>::type
+atomic_fetch_min(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    T,
+    DesulToSYCLMemoryOrder<MemoryOrder>::value,
+    DesulToSYCLMemoryScope<MemoryScopeDevice>::value,
+    sycl::access::address_space::global_device_space>
+  dest_ref(*dest);
+  return dest_ref.fetch_min(val);
+}
+
+// Atomic And
+template<class T, class MemoryOrder/*, class MemoryScope*/>
+inline
+typename std::enable_if<Impl::is_sycl_atomic_type<T>::value,T>::type
+atomic_fetch_and(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    T,
+    DesulToSYCLMemoryOrder<MemoryOrder>::value,
+    DesulToSYCLMemoryScope<MemoryScopeDevice>::value,
+    sycl::access::address_space::global_device_space>
+  dest_ref(*dest);
+  return dest_ref.fetch_and(val);
+}
+
+// Atomic XOR
+template<class T, class MemoryOrder/*, class MemoryScope*/>
+inline
+typename std::enable_if<Impl::is_sycl_atomic_type<T>::value,T>::type
+atomic_fetch_xor(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    T,
+    DesulToSYCLMemoryOrder<MemoryOrder>::value,
+    DesulToSYCLMemoryScope<MemoryScopeDevice>::value,
+    sycl::access::address_space::global_device_space>
+  dest_ref(*dest);
+  return dest_ref.fetch_xor(val);
+}
+
+// Atomic OR
+template<class T, class MemoryOrder/*, class MemoryScope*/>
+inline
+typename std::enable_if<Impl::is_sycl_atomic_type<T>::value,T>::type
+atomic_fetch_or(T* dest, T val, MemoryOrder, MemoryScopeDevice) {
+  DESUL_SYCL_NAMESPACE::atomic_ref<
+    T,
+    DesulToSYCLMemoryOrder<MemoryOrder>::value,
+    DesulToSYCLMemoryScope<MemoryScopeDevice>::value,
+    sycl::access::address_space::global_device_space>
+  dest_ref(*dest);
+  return dest_ref.fetch_or(val);
+}
+
+} // desul
+#endif  // DESUL_HAVE_SYCL_ATOMICS
+#endif  // DESUL_ATOMICS_SYCL_HPP_
diff --git a/packages/kokkos/core/src/desul/atomics/SYCLConversions.hpp b/packages/kokkos/core/src/desul/atomics/SYCLConversions.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a66e5cf051f684695b17b7fae9fe2aaa5009a2c3
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/SYCLConversions.hpp
@@ -0,0 +1,58 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_SYCL_CONVERSIONS_HPP_
+#define DESUL_ATOMICS_SYCL_CONVERSIONS_HPP_
+#ifdef DESUL_HAVE_SYCL_ATOMICS
+#include "desul/atomics/Common.hpp"
+#include <CL/sycl.hpp>
+
+namespace desul {
+
+template<class MemoryOrder>
+struct DesulToSYCLMemoryOrder;
+template<>
+struct DesulToSYCLMemoryOrder<MemoryOrderSeqCst> {
+  static constexpr DESUL_SYCL_NAMESPACE::memory_order value = DESUL_SYCL_NAMESPACE::memory_order::seq_cst;
+};
+template<>
+struct DesulToSYCLMemoryOrder<MemoryOrderAcquire> {
+  static constexpr DESUL_SYCL_NAMESPACE::memory_order value = DESUL_SYCL_NAMESPACE::memory_order::acquire;
+};
+template<>
+struct DesulToSYCLMemoryOrder<MemoryOrderRelease> {
+  static constexpr DESUL_SYCL_NAMESPACE::memory_order value = DESUL_SYCL_NAMESPACE::memory_order::release;
+};
+template<>
+struct DesulToSYCLMemoryOrder<MemoryOrderAcqRel> {
+  static constexpr DESUL_SYCL_NAMESPACE::memory_order value = DESUL_SYCL_NAMESPACE::memory_order::acq_rel;
+};
+template<>
+struct DesulToSYCLMemoryOrder<MemoryOrderRelaxed> {
+  static constexpr DESUL_SYCL_NAMESPACE::memory_order value = DESUL_SYCL_NAMESPACE::memory_order::relaxed;
+};
+
+template<class MemoryScope>
+struct DesulToSYCLMemoryScope;
+template<>
+struct DesulToSYCLMemoryScope<MemoryScopeCore> {
+  static constexpr DESUL_SYCL_NAMESPACE::memory_scope value = DESUL_SYCL_NAMESPACE::memory_scope::work_group;
+};
+template<>
+struct DesulToSYCLMemoryScope<MemoryScopeDevice> {
+  static constexpr DESUL_SYCL_NAMESPACE::memory_scope value = DESUL_SYCL_NAMESPACE::memory_scope::device;
+};
+template<>
+struct DesulToSYCLMemoryScope<MemoryScopeSystem> {
+  static constexpr DESUL_SYCL_NAMESPACE::memory_scope value = DESUL_SYCL_NAMESPACE::memory_scope::system;
+};
+
+}
+
+#endif
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/CUDA_asm.hpp b/packages/kokkos/core/src/desul/atomics/cuda/CUDA_asm.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..461d3e0928a19840973d1c971ccd100968193a42
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/CUDA_asm.hpp
@@ -0,0 +1,18 @@
+#include<limits>
+namespace desul {
+#if defined(__CUDA_ARCH__)  || (defined(__clang__) && !defined(__NVCC__))
+// Choose the variant of atomics we are using later
+#if !defined(DESUL_IMPL_ATOMIC_CUDA_PTX_GENERIC) && \
+    !defined(DESUL_IMPL_ATOMIC_CUDA_PTX_PREDICATE) && \
+    !defined(DESUL_IMPL_ATOMIC_CUDA_PTX_ISGLOBAL) && \
+    !defined(DESUL_IMPL_ATOMIC_CUDA_PTX_FORCEGLOBAL)
+#if (__CUDACC_VER_MAJOR__ > 11) || ((__CUDACC_VER_MAJOR__==11) && (__CUDACC_VER_MINOR__>1))
+#define DESUL_IMPL_ATOMIC_CUDA_PTX_ISGLOBAL
+#else
+#define DESUL_IMPL_ATOMIC_CUDA_PTX_PREDICATE
+#endif
+#endif
+#include<desul/atomics/cuda/cuda_cc7_asm.inc>
+
+#endif
+}
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/CUDA_asm_exchange.hpp b/packages/kokkos/core/src/desul/atomics/cuda/CUDA_asm_exchange.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0ab95e6a00a4e67ffad0232f8652e8a0c9d4f6e6
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/CUDA_asm_exchange.hpp
@@ -0,0 +1,8 @@
+#include<limits>
+namespace desul {
+#if defined(__CUDA_ARCH__)  || (defined(__clang__) && !defined(__NVCC__))
+
+#include<desul/atomics/cuda/cuda_cc7_asm_exchange.inc>
+
+#endif
+}
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm.inc b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm.inc
new file mode 100644
index 0000000000000000000000000000000000000000..2bc64a74b2caf06a731a319c07c7b26490924aa3
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm.inc
@@ -0,0 +1,20 @@
+
+// Non returning atomic operation (ptx red instruction) only exists for relaxed and release memorder
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE MemoryScopeDevice
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".gpu"
+#include "desul/atomics/cuda/cuda_cc7_asm_memorder.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE MemoryScopeNode
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".sys"
+#include "desul/atomics/cuda/cuda_cc7_asm_memorder.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE MemoryScopeCore
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".cta"
+#include "desul/atomics/cuda/cuda_cc7_asm_memorder.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM
+
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc
new file mode 100644
index 0000000000000000000000000000000000000000..6de590a952fa38f60c58a34c9499a420502ae381
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc
@@ -0,0 +1,18 @@
+#ifdef DESUL_IMPL_ATOMIC_CUDA_PTX_GENERIC
+#include "cuda_cc7_asm_atomic_fetch_op.inc_generic"
+#endif
+
+#ifdef DESUL_IMPL_ATOMIC_CUDA_PTX_ISGLOBAL
+#include "cuda_cc7_asm_atomic_fetch_op.inc_isglobal"
+#endif
+
+#ifdef DESUL_IMPL_ATOMIC_CUDA_PTX_PREDICATE
+#include "cuda_cc7_asm_atomic_fetch_op.inc_predicate"
+#endif
+
+// This version is not generally safe
+// Only here for performance comparison purposes
+#ifdef DESUL_IMPL_ATOMIC_CUDA_PTX_FORCEGLOBAL
+#include "cuda_cc7_asm_atomic_fetch_op.inc_forceglobal"
+#endif
+
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_forceglobal b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_forceglobal
new file mode 100644
index 0000000000000000000000000000000000000000..d00e2223d22485f4ee831dd53dce064a49deec5e
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_forceglobal
@@ -0,0 +1,143 @@
+
+// Inline PTX: h u16 , r u32,  l u64, f f32, d f64
+// Ops:
+
+// binary operations
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_and(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile("atom.and.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_and(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile("atom.and.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_OR() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_or(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile("atom.or.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_or(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile("atom.or.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_XOR() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_xor(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile("atom.xor.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_xor(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile("atom.xor.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+// Fetch atomics
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_add(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  asm volatile("atom.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_sub(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  ctype neg_value = -value; \
+  asm volatile("atom.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(neg_value) : "memory"); \
+  return result; \
+}
+
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_min(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  asm volatile("atom.min.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_max(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  asm volatile("atom.max.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_inc(ctype* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result = 0; \
+  ctype limit = desul::Impl::numeric_limits_max<ctype>::value; \
+  asm volatile("atom.inc.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_dec(ctype* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result = 0; \
+  ctype limit = desul::Impl::numeric_limits_max<ctype>::value; \
+  asm volatile("atom.dec.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  return result; \
+}
+
+// Group ops for integer ctypes
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_BIN_OP(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_OR(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_XOR(ctype)
+
+
+// Instantiate Functions
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(float,".f32","f","=f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(float,".f32","f","=f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(double,".f64","d","=d")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(double,".f64","d","=d")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(uint32_t,".u32","r","=r")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(uint64_t,".u64","l","=l")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(int32_t,".s32","r","=r")
+//__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(int64_t,".s64","l","=l")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC(uint32_t,".u32","r","=r")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC(uint32_t,".u32","r","=r")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_BIN_OP()
+
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND
+
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_generic b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_generic
new file mode 100644
index 0000000000000000000000000000000000000000..364b6a2e4d1950f110c29958976a660d01d05771
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_generic
@@ -0,0 +1,142 @@
+
+// Inline PTX: h u16 , r u32,  l u64, f f32, d f64
+// Ops: 
+
+// binary operations
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_and(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile("atom.and" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_and(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile("atom.and" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_OR() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_or(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile("atom.or" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_or(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile("atom.or" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_XOR() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_xor(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile("atom.xor" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_xor(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile("atom.xor" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+// Fetch atomics
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_add(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  asm volatile("atom.add" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_sub(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  ctype neg_value = -value; \
+  asm volatile("atom.add" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(neg_value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_min(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  asm volatile("atom.min" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_max(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  asm volatile("atom.max" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_inc(ctype* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result = 0; \
+  ctype limit = desul::Impl::numeric_limits_max<ctype>::value; \
+  asm volatile("atom.inc" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_dec(ctype* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result = 0; \
+  ctype limit = desul::Impl::numeric_limits_max<ctype>::value; \
+  asm volatile("atom.dec" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  return result; \
+}
+
+// Group ops for integer ctypes
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_BIN_OP(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_OR(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_XOR(ctype)
+
+
+// Instantiate Functions
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(float,".f32","f","=f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(float,".f32","f","=f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(double,".f64","d","=d")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(double,".f64","d","=d")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(uint32_t,".u32","r","=r")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(uint64_t,".u64","l","=l")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(int32_t,".s32","r","=r")
+//__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(int64_t,".s64","l","=l")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC(uint32_t,".u32","r","=r")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC(uint32_t,".u32","r","=r")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_BIN_OP()
+
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND
+
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_isglobal b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_isglobal
new file mode 100644
index 0000000000000000000000000000000000000000..2e8e54062dd3494f7440b959618359ef0547d87b
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_isglobal
@@ -0,0 +1,190 @@
+
+// Inline PTX: h u16 , r u32,  l u64, f f32, d f64
+// Ops:
+
+// binary operations
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_and(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.and.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  } else { \
+  asm volatile("atom.and"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  } \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_and(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.and.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  } else { \
+  asm volatile("atom.and"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  } \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_OR() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_or(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.or.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  } else { \
+  asm volatile("atom.or"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  } \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_or(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.or.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  } else { \
+  asm volatile("atom.or"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  } \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_XOR() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_xor(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.xor.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  } else { \
+  asm volatile("atom.xor"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  } \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_xor(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.xor.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  } else { \
+  asm volatile("atom.xor"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  } \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+// Fetch atomics
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_add(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  } else { \
+  asm volatile("atom.add" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  } \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_sub(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  ctype neg_value = -value; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(neg_value) : "memory"); \
+  } else { \
+  asm volatile("atom.add" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(neg_value) : "memory"); \
+  } \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_min(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.min.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  } else { \
+  asm volatile("atom.min"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  } \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_max(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.max.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  } else { \
+  asm volatile("atom.max"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  } \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_inc(ctype* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result = 0; \
+  ctype limit = desul::Impl::numeric_limits_max<ctype>::value; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.inc.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  } else { \
+  asm volatile("atom.inc"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  } \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_dec(ctype* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result = 0; \
+  ctype limit = desul::Impl::numeric_limits_max<ctype>::value; \
+  if(__isGlobal(dest)) { \
+  asm volatile("atom.dec.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  } else { \
+  asm volatile("atom.dec"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;" : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  } \
+  return result; \
+}
+
+// Group ops for integer ctypes
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_BIN_OP(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_OR(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_XOR(ctype)
+
+
+// Instantiate Functions
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(float,".f32","f","=f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(float,".f32","f","=f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(double,".f64","d","=d")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(double,".f64","d","=d")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(uint32_t,".u32","r","=r")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(uint64_t,".u64","l","=l")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(int32_t,".s32","r","=r")
+//__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(int64_t,".s64","l","=l")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC(uint32_t,".u32","r","=r")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC(uint32_t,".u32","r","=r")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_BIN_OP()
+
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND
+
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_predicate b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_predicate
new file mode 100644
index 0000000000000000000000000000000000000000..5f53279daf541aad169e1bc5a046518cc5b084c8
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc_predicate
@@ -0,0 +1,226 @@
+
+// Inline PTX: h u16 , r u32,  l u64, f f32, d f64
+// Ops:
+
+// binary operations
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_and(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.and.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;\n\t" \
+          "@!p atom.and"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_and(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.and.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;\n\t" \
+          "@!p atom.and"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_OR() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_or(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.or.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;\n\t" \
+          "@!p atom.or"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_or(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.or.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;\n\t" \
+          "@!p atom.or"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_XOR() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_fetch_xor(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.xor.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;\n\t" \
+          "@!p atom.xor"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_fetch_xor(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.xor.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;\n\t" \
+          "@!p atom.xor"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+// Fetch atomics
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_add(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "@!p atom.add"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_sub(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  ctype neg_value = -value; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "@!p atom.add"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : reg_ret_ctype(result) : "l"(dest),reg_ctype(neg_value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_min(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.min.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "@!p atom.min"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_max(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result=0; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.max.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "@!p atom.max"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : reg_ret_ctype(result) : "l"(dest),reg_ctype(value) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_inc(ctype* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result = 0; \
+  ctype limit = desul::Impl::numeric_limits_max<ctype>::value; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.inc.gobal" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "@!p atom.inc"       __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  return result; \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+inline __device__ ctype atomic_fetch_dec(ctype* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  ctype result = 0; \
+  ctype limit = desul::Impl::numeric_limits_max<ctype>::value; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %1;\n\t" \
+          "@p  atom.dec.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "@!p atom.dec"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_ctype " %0,[%1],%2;\n\t" \
+          "}\n\t" \
+    : reg_ret_ctype(result) : "l"(dest),reg_ctype(limit) : "memory"); \
+  return result; \
+}
+
+// Group ops for integer ctypes
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN(ctype,asm_ctype,reg_ctype,reg_ret_ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX(ctype,asm_ctype,reg_ctype,reg_ret_ctype)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_BIN_OP(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_OR(ctype) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_XOR(ctype)
+
+
+// Instantiate Functions
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(float,".f32","f","=f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(float,".f32","f","=f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD(double,".f64","d","=d")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_SUB(double,".f64","d","=d")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(uint32_t,".u32","r","=r")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_UNSIGNED_OP(uint64_t,".u64","l","=l")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(int32_t,".s32","r","=r")
+//__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INTEGER_OP(int64_t,".s64","l","=l")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC(uint32_t,".u32","r","=r")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC(uint32_t,".u32","r","=r")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_BIN_OP()
+
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_ADD
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MIN
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_MAX
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_INC
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_DEC
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_FETCH_AND
+
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc
new file mode 100644
index 0000000000000000000000000000000000000000..ca02410515db3491c002b569f01ee150d1c0c683
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc
@@ -0,0 +1,18 @@
+#ifdef DESUL_IMPL_ATOMIC_CUDA_PTX_GENERIC
+#include "cuda_cc7_asm_atomic_op.inc_generic"
+#endif
+
+#ifdef DESUL_IMPL_ATOMIC_CUDA_PTX_ISGLOBAL
+#include "cuda_cc7_asm_atomic_op.inc_isglobal"
+#endif
+
+#ifdef DESUL_IMPL_ATOMIC_CUDA_PTX_PREDICATE
+#include "cuda_cc7_asm_atomic_op.inc_predicate"
+#endif
+
+// This version is not generally safe
+// Only here for performance comparison purposes
+#ifdef DESUL_IMPL_ATOMIC_CUDA_PTX_FORCEGLOBAL
+#include "cuda_cc7_asm_atomic_op.inc_forceglobal"
+#endif
+
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_forceglobal b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_forceglobal
new file mode 100644
index 0000000000000000000000000000000000000000..3767b2ab4980c0d811357a6d0e1a912de5bba500
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_forceglobal
@@ -0,0 +1,64 @@
+
+// Inline PTX: h u16 , r u32,  l u64, f f32, d f64
+// Ops:
+
+// Non Returning Atomic Operations
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+inline __device__ void atomic_add(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  asm volatile("red.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+inline __device__ void atomic_sub(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type neg_value = -value; \
+  asm volatile("red.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(neg_value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+inline __device__ void atomic_min(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  asm volatile("red.min.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type) \
+inline __device__ void atomic_max(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  asm volatile("red.max.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_INC(type,asm_type,reg_type) \
+inline __device__ void atomic_inc(type* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type limit = desul::Impl::numeric_limits_max<type>::value; \
+  asm volatile("red.inc.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(limit) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_DEC(type,asm_type,reg_type) \
+inline __device__ void atomic_dec(type* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type limit = desul::Impl::numeric_limits_max<type>::value; \
+  asm volatile("red.dec.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(limit) : "memory"); \
+}
+
+// Group ops for integer types
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_UNSIGNED_OP(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INC(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_DEC(type,asm_type,reg_type)
+
+// Instantiate Functions
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(float,".f32","f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(float,".f32","f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(double,".f64","d")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(double,".f64","d")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_UNSIGNED_OP(uint32_t,".u32","r")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int64_t,".u64","l")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int32_t,".s32","r")
+//__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int64_t,".s64","l")
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_generic b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_generic
new file mode 100644
index 0000000000000000000000000000000000000000..5de36a3e0a87b967fff5d9a936644c5e8a566051
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_generic
@@ -0,0 +1,64 @@
+
+// Inline PTX: h u16 , r u32,  l u64, f f32, d f64
+// Ops:
+
+// Non Returning Atomic Operations
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+inline __device__ void atomic_add(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  asm volatile("red.add" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+inline __device__ void atomic_sub(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type neg_value = -value; \
+  asm volatile("red.add" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(neg_value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+inline __device__ void atomic_min(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  asm volatile("red.min" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type) \
+inline __device__ void atomic_max(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  asm volatile("red.max" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_INC(type,asm_type,reg_type) \
+inline __device__ void atomic_inc(type* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type limit = desul::Impl::numeric_limits_max<type>::value; \
+  asm volatile("red.inc" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(limit) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_DEC(type,asm_type,reg_type) \
+inline __device__ void atomic_dec(type* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type limit = desul::Impl::numeric_limits_max<type>::value; \
+  asm volatile("red.dec" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(limit) : "memory"); \
+}
+
+// Group ops for integer types
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_UNSIGNED_OP(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INC(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_DEC(type,asm_type,reg_type)
+
+// Instantiate Functions
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(float,".f32","f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(float,".f32","f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(double,".f64","d")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(double,".f64","d")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_UNSIGNED_OP(uint32_t,".u32","r")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int64_t,".u64","l")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int32_t,".s32","r")
+//__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int64_t,".s64","l")
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_isglobal b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_isglobal
new file mode 100644
index 0000000000000000000000000000000000000000..ba8937883423e62b9461b6d764d24022531db719
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_isglobal
@@ -0,0 +1,88 @@
+
+// Inline PTX: h u16 , r u32,  l u64, f f32, d f64
+// Ops:
+
+// Non Returning Atomic Operations
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+inline __device__ void atomic_add(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  if(__isGlobal(dest)) { \
+  asm volatile("red.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+  } else { \
+  asm volatile("red.add"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+  } \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+inline __device__ void atomic_sub(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type neg_value = -value; \
+  if(__isGlobal(dest)) { \
+  asm volatile("red.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(neg_value) : "memory"); \
+  } else { \
+  asm volatile("red.add"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(neg_value) : "memory"); \
+  } \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+inline __device__ void atomic_min(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  if(__isGlobal(dest)) { \
+  asm volatile("red.min.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+  } else { \
+  asm volatile("red.min"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+  } \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type) \
+inline __device__ void atomic_max(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  if(__isGlobal(dest)) { \
+  asm volatile("red.max.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+  } else { \
+  asm volatile("red.max"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(value) : "memory"); \
+  } \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_INC(type,asm_type,reg_type) \
+inline __device__ void atomic_inc(type* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type limit = desul::Impl::numeric_limits_max<type>::value; \
+  if(__isGlobal(dest)) { \
+  asm volatile("red.inc.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(limit) : "memory"); \
+  } else { \
+  asm volatile("red.inc"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(limit) : "memory"); \
+  } \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_DEC(type,asm_type,reg_type) \
+inline __device__ void atomic_dec(type* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type limit = desul::Impl::numeric_limits_max<type>::value; \
+  if(__isGlobal(dest)) { \
+  asm volatile("red.dec.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(limit) : "memory"); \
+  } else { \
+  asm volatile("red.dec"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;" :: "l"(dest),reg_type(limit) : "memory"); \
+  } \
+}
+
+// Group ops for integer types
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_UNSIGNED_OP(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INC(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_DEC(type,asm_type,reg_type)
+
+// Instantiate Functions
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(float,".f32","f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(float,".f32","f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(double,".f64","d")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(double,".f64","d")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_UNSIGNED_OP(uint32_t,".u32","r")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int64_t,".u64","l")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int32_t,".s32","r")
+//__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int64_t,".s64","l")
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_predicate b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_predicate
new file mode 100644
index 0000000000000000000000000000000000000000..46e0ccf5e747e151039f68e17a72c82a05fc14fc
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc_predicate
@@ -0,0 +1,106 @@
+
+// Inline PTX: h u16 , r u32,  l u64, f f32, d f64
+// Ops:
+
+// Non Returning Atomic Operations
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+inline __device__ void atomic_add(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %0;\n\t" \
+          "@p  red.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "@!p red.add"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "}\n\t" \
+    :: "l"(dest),reg_type(value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+inline __device__ void atomic_sub(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type neg_value = -value; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %0;\n\t" \
+          "@p  red.add.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "@!p red.add"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "}\n\t" \
+    :: "l"(dest),reg_type(neg_value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+inline __device__ void atomic_min(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %0;\n\t" \
+          "@p  red.min.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "@!p red.min"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "}\n\t" \
+    :: "l"(dest),reg_type(value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type) \
+inline __device__ void atomic_max(type* dest, type value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %0;\n\t" \
+          "@p  red.max.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "@!p red.max"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "}\n\t" \
+    :: "l"(dest),reg_type(value) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_INC(type,asm_type,reg_type) \
+inline __device__ void atomic_inc(type* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type limit = desul::Impl::numeric_limits_max<type>::value; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %0;\n\t" \
+          "@p  red.inc.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "@!p red.inc"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "}\n\t" \
+    :: "l"(dest),reg_type(limit) : "memory"); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_DEC(type,asm_type,reg_type) \
+inline __device__ void atomic_dec(type* dest, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  type limit = desul::Impl::numeric_limits_max<type>::value; \
+  asm volatile( \
+          "{\n\t" \
+          ".reg .pred p;\n\t" \
+          "isspacep.global p, %0;\n\t" \
+          "@p  red.dec.global" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "@!p red.dec"        __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM asm_type " [%0],%1;\n\t" \
+          "}\n\t" \
+    :: "l"(dest),reg_type(limit) : "memory"); \
+}
+
+// Group ops for integer types
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type)
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_UNSIGNED_OP(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MIN(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_MAX(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INC(type,asm_type,reg_type) \
+__DESUL_IMPL_CUDA_ASM_ATOMIC_DEC(type,asm_type,reg_type)
+
+// Instantiate Functions
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(float,".f32","f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(float,".f32","f")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_ADD(double,".f64","d")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_SUB(double,".f64","d")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_UNSIGNED_OP(uint32_t,".u32","r")
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int64_t,".u64","l")
+__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int32_t,".s32","r")
+//__DESUL_IMPL_CUDA_ASM_ATOMIC_INTEGER_OP(int64_t,".s64","l")
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_exchange.inc b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_exchange.inc
new file mode 100644
index 0000000000000000000000000000000000000000..dfd211249fcc625733e1737d9f2258bf9d066ef9
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_exchange.inc
@@ -0,0 +1,20 @@
+
+// Non returning atomic operation (ptx red instruction) only exists for relaxed and release memorder
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE MemoryScopeDevice
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".gpu"
+#include "desul/atomics/cuda/cuda_cc7_asm_exchange_memorder.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE MemoryScopeNode
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".sys"
+#include "desul/atomics/cuda/cuda_cc7_asm_exchange_memorder.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE MemoryScopeCore
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".cta"
+#include "desul/atomics/cuda/cuda_cc7_asm_exchange_memorder.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM
+
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_exchange_memorder.inc b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_exchange_memorder.inc
new file mode 100644
index 0000000000000000000000000000000000000000..7b4f7d094e8ac566213b6f11938dab721da003fa
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_exchange_memorder.inc
@@ -0,0 +1,27 @@
+
+// Non returning atomic operation (ptx red instruction) only exists for relaxed and release memorder
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER MemoryOrderRelaxed
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM ".relaxed"
+#include "desul/atomics/cuda/cuda_cc7_asm_exchange_op.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER MemoryOrderRelease
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM ".release"
+#include "desul/atomics/cuda/cuda_cc7_asm_exchange_op.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM
+
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER MemoryOrderAcquire
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM ".acquire"
+#include "desul/atomics/cuda/cuda_cc7_asm_exchange_op.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER MemoryOrderAcqRel
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM ".acq_rel"
+#include "desul/atomics/cuda/cuda_cc7_asm_exchange_op.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM
+
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_exchange_op.inc b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_exchange_op.inc
new file mode 100644
index 0000000000000000000000000000000000000000..51d992087e35f8842fbc143f8e903273499d4507
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_exchange_op.inc
@@ -0,0 +1,40 @@
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_EXCHANGE() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_exchange(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_result = 0u; \
+  asm volatile("atom.exch" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2;" : "=r"(asm_result) : "l"(dest),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_exchange(ctype* dest, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_result = 0u; \
+  asm volatile("atom.exch" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2;" : "=l"(asm_result) : "l"(dest),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+#define __DESUL_IMPL_CUDA_ASM_ATOMIC_COMPARE_EXCHANGE() \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==4, ctype>::type atomic_compare_exchange(ctype* dest, ctype compare, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint32_t asm_value = reinterpret_cast<uint32_t&>(value); \
+  uint32_t asm_compare = reinterpret_cast<uint32_t&>(compare); \
+  uint32_t asm_result = 0u; \
+  asm volatile("atom.cas" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b32" " %0,[%1],%2,%3;" : "=r"(asm_result) : "l"(dest),"r"(asm_compare),"r"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+} \
+template<class ctype> \
+inline __device__ typename std::enable_if<sizeof(ctype)==8, ctype>::type atomic_compare_exchange(ctype* dest, ctype compare, ctype value, __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER, __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE) { \
+  uint64_t asm_value = reinterpret_cast<uint64_t&>(value); \
+  uint64_t asm_compare = reinterpret_cast<uint64_t&>(compare); \
+  uint64_t asm_result = 0u; \
+  asm volatile("atom.cas" __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM __DESUL_IMPL_CUDA_ASM_MEMORY_SCOPE_ASM ".b64" " %0,[%1],%2,%3;" : "=l"(asm_result) : "l"(dest),"l"(asm_compare),"l"(asm_value) : "memory"); \
+  return reinterpret_cast<ctype&>(asm_result); \
+}
+
+__DESUL_IMPL_CUDA_ASM_ATOMIC_EXCHANGE()
+__DESUL_IMPL_CUDA_ASM_ATOMIC_COMPARE_EXCHANGE()
+
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_EXCHANGE
+#undef __DESUL_IMPL_CUDA_ASM_ATOMIC_COMPARE_EXCHANGE
diff --git a/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_memorder.inc b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_memorder.inc
new file mode 100644
index 0000000000000000000000000000000000000000..3eb613d8a74c5bfc911bd51787dac9c4a972f698
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/cuda/cuda_cc7_asm_memorder.inc
@@ -0,0 +1,29 @@
+
+// Non returning atomic operation (ptx red instruction) only exists for relaxed and release memorder
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER MemoryOrderRelaxed
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM ".relaxed"
+#include "desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc"
+#include "desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER MemoryOrderRelease
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM ".release"
+#include "desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc"
+#include "desul/atomics/cuda/cuda_cc7_asm_atomic_op.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM
+
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER MemoryOrderAcquire
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM ".acquire"
+#include "desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM
+
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER MemoryOrderAcqRel
+#define __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM ".acq_rel"
+#include "desul/atomics/cuda/cuda_cc7_asm_atomic_fetch_op.inc"
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER
+#undef __DESUL_IMPL_CUDA_ASM_MEMORY_ORDER_ASM
+
diff --git a/packages/kokkos/core/src/desul/atomics/openmp/OpenMP_40.hpp b/packages/kokkos/core/src/desul/atomics/openmp/OpenMP_40.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f4f1bbd96ee6b73d5ec15372256bb4177c033234
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/openmp/OpenMP_40.hpp
@@ -0,0 +1,97 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#ifndef DESUL_ATOMICS_OPENMP40_HPP_
+#define DESUL_ATOMICS_OPENMP40_HPP_
+#include<type_traits>
+
+namespace desul {
+namespace Impl {
+  template<class MEMORY_ORDER_TMP, class MEMORY_SCOPE_TMP>
+  void openmp_maybe_call_pre_capture_flush(MEMORY_ORDER_TMP, MEMORY_SCOPE_TMP) {}
+  template<class MEMORY_SCOPE_TMP>
+  void openmp_maybe_call_pre_capture_flush(MemoryOrderAcquire, MEMORY_SCOPE_TMP) {
+    atomic_thread_fence(MemoryOrderAcquire(), MEMORY_SCOPE_TMP());
+  }
+  template<class MEMORY_SCOPE_TMP>
+  void openmp_maybe_call_pre_capture_flush(MemoryOrderAcqRel, MEMORY_SCOPE_TMP) {
+    atomic_thread_fence(MemoryOrderAcqRel(), MEMORY_SCOPE_TMP());
+  }
+  template<class MEMORY_SCOPE_TMP>
+  void openmp_maybe_call_pre_capture_flush(MemoryOrderSeqCst, MEMORY_SCOPE_TMP) {
+    atomic_thread_fence(MemoryOrderSeqCst(), MEMORY_SCOPE_TMP());
+  }
+
+  template<class MEMORY_ORDER_TMP, class MEMORY_SCOPE_TMP>
+  void openmp_maybe_call_post_capture_flush(MEMORY_ORDER_TMP, MEMORY_SCOPE_TMP) {}
+  template<class MEMORY_SCOPE_TMP>
+  void openmp_maybe_call_post_capture_flush(MemoryOrderRelease, MEMORY_SCOPE_TMP) {
+    atomic_thread_fence(MemoryOrderRelease(), MEMORY_SCOPE_TMP());
+  }
+  template<class MEMORY_SCOPE_TMP>
+  void openmp_maybe_call_post_capture_flush(MemoryOrderAcqRel, MEMORY_SCOPE_TMP) {
+    atomic_thread_fence(MemoryOrderAcqRel(), MEMORY_SCOPE_TMP());
+  }
+  template<class MEMORY_SCOPE_TMP>
+  void openmp_maybe_call_post_capture_flush(MemoryOrderSeqCst, MEMORY_SCOPE_TMP) {
+    atomic_thread_fence(MemoryOrderSeqCst(), MEMORY_SCOPE_TMP());
+  }
+
+  template<class T>
+  struct is_openmp_atomic_type_t {
+    static constexpr bool value = std::is_arithmetic<T>::value;
+  };
+  template<class T>
+  constexpr bool is_openmp_atomic_type_v = is_openmp_atomic_type_t<T>::value;
+}
+}
+
+namespace desul {
+// Can't use a macro approach to get all definitions since the ops include #pragma omp
+// So gonna use multiple inclusion of the same code snippet here.
+
+// Can't do Node level atomics this way with OpenMP Target, but we could 
+// have a define which says whether or not Device level IS node level (e.g. for pure CPU node)
+
+#define MEMORY_ORDER MemoryOrderRelaxed
+// #define MEMORY_SCOPE MemoryScopeNode
+// #include<desul/atomics/openmp/OpenMP_40_op.inc>
+// #undef MEMORY_SCOPE
+#define MEMORY_SCOPE MemoryScopeDevice
+#include<desul/atomics/openmp/OpenMP_40_op.inc>
+#undef MEMORY_SCOPE
+#define MEMORY_SCOPE MemoryScopeCore
+#include<desul/atomics/openmp/OpenMP_40_op.inc>
+#undef MEMORY_SCOPE
+#undef MEMORY_ORDER
+
+#define MEMORY_ORDER MemoryOrderAcqRel
+// #define MEMORY_SCOPE MemoryScopeNode
+// #include<desul/atomics/openmp/OpenMP_40_op.inc>
+// #undef MEMORY_SCOPE
+#define MEMORY_SCOPE MemoryScopeDevice
+#include<desul/atomics/openmp/OpenMP_40_op.inc>
+#undef MEMORY_SCOPE
+#define MEMORY_SCOPE MemoryScopeCore
+#include<desul/atomics/openmp/OpenMP_40_op.inc>
+#undef MEMORY_SCOPE
+#undef MEMORY_ORDER
+
+#define MEMORY_ORDER MemoryOrderSeqCst
+// #define MEMORY_SCOPE MemoryScopeNode
+// #include<desul/atomics/openmp/OpenMP_40_op.inc>
+// #undef MEMORY_SCOPE
+#define MEMORY_SCOPE MemoryScopeDevice
+#include<desul/atomics/openmp/OpenMP_40_op.inc>
+#undef MEMORY_SCOPE
+#define MEMORY_SCOPE MemoryScopeCore
+#include<desul/atomics/openmp/OpenMP_40_op.inc>
+#undef MEMORY_SCOPE
+#undef MEMORY_ORDER
+}  // namespace desul
+#endif
diff --git a/packages/kokkos/core/src/desul/atomics/openmp/OpenMP_40_op.inc b/packages/kokkos/core/src/desul/atomics/openmp/OpenMP_40_op.inc
new file mode 100644
index 0000000000000000000000000000000000000000..a65f2a457dff8b2ec6e186411359e73b729fb5e1
--- /dev/null
+++ b/packages/kokkos/core/src/desul/atomics/openmp/OpenMP_40_op.inc
@@ -0,0 +1,101 @@
+
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_fetch_add(  
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());
+    #pragma omp atomic capture                                                    
+    { tmp = *dest;  *dest += value; }                                             
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }                                                                               
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_fetch_sub(  
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());    
+    #pragma omp atomic capture                                                    
+    { tmp = *dest;  *dest -= value; }                                             
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }                                                                               
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_fetch_and(  
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    #pragma omp atomic capture                                                    
+    { tmp = *dest;  *dest &= value; }                                             
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }                                                                               
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_fetch_or(   
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());    
+    #pragma omp atomic capture                                                    
+    { tmp = *dest;  *dest |= value; }                                             
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }                                                                               
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_fetch_xor(  
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());    
+    #pragma omp atomic capture                                                    
+    { tmp = *dest;  *dest ^= value; }                                             
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }                                                                               
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_add_fetch(  
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());    
+    #pragma omp atomic capture                                                    
+    { *dest += value; tmp = *dest; }                                              
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }                                                                               
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_sub_fetch(  
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());    
+    #pragma omp atomic capture                                                    
+    { *dest -= value; tmp = *dest; }                                              
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }                                                                               
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_and_fetch(  
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());    
+    #pragma omp atomic capture                                                    
+    { *dest &= value; tmp = *dest; }                                              
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }                                                                               
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_or_fetch(   
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());    
+    #pragma omp atomic capture                                                    
+    { *dest |= value; tmp = *dest; }                                              
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }                                                                               
+  template <typename T>                                                           
+  std::enable_if_t<Impl::is_openmp_atomic_type_v<T>,T> atomic_xor_fetch(  
+      T* const dest, T value, MEMORY_ORDER, MEMORY_SCOPE) {                       
+    T tmp;                                                                        
+    Impl::openmp_maybe_call_pre_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());    
+    #pragma omp atomic capture                                                    
+    { *dest ^= value; tmp = *dest; }                                              
+    Impl::openmp_maybe_call_post_capture_flush(MEMORY_ORDER(), MEMORY_SCOPE());   
+    return tmp;                                                                   
+  }
diff --git a/packages/kokkos/core/src/desul/src/Lock_Array_CUDA.cpp b/packages/kokkos/core/src/desul/src/Lock_Array_CUDA.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8913f8bc7b80fc11844438f384582e7a036c824f
--- /dev/null
+++ b/packages/kokkos/core/src/desul/src/Lock_Array_CUDA.cpp
@@ -0,0 +1,98 @@
+/* 
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#include <desul/atomics/Lock_Array.hpp>
+#include <cinttypes>
+#include <string>
+#include <sstream>
+
+#ifdef DESUL_HAVE_CUDA_ATOMICS
+#ifdef __CUDACC_RDC__
+namespace desul {
+namespace Impl {
+__device__ __constant__ int32_t* CUDA_SPACE_ATOMIC_LOCKS_DEVICE = nullptr;
+__device__ __constant__ int32_t* CUDA_SPACE_ATOMIC_LOCKS_NODE = nullptr;
+}
+}  // namespace desul
+#endif
+
+namespace desul {
+
+namespace {
+
+__global__ void init_lock_arrays_cuda_kernel() {
+  unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
+  if (i < CUDA_SPACE_ATOMIC_MASK + 1) {
+    Impl::CUDA_SPACE_ATOMIC_LOCKS_DEVICE[i] = 0;
+    Impl::CUDA_SPACE_ATOMIC_LOCKS_NODE[i] = 0;
+  }
+}
+
+}  // namespace
+
+namespace Impl {
+
+
+int32_t* CUDA_SPACE_ATOMIC_LOCKS_DEVICE_h = nullptr;
+int32_t* CUDA_SPACE_ATOMIC_LOCKS_NODE_h = nullptr;
+
+// Putting this into anonymous namespace so we don't have multiple defined symbols
+// When linking in more than one copy of the object file
+namespace {
+
+void check_error_and_throw_cuda(cudaError e, const std::string msg) {
+  if(e != cudaSuccess) {
+    std::ostringstream out;
+    out << "Desul::Error: " << msg << " error(" << cudaGetErrorName(e)
+                  << "): " << cudaGetErrorString(e);
+    throw std::runtime_error(out.str());
+  }
+}
+
+}
+
+// define functions
+template<typename T>
+void init_lock_arrays_cuda() {
+  if (CUDA_SPACE_ATOMIC_LOCKS_DEVICE_h != nullptr) return;
+  auto error_malloc1 = cudaMalloc(&CUDA_SPACE_ATOMIC_LOCKS_DEVICE_h,
+                                 sizeof(int32_t) * (CUDA_SPACE_ATOMIC_MASK + 1));
+  check_error_and_throw_cuda(error_malloc1, "init_lock_arrays_cuda: cudaMalloc device locks");
+
+  auto error_malloc2 = cudaMallocHost(&CUDA_SPACE_ATOMIC_LOCKS_NODE_h,
+                                 sizeof(int32_t) * (CUDA_SPACE_ATOMIC_MASK + 1));
+  check_error_and_throw_cuda(error_malloc2, "init_lock_arrays_cuda: cudaMalloc host locks");
+
+  auto error_sync1 = cudaDeviceSynchronize();
+  DESUL_IMPL_COPY_CUDA_LOCK_ARRAYS_TO_DEVICE();
+  check_error_and_throw_cuda(error_sync1, "init_lock_arrays_cuda: post mallocs");
+  init_lock_arrays_cuda_kernel<<<(CUDA_SPACE_ATOMIC_MASK + 1 + 255) / 256, 256>>>();
+  auto error_sync2 = cudaDeviceSynchronize();
+  check_error_and_throw_cuda(error_sync2, "init_lock_arrays_cuda: post init kernel");
+}
+
+template<typename T>
+void finalize_lock_arrays_cuda() {
+  if (CUDA_SPACE_ATOMIC_LOCKS_DEVICE_h == nullptr) return;
+  cudaFree(CUDA_SPACE_ATOMIC_LOCKS_DEVICE_h);
+  cudaFreeHost(CUDA_SPACE_ATOMIC_LOCKS_NODE_h);
+  CUDA_SPACE_ATOMIC_LOCKS_DEVICE_h = nullptr;
+  CUDA_SPACE_ATOMIC_LOCKS_NODE_h = nullptr;
+#ifdef __CUDACC_RDC__
+  DESUL_IMPL_COPY_CUDA_LOCK_ARRAYS_TO_DEVICE();
+#endif
+}
+
+// Instantiate functions
+template void init_lock_arrays_cuda<int>();
+template void finalize_lock_arrays_cuda<int>();
+
+}  // namespace Impl
+
+}  // namespace desul
+#endif
diff --git a/packages/kokkos/core/src/desul/src/Lock_Array_HIP.cpp b/packages/kokkos/core/src/desul/src/Lock_Array_HIP.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..40030df643fa1e85a0fe433b0135386418590193
--- /dev/null
+++ b/packages/kokkos/core/src/desul/src/Lock_Array_HIP.cpp
@@ -0,0 +1,101 @@
+/*
+Copyright (c) 2019, Lawrence Livermore National Security, LLC
+and DESUL project contributors. See the COPYRIGHT file for details.
+Source: https://github.com/desul/desul
+
+SPDX-License-Identifier: (BSD-3-Clause)
+*/
+
+#include <cinttypes>
+#include <desul/atomics/Lock_Array.hpp>
+#include <string>
+#include <sstream>
+
+#ifdef DESUL_HAVE_HIP_ATOMICS
+#ifdef DESUL_HIP_RDC
+namespace desul {
+namespace Impl {
+__device__ __constant__ int32_t* HIP_SPACE_ATOMIC_LOCKS_DEVICE = nullptr;
+__device__ __constant__ int32_t* HIP_SPACE_ATOMIC_LOCKS_NODE = nullptr;
+}  // namespace Impl
+}  // namespace desul
+#endif
+
+namespace desul {
+
+namespace {
+
+__global__ void init_lock_arrays_hip_kernel() {
+  unsigned i = blockIdx.x * blockDim.x + threadIdx.x;
+  if (i < HIP_SPACE_ATOMIC_MASK + 1) {
+    Impl::HIP_SPACE_ATOMIC_LOCKS_DEVICE[i] = 0;
+    Impl::HIP_SPACE_ATOMIC_LOCKS_NODE[i] = 0;
+  }
+}
+
+}  // namespace
+
+namespace Impl {
+
+int32_t* HIP_SPACE_ATOMIC_LOCKS_DEVICE_h = nullptr;
+int32_t* HIP_SPACE_ATOMIC_LOCKS_NODE_h = nullptr;
+
+// Putting this into anonymous namespace so we don't have multiple defined symbols
+// When linking in more than one copy of the object file
+namespace {
+
+void check_error_and_throw_hip(hipError_t e, const std::string msg) {
+  if(e != hipSuccess) {
+    std::ostringstream out;
+    out << "Desul::Error: " << msg << " error(" << hipGetErrorName(e)
+                  << "): " << hipGetErrorString(e);
+    throw std::runtime_error(out.str());
+  }
+}
+
+}
+
+template<typename T>
+void init_lock_arrays_hip() {
+  if (HIP_SPACE_ATOMIC_LOCKS_DEVICE_h != nullptr) return;
+
+  auto error_malloc1 = hipMalloc(&HIP_SPACE_ATOMIC_LOCKS_DEVICE_h,
+            sizeof(int32_t) * (HIP_SPACE_ATOMIC_MASK + 1));
+  check_error_and_throw_hip(error_malloc1, "init_lock_arrays_hip: hipMalloc device locks");
+
+  auto error_malloc2 = hipHostMalloc(&HIP_SPACE_ATOMIC_LOCKS_NODE_h,
+                sizeof(int32_t) * (HIP_SPACE_ATOMIC_MASK + 1));
+  check_error_and_throw_hip(error_malloc2, "init_lock_arrays_hip: hipMallocHost host locks");
+
+  auto error_sync1 = hipDeviceSynchronize();
+  DESUL_IMPL_COPY_HIP_LOCK_ARRAYS_TO_DEVICE();
+  check_error_and_throw_hip(error_sync1, "init_lock_arrays_hip: post malloc");
+
+  init_lock_arrays_hip_kernel<<<(HIP_SPACE_ATOMIC_MASK + 1 + 255) / 256, 256>>>();
+
+  auto error_sync2 = hipDeviceSynchronize();
+  check_error_and_throw_hip(error_sync2, "init_lock_arrays_hip: post init");
+}
+
+template<typename T>
+void finalize_lock_arrays_hip() {
+  if (HIP_SPACE_ATOMIC_LOCKS_DEVICE_h == nullptr) return;
+  auto error_free1 = hipFree(HIP_SPACE_ATOMIC_LOCKS_DEVICE_h);
+  check_error_and_throw_hip(error_free1, "finalize_lock_arrays_hip: free device locks");
+  auto error_free2 = hipHostFree(HIP_SPACE_ATOMIC_LOCKS_NODE_h);
+  check_error_and_throw_hip(error_free2, "finalize_lock_arrays_hip: free host locks");
+  HIP_SPACE_ATOMIC_LOCKS_DEVICE_h = nullptr;
+  HIP_SPACE_ATOMIC_LOCKS_NODE_h = nullptr;
+#ifdef DESUL_HIP_RDC
+  DESUL_IMPL_COPY_HIP_LOCK_ARRAYS_TO_DEVICE();
+#endif
+}
+
+template void init_lock_arrays_hip<int>();
+template void finalize_lock_arrays_hip<int>();
+
+}  // namespace Impl
+
+}  // namespace desul
+#endif
+
diff --git a/packages/kokkos/core/src/fwd/Kokkos_Fwd_SYCL.hpp b/packages/kokkos/core/src/fwd/Kokkos_Fwd_SYCL.hpp
index 7754daa8a0189a3d0708ce6505955be4b76b2d61..0ce680cd69efb1de0e9cbebadfda1f739e325630 100644
--- a/packages/kokkos/core/src/fwd/Kokkos_Fwd_SYCL.hpp
+++ b/packages/kokkos/core/src/fwd/Kokkos_Fwd_SYCL.hpp
@@ -52,6 +52,8 @@ class SYCLDeviceUSMSpace;  ///< Memory space on SYCL device, not accessible from
                            ///< the host
 class SYCLSharedUSMSpace;  ///< Memory space accessible from both the SYCL
                            ///< device and the host
+class SYCLHostUSMSpace;    ///< Memory space accessible from both the SYCL
+                           ///< device and the host (host pinned)
 class SYCL;                ///< Execution space for SYCL
 }  // namespace Experimental
 }  // namespace Kokkos
diff --git a/packages/kokkos/core/src/impl/KokkosExp_Host_IterateTile.hpp b/packages/kokkos/core/src/impl/KokkosExp_Host_IterateTile.hpp
index 7f72b3983f57c9adea157cf70d815339696cd986..5167c9ed65b42b1e567286849f37e89616e0e980 100644
--- a/packages/kokkos/core/src/impl/KokkosExp_Host_IterateTile.hpp
+++ b/packages/kokkos/core/src/impl/KokkosExp_Host_IterateTile.hpp
@@ -1518,28 +1518,14 @@ struct Tile_Loop_Type<
 };
 // end Structs for calling loops
 
-template <typename T>
-using is_void_type = std::is_same<T, void>;
-
-template <typename T>
-struct is_type_array : std::false_type {
-  using value_type = T;
-};
-
-template <typename T>
-struct is_type_array<T[]> : std::true_type {
-  using value_type = T;
-};
-
 template <typename RP, typename Functor, typename Tag = void,
           typename ValueType = void, typename Enable = void>
 struct HostIterateTile;
 
 // For ParallelFor
 template <typename RP, typename Functor, typename Tag, typename ValueType>
-struct HostIterateTile<
-    RP, Functor, Tag, ValueType,
-    typename std::enable_if<is_void_type<ValueType>::value>::type> {
+struct HostIterateTile<RP, Functor, Tag, ValueType,
+                       std::enable_if_t<std::is_void<ValueType>::value>> {
   using index_type = typename RP::index_type;
   using point_type = typename RP::point_type;
 
@@ -1947,10 +1933,9 @@ struct HostIterateTile<
 // For ParallelReduce
 // ValueType - scalar: For reductions
 template <typename RP, typename Functor, typename Tag, typename ValueType>
-struct HostIterateTile<
-    RP, Functor, Tag, ValueType,
-    typename std::enable_if<!is_void_type<ValueType>::value &&
-                            !is_type_array<ValueType>::value>::type> {
+struct HostIterateTile<RP, Functor, Tag, ValueType,
+                       std::enable_if_t<!std::is_void<ValueType>::value &&
+                                        !std::is_array<ValueType>::value>> {
   using index_type = typename RP::index_type;
   using point_type = typename RP::point_type;
 
@@ -2370,17 +2355,16 @@ struct HostIterateTile<
 // Extra specialization for array reductions
 // ValueType[]: For array reductions
 template <typename RP, typename Functor, typename Tag, typename ValueType>
-struct HostIterateTile<
-    RP, Functor, Tag, ValueType,
-    typename std::enable_if<!is_void_type<ValueType>::value &&
-                            is_type_array<ValueType>::value>::type> {
+struct HostIterateTile<RP, Functor, Tag, ValueType,
+                       std::enable_if_t<!std::is_void<ValueType>::value &&
+                                        std::is_array<ValueType>::value>> {
   using index_type = typename RP::index_type;
   using point_type = typename RP::point_type;
 
   using value_type =
-      typename is_type_array<ValueType>::value_type;  // strip away the
-                                                      // 'array-ness' [], only
-                                                      // underlying type remains
+      std::remove_extent_t<ValueType>;  // strip away the
+                                        // 'array-ness' [], only
+                                        // underlying type remains
 
   inline HostIterateTile(
       RP const& rp, Functor const& func,
diff --git a/packages/kokkos/core/src/impl/Kokkos_AnalyzePolicy.hpp b/packages/kokkos/core/src/impl/Kokkos_AnalyzePolicy.hpp
index c513817b5b8cbd74847e180099081bb475020c44..20fc6268c7dcd30c5c8deac332c61be673c7cc3d 100644
--- a/packages/kokkos/core/src/impl/Kokkos_AnalyzePolicy.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_AnalyzePolicy.hpp
@@ -63,12 +63,39 @@
 namespace Kokkos {
 namespace Impl {
 
+//==============================================================================
+// <editor-fold desc="AnalyzePolicyBaseTraits"> {{{1
+
+// Mix in the defaults (base_traits) for the traits that aren't yet handled
+
 //------------------------------------------------------------------------------
+// <editor-fold desc="MSVC EBO failure workaround"> {{{2
+
+template <class TraitSpecList>
+struct KOKKOS_IMPL_ENFORCE_EMPTY_BASE_OPTIMIZATION AnalyzeExecPolicyBaseTraits;
+template <class... TraitSpecifications>
+struct KOKKOS_IMPL_ENFORCE_EMPTY_BASE_OPTIMIZATION
+    AnalyzeExecPolicyBaseTraits<type_list<TraitSpecifications...>>
+    : TraitSpecifications::base_traits... {};
+
+// </editor-fold> end AnalyzePolicyBaseTraits }}}1
+//==============================================================================
 
-using execution_policy_trait_specifications =
-    type_list<ExecutionSpaceTrait, GraphKernelTrait, IndexTypeTrait,
-              IterationPatternTrait, LaunchBoundsTrait, OccupancyControlTrait,
-              ScheduleTrait, WorkItemPropertyTrait, WorkTagTrait>;
+//==============================================================================
+// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
+
+//------------------------------------------------------------------------------
+// Note: unspecialized, so that the default pathway is to fall back to using
+// the PolicyTraitMatcher. See AnalyzeExecPolicyUseMatcher below
+template <class Enable, class... Traits>
+struct AnalyzeExecPolicy
+    : AnalyzeExecPolicyUseMatcher<void, execution_policy_trait_specifications,
+                                  Traits...> {
+  using base_t =
+      AnalyzeExecPolicyUseMatcher<void, execution_policy_trait_specifications,
+                                  Traits...>;
+  using base_t::base_t;
+};
 
 //------------------------------------------------------------------------------
 // Ignore void for backwards compatibility purposes, though hopefully no one is
@@ -81,15 +108,6 @@ struct AnalyzeExecPolicy<void, void, Traits...>
 };
 
 //------------------------------------------------------------------------------
-// Mix in the defaults (base_traits) for the traits that aren't yet handled
-
-template <class TraitSpecList>
-struct KOKKOS_IMPL_ENFORCE_EMPTY_BASE_OPTIMIZATION AnalyzeExecPolicyBaseTraits;
-template <class... TraitSpecifications>
-struct KOKKOS_IMPL_ENFORCE_EMPTY_BASE_OPTIMIZATION
-    AnalyzeExecPolicyBaseTraits<type_list<TraitSpecifications...>>
-    : TraitSpecifications::base_traits... {};
-
 template <>
 struct AnalyzeExecPolicy<void>
     : AnalyzeExecPolicyBaseTraits<execution_policy_trait_specifications> {
@@ -108,6 +126,68 @@ struct AnalyzeExecPolicy<void>
   }
 };
 
+// </editor-fold> end AnalyzeExecPolicy specializations }}}1
+//==============================================================================
+
+//==============================================================================
+// <editor-fold desc="AnalyzeExecPolicyUseMatcher"> {{{1
+
+// We can avoid having to have policies specialize AnalyzeExecPolicy themselves
+// by piggy-backing off of the PolicyTraitMatcher that we need to have for
+// things like require() anyway. We mixin the effects of the trait using
+// the `mixin_matching_trait` nested alias template in the trait specification
+
+// General PolicyTraitMatcher version
+
+// Matching case
+template <class TraitSpec, class... TraitSpecs, class Trait, class... Traits>
+struct AnalyzeExecPolicyUseMatcher<
+    std::enable_if_t<PolicyTraitMatcher<TraitSpec, Trait>::value>,
+    type_list<TraitSpec, TraitSpecs...>, Trait, Traits...>
+    : TraitSpec::template mixin_matching_trait<
+          Trait, AnalyzeExecPolicy<void, Traits...>> {
+  using base_t = typename TraitSpec::template mixin_matching_trait<
+      Trait, AnalyzeExecPolicy<void, Traits...>>;
+  using base_t::base_t;
+};
+
+// Non-matching case
+template <class TraitSpec, class... TraitSpecs, class Trait, class... Traits>
+struct AnalyzeExecPolicyUseMatcher<
+    std::enable_if_t<!PolicyTraitMatcher<TraitSpec, Trait>::value>,
+    type_list<TraitSpec, TraitSpecs...>, Trait, Traits...>
+    : AnalyzeExecPolicyUseMatcher<void, type_list<TraitSpecs...>, Trait,
+                                  Traits...> {
+  using base_t = AnalyzeExecPolicyUseMatcher<void, type_list<TraitSpecs...>,
+                                             Trait, Traits...>;
+  using base_t::base_t;
+};
+
+// No match found case:
+template <class>
+struct show_name_of_invalid_execution_policy_trait;
+template <class Trait, class... Traits>
+struct AnalyzeExecPolicyUseMatcher<void, type_list<>, Trait, Traits...> {
+  static constexpr auto trigger_error_message =
+      show_name_of_invalid_execution_policy_trait<Trait>{};
+  static_assert(
+      /* always false: */ std::is_void<Trait>::value,
+      "Unknown execution policy trait. Search compiler output for "
+      "'show_name_of_invalid_execution_policy_trait' to see the type of the "
+      "invalid trait.");
+};
+
+// All traits matched case:
+template <>
+struct AnalyzeExecPolicyUseMatcher<void, type_list<>>
+    : AnalyzeExecPolicy<void> {
+  using base_t = AnalyzeExecPolicy<void>;
+  using base_t::base_t;
+};
+
+// </editor-fold> end AnalyzeExecPolicyUseMatcher }}}1
+//==============================================================================
+
 //------------------------------------------------------------------------------
 // Used for defaults that depend on other analysis results
 template <class AnalysisResults>
diff --git a/packages/kokkos/core/src/impl/Kokkos_Atomic_Compare_Exchange_Strong.hpp b/packages/kokkos/core/src/impl/Kokkos_Atomic_Compare_Exchange_Strong.hpp
index dd571eb6d72e23bf0d028493fbeec42f645b382b..d481a8dc0f21efa675e0b181a8c6981e1f9afce6 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Atomic_Compare_Exchange_Strong.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Atomic_Compare_Exchange_Strong.hpp
@@ -51,10 +51,6 @@
     !defined(KOKKOS_ATOMIC_COMPARE_EXCHANGE_STRONG_HPP)
 #define KOKKOS_ATOMIC_COMPARE_EXCHANGE_STRONG_HPP
 
-#if defined(KOKKOS_ENABLE_CUDA)
-#include <Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp>
-#endif
-
 #include <impl/Kokkos_Atomic_Memory_Order.hpp>
 #include <impl/Kokkos_Memory_Fence.hpp>
 
@@ -115,13 +111,9 @@ __inline__ __device__ T atomic_compare_exchange(
                             const T>::type& val) {
   T return_val;
   // This is a way to (hopefully) avoid dead lock in a warp
-  int done = 0;
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-  unsigned int mask   = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+  int done                 = 0;
+  unsigned int mask        = __activemask();
+  unsigned int active      = __ballot_sync(mask, 1);
   unsigned int done_active = 0;
   while (active != done_active) {
     if (!done) {
@@ -134,11 +126,7 @@ __inline__ __device__ T atomic_compare_exchange(
         done = 1;
       }
     }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-    done_active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, done);
-#else
-    done_active = KOKKOS_IMPL_CUDA_BALLOT(done);
-#endif
+    done_active = __ballot_sync(mask, done);
   }
   return return_val;
 }
diff --git a/packages/kokkos/core/src/impl/Kokkos_Atomic_Compare_Exchange_Weak.hpp b/packages/kokkos/core/src/impl/Kokkos_Atomic_Compare_Exchange_Weak.hpp
index bbea3c99b8fcfbf5c25132c913f92b7337001806..4bb8b4fd52af0c8beaf8c4dfadddfa7be58c5c54 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Atomic_Compare_Exchange_Weak.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Atomic_Compare_Exchange_Weak.hpp
@@ -51,10 +51,6 @@
 #ifndef KOKKOS_ATOMIC_COMPARE_EXCHANGE_WEAK_HPP
 #define KOKKOS_ATOMIC_COMPARE_EXCHANGE_WEAK_HPP
 
-#if defined(KOKKOS_ENABLE_CUDA)
-#include <Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp>
-#endif
-
 namespace Kokkos {
 
 //----------------------------------------------------------------------------
diff --git a/packages/kokkos/core/src/impl/Kokkos_Atomic_Exchange.hpp b/packages/kokkos/core/src/impl/Kokkos_Atomic_Exchange.hpp
index f2c1c756a910d26de0eb3765e0b90684e564d243..cd840983d8a3bd24cace6e411cabc940d44ddfe1 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Atomic_Exchange.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Atomic_Exchange.hpp
@@ -50,10 +50,6 @@
 #if defined(KOKKOS_ATOMIC_HPP) && !defined(KOKKOS_ATOMIC_EXCHANGE_HPP)
 #define KOKKOS_ATOMIC_EXCHANGE_HPP
 
-#if defined(KOKKOS_ENABLE_CUDA)
-#include <Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp>
-#endif
-
 namespace Kokkos {
 
 //----------------------------------------------------------------------------
@@ -122,13 +118,9 @@ atomic_exchange(volatile T* const dest,
   _mm_prefetch((const char*)dest, _MM_HINT_ET0);
 #endif
 
-  int done = 0;
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-  unsigned int mask   = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+  int done                 = 0;
+  unsigned int mask        = __activemask();
+  unsigned int active      = __ballot_sync(mask, 1);
   unsigned int done_active = 0;
   while (active != done_active) {
     if (!done) {
@@ -141,11 +133,7 @@ atomic_exchange(volatile T* const dest,
         done = 1;
       }
     }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-    done_active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, done);
-#else
-    done_active = KOKKOS_IMPL_CUDA_BALLOT(done);
-#endif
+    done_active = __ballot_sync(mask, done);
   }
   return return_val;
 }
diff --git a/packages/kokkos/core/src/impl/Kokkos_Atomic_Fetch_Add.hpp b/packages/kokkos/core/src/impl/Kokkos_Atomic_Fetch_Add.hpp
index 5c3f825ed100450bac57110829f64094b782011d..9a2b13debc70f24bf6adb34ddee13815458245b3 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Atomic_Fetch_Add.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Atomic_Fetch_Add.hpp
@@ -50,10 +50,6 @@
 #if defined(KOKKOS_ATOMIC_HPP) && !defined(KOKKOS_ATOMIC_FETCH_ADD_HPP)
 #define KOKKOS_ATOMIC_FETCH_ADD_HPP
 
-#if defined(KOKKOS_ENABLE_CUDA)
-#include <Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp>
-#endif
-
 namespace Kokkos {
 
 //----------------------------------------------------------------------------
@@ -148,13 +144,9 @@ atomic_fetch_add(volatile T* const dest,
                                          const T>::type& val) {
   T return_val;
   // This is a way to (hopefully) avoid dead lock in a warp
-  int done = 0;
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-  unsigned int mask   = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+  int done                 = 0;
+  unsigned int mask        = __activemask();
+  unsigned int active      = __ballot_sync(mask, 1);
   unsigned int done_active = 0;
   while (active != done_active) {
     if (!done) {
@@ -169,11 +161,7 @@ atomic_fetch_add(volatile T* const dest,
       }
     }
 
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-    done_active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, done);
-#else
-    done_active = KOKKOS_IMPL_CUDA_BALLOT(done);
-#endif
+    done_active = __ballot_sync(mask, done);
   }
   return return_val;
 }
diff --git a/packages/kokkos/core/src/impl/Kokkos_Atomic_Fetch_Sub.hpp b/packages/kokkos/core/src/impl/Kokkos_Atomic_Fetch_Sub.hpp
index c3446ae6a3bda89fac094ab9693688c2be9f77a5..148ed974420ff88d2831a2ac98ac70a8ea5f4bf2 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Atomic_Fetch_Sub.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Atomic_Fetch_Sub.hpp
@@ -50,10 +50,6 @@
 #if defined(KOKKOS_ATOMIC_HPP) && !defined(KOKKOS_ATOMIC_FETCH_SUB_HPP)
 #define KOKKOS_ATOMIC_FETCH_SUB_HPP
 
-#if defined(KOKKOS_ENABLE_CUDA)
-#include <Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp>
-#endif
-
 namespace Kokkos {
 
 //----------------------------------------------------------------------------
@@ -143,13 +139,9 @@ atomic_fetch_sub(volatile T* const dest,
                                          const T>::type& val) {
   T return_val;
   // This is a way to (hopefully) avoid dead lock in a warp
-  int done = 0;
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-  unsigned int mask   = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+  int done                 = 0;
+  unsigned int mask        = __activemask();
+  unsigned int active      = __ballot_sync(mask, 1);
   unsigned int done_active = 0;
   while (active != done_active) {
     if (!done) {
@@ -162,11 +154,7 @@ atomic_fetch_sub(volatile T* const dest,
         done = 1;
       }
     }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-    done_active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, done);
-#else
-    done_active = KOKKOS_IMPL_CUDA_BALLOT(done);
-#endif
+    done_active = __ballot_sync(mask, done);
   }
   return return_val;
 }
diff --git a/packages/kokkos/core/src/impl/Kokkos_Atomic_Generic.hpp b/packages/kokkos/core/src/impl/Kokkos_Atomic_Generic.hpp
index 28ac7a3bab9e748f9d315ca479f57db885ed75c4..f6bdbca729a335e4218ec3ac9108f0c3046eac05 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Atomic_Generic.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Atomic_Generic.hpp
@@ -47,10 +47,6 @@
 #define KOKKOS_ATOMIC_GENERIC_HPP
 #include <Kokkos_Macros.hpp>
 
-#if defined(KOKKOS_ENABLE_CUDA)
-#include <Cuda/Kokkos_Cuda_Version_9_8_Compatibility.hpp>
-#endif
-
 // Combination operands to be used in an Compare and Exchange based atomic
 // operation
 namespace Kokkos {
@@ -301,12 +297,8 @@ KOKKOS_INLINE_FUNCTION T atomic_fetch_oper(
   // This is a way to (hopefully) avoid dead lock in a warp
   T return_val;
   int done                 = 0;
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-  unsigned int mask        = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-  unsigned int active      = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+  unsigned int mask        = __activemask();
+  unsigned int active      = __ballot_sync(mask, 1);
   unsigned int done_active = 0;
   while (active != done_active) {
     if (!done) {
@@ -319,11 +311,7 @@ KOKKOS_INLINE_FUNCTION T atomic_fetch_oper(
         done = 1;
       }
     }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-    done_active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, done);
-#else
-    done_active = KOKKOS_IMPL_CUDA_BALLOT(done);
-#endif
+    done_active = __ballot_sync(mask, done);
   }
   return return_val;
 #elif defined(__HIP_DEVICE_COMPILE__)
@@ -377,12 +365,8 @@ atomic_oper_fetch(const Oper& op, volatile T* const dest,
   T return_val;
   // This is a way to (hopefully) avoid dead lock in a warp
   int done                 = 0;
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-  unsigned int mask        = KOKKOS_IMPL_CUDA_ACTIVEMASK;
-  unsigned int active      = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, 1);
-#else
-  unsigned int active = KOKKOS_IMPL_CUDA_BALLOT(1);
-#endif
+  unsigned int mask        = __activemask();
+  unsigned int active      = __ballot_sync(mask, 1);
   unsigned int done_active = 0;
   while (active != done_active) {
     if (!done) {
@@ -395,11 +379,7 @@ atomic_oper_fetch(const Oper& op, volatile T* const dest,
         done = 1;
       }
     }
-#ifdef KOKKOS_IMPL_CUDA_SYNCWARP_NEEDS_MASK
-    done_active = KOKKOS_IMPL_CUDA_BALLOT_MASK(mask, done);
-#else
-    done_active = KOKKOS_IMPL_CUDA_BALLOT(done);
-#endif
+    done_active = __ballot_sync(mask, done);
   }
   return return_val;
 #elif defined(__HIP_DEVICE_COMPILE__)
diff --git a/packages/kokkos/core/src/impl/Kokkos_Atomic_View.hpp b/packages/kokkos/core/src/impl/Kokkos_Atomic_View.hpp
index 975318b7dde67a1d1569c3cf657060c3ae18215d..f763f8c7916e4875e6c1b2a6c3733c89c532f6bb 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Atomic_View.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Atomic_View.hpp
@@ -339,9 +339,8 @@ class AtomicDataElement {
   }
 
   KOKKOS_INLINE_FUNCTION
-  operator volatile non_const_value_type() volatile const {
-    // return Kokkos::atomic_load(ptr);
-    return *ptr;
+  operator non_const_value_type() volatile const {
+    return Kokkos::Impl::atomic_load(ptr);
   }
 };
 
diff --git a/packages/kokkos/core/src/impl/Kokkos_Atomic_Windows.hpp b/packages/kokkos/core/src/impl/Kokkos_Atomic_Windows.hpp
index 3f2e8914ea9347f74cad8d84dde87bc8f5764f19..2f824566b8043a080e8d1f9010256b755547d42a 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Atomic_Windows.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Atomic_Windows.hpp
@@ -152,12 +152,6 @@ inline T atomic_compare_exchange(
                                  ((LONGLONG*)&compare_and_result));
   return compare_and_result;
 }
-
-template <typename T>
-inline T atomic_compare_exchange_strong(volatile T* const dest,
-                                        const T& compare, const T& val) {
-  return atomic_compare_exchange(dest, compare, val);
-}
 #endif
 
 }  // namespace Kokkos
diff --git a/packages/kokkos/core/src/impl/Kokkos_ClockTic.hpp b/packages/kokkos/core/src/impl/Kokkos_ClockTic.hpp
index 4e46b8d157f83129182d4db9b725bcddbe3ed28b..87f18604da52a62c6c8de22e8c670169ceec643a 100644
--- a/packages/kokkos/core/src/impl/Kokkos_ClockTic.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_ClockTic.hpp
@@ -55,7 +55,7 @@
 // To use OpenCL(TM) built-in intrinsics inside kernels, we have to
 // forward-declare their prototype, also see
 // https://github.com/intel/pti-gpu/blob/master/chapters/binary_instrumentation/OpenCLBuiltIn.md
-#if defined(KOKKOS_ENABLE_SYCL) && defined(KOKKOS_ARCH_INTEL_GEN) && \
+#if defined(KOKKOS_ENABLE_SYCL) && defined(KOKKOS_ARCH_INTEL_GPU) && \
     defined(__SYCL_DEVICE_ONLY__)
 extern SYCL_EXTERNAL unsigned long __attribute__((overloadable))
 intel_get_cycle_counter();
@@ -85,7 +85,7 @@ uint64_t clock_tic() noexcept {
 
   return clock64();
 
-#elif defined(KOKKOS_ENABLE_SYCL) && defined(KOKKOS_ARCH_INTEL_GEN) && \
+#elif defined(KOKKOS_ENABLE_SYCL) && defined(KOKKOS_ARCH_INTEL_GPU) && \
     defined(__SYCL_DEVICE_ONLY__)
   return intel_get_cycle_counter();
 #elif defined(KOKKOS_ENABLE_OPENMPTARGET)
diff --git a/packages/kokkos/core/src/impl/Kokkos_Combined_Reducer.hpp b/packages/kokkos/core/src/impl/Kokkos_Combined_Reducer.hpp
index 06681a95ae902c613c701cd78ff572d35da6c0a1..4ec8513191f21e07896bac21274e3af088dfe518 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Combined_Reducer.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Combined_Reducer.hpp
@@ -76,17 +76,17 @@ struct CombinedReducerValueItemImpl {
       CombinedReducerValueItemImpl const&) = default;
   KOKKOS_DEFAULTED_FUNCTION constexpr CombinedReducerValueItemImpl(
       CombinedReducerValueItemImpl&&) = default;
-  KOKKOS_DEFAULTED_FUNCTION KOKKOS_CONSTEXPR_14 CombinedReducerValueItemImpl&
-  operator=(CombinedReducerValueItemImpl const&) = default;
-  KOKKOS_DEFAULTED_FUNCTION KOKKOS_CONSTEXPR_14 CombinedReducerValueItemImpl&
-  operator=(CombinedReducerValueItemImpl&&) = default;
+  KOKKOS_DEFAULTED_FUNCTION constexpr CombinedReducerValueItemImpl& operator=(
+      CombinedReducerValueItemImpl const&) = default;
+  KOKKOS_DEFAULTED_FUNCTION constexpr CombinedReducerValueItemImpl& operator=(
+      CombinedReducerValueItemImpl&&) = default;
   KOKKOS_DEFAULTED_FUNCTION
   ~CombinedReducerValueItemImpl() = default;
   explicit KOKKOS_FUNCTION CombinedReducerValueItemImpl(value_type arg_value)
       : m_value(std::move(arg_value)) {}
 
   KOKKOS_FORCEINLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14 value_type& ref() & noexcept { return m_value; }
+  constexpr value_type& ref() & noexcept { return m_value; }
   KOKKOS_FORCEINLINE_FUNCTION
   constexpr value_type const& ref() const& noexcept { return m_value; }
   KOKKOS_FORCEINLINE_FUNCTION
@@ -112,11 +112,11 @@ struct CombinedReducerValueImpl<std::integer_sequence<size_t, Idxs...>,
   KOKKOS_DEFAULTED_FUNCTION
   constexpr CombinedReducerValueImpl(CombinedReducerValueImpl&&) = default;
   KOKKOS_DEFAULTED_FUNCTION
-  KOKKOS_CONSTEXPR_14 CombinedReducerValueImpl& operator=(
+  constexpr CombinedReducerValueImpl& operator=(
       CombinedReducerValueImpl const&) = default;
   KOKKOS_DEFAULTED_FUNCTION
-  KOKKOS_CONSTEXPR_14 CombinedReducerValueImpl& operator=(
-      CombinedReducerValueImpl&&) = default;
+  constexpr CombinedReducerValueImpl& operator=(CombinedReducerValueImpl&&) =
+      default;
   KOKKOS_DEFAULTED_FUNCTION
   ~CombinedReducerValueImpl() = default;
 
@@ -165,20 +165,19 @@ struct CombinedReducerStorageImpl {
   // model Reducer
 
   KOKKOS_INLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14 _fold_comma_emulation_return
-  _init(value_type& val) const {
+  constexpr _fold_comma_emulation_return _init(value_type& val) const {
     m_reducer.init(val);
     return _fold_comma_emulation_return{};
   }
 
-  KOKKOS_INLINE_FUNCTION KOKKOS_CONSTEXPR_14 _fold_comma_emulation_return
-  _join(value_type& dest, value_type const& src) const {
+  KOKKOS_INLINE_FUNCTION constexpr _fold_comma_emulation_return _join(
+      value_type& dest, value_type const& src) const {
     m_reducer.join(dest, src);
     return _fold_comma_emulation_return{};
   }
 
-  KOKKOS_INLINE_FUNCTION KOKKOS_CONSTEXPR_14 _fold_comma_emulation_return
-  _join(value_type volatile& dest, value_type const volatile& src) const {
+  KOKKOS_INLINE_FUNCTION constexpr _fold_comma_emulation_return _join(
+      value_type volatile& dest, value_type const volatile& src) const {
     m_reducer.join(dest, src);
     return _fold_comma_emulation_return{};
   }
@@ -242,10 +241,10 @@ struct CombinedReducerImpl<std::integer_sequence<size_t, Idxs...>, Space,
   KOKKOS_DEFAULTED_FUNCTION constexpr CombinedReducerImpl(
       CombinedReducerImpl const&) = default;
   KOKKOS_DEFAULTED_FUNCTION constexpr CombinedReducerImpl(
-      CombinedReducerImpl&&) = default;
-  KOKKOS_DEFAULTED_FUNCTION KOKKOS_CONSTEXPR_14 CombinedReducerImpl& operator=(
+      CombinedReducerImpl&&)                                       = default;
+  KOKKOS_DEFAULTED_FUNCTION constexpr CombinedReducerImpl& operator=(
       CombinedReducerImpl const&) = default;
-  KOKKOS_DEFAULTED_FUNCTION KOKKOS_CONSTEXPR_14 CombinedReducerImpl& operator=(
+  KOKKOS_DEFAULTED_FUNCTION constexpr CombinedReducerImpl& operator=(
       CombinedReducerImpl&&) = default;
 
   KOKKOS_DEFAULTED_FUNCTION ~CombinedReducerImpl() = default;
@@ -257,9 +256,8 @@ struct CombinedReducerImpl<std::integer_sequence<size_t, Idxs...>, Space,
                                                        reducers)...,
         m_value_view(&value) {}
 
-  KOKKOS_FUNCTION KOKKOS_CONSTEXPR_14 void join(value_type& dest,
-                                                value_type const& src) const
-      noexcept {
+  KOKKOS_FUNCTION constexpr void join(value_type& dest,
+                                      value_type const& src) const noexcept {
     emulate_fold_comma_operator(
         this->CombinedReducerStorageImpl<Idxs, Reducers>::_join(
             dest.template get<Idxs, typename Reducers::value_type>(),
@@ -274,8 +272,7 @@ struct CombinedReducerImpl<std::integer_sequence<size_t, Idxs...>, Space,
             src.template get<Idxs, typename Reducers::value_type>())...);
   }
 
-  KOKKOS_FUNCTION KOKKOS_CONSTEXPR_14 void init(value_type& dest) const
-      noexcept {
+  KOKKOS_FUNCTION constexpr void init(value_type& dest) const noexcept {
     emulate_fold_comma_operator(
         this->CombinedReducerStorageImpl<Idxs, Reducers>::_init(
             dest.template get<Idxs, typename Reducers::value_type>())...);
@@ -298,7 +295,7 @@ struct CombinedReducerImpl<std::integer_sequence<size_t, Idxs...>, Space,
   }
 
   KOKKOS_FUNCTION
-  KOKKOS_CONSTEXPR_14 static void write_value_back_to_original_references(
+  constexpr static void write_value_back_to_original_references(
       value_type const& value,
       Reducers const&... reducers_that_reference_original_values) noexcept {
     emulate_fold_comma_operator(
@@ -360,10 +357,10 @@ struct CombinedReductionFunctorWrapperImpl<
   constexpr CombinedReductionFunctorWrapperImpl(
       CombinedReductionFunctorWrapperImpl&&) = default;
   KOKKOS_DEFAULTED_FUNCTION
-  KOKKOS_CONSTEXPR_14 CombinedReductionFunctorWrapperImpl& operator=(
+  constexpr CombinedReductionFunctorWrapperImpl& operator=(
       CombinedReductionFunctorWrapperImpl const&) = default;
   KOKKOS_DEFAULTED_FUNCTION
-  KOKKOS_CONSTEXPR_14 CombinedReductionFunctorWrapperImpl& operator=(
+  constexpr CombinedReductionFunctorWrapperImpl& operator=(
       CombinedReductionFunctorWrapperImpl&&) = default;
   KOKKOS_DEFAULTED_FUNCTION
   ~CombinedReductionFunctorWrapperImpl() = default;
@@ -551,7 +548,7 @@ auto parallel_reduce(std::string const& label, PolicyType const& policy,
                      ReturnType2&& returnType2,
                      ReturnTypes&&... returnTypes) noexcept ->
     typename std::enable_if<
-        Kokkos::Impl::is_execution_policy<PolicyType>::value>::type {
+        Kokkos::is_execution_policy<PolicyType>::value>::type {
   //----------------------------------------
   // Since we don't support asynchronous combined reducers yet for various
   // reasons, we actually just want to work with the pointers and references
@@ -581,8 +578,11 @@ auto parallel_reduce(std::string const& label, PolicyType const& policy,
 
   reduce_adaptor_t::execute(label, policy, combined_functor, combined_reducer);
   Impl::ParallelReduceFence<typename PolicyType::execution_space,
-                            combined_reducer_type>::fence(policy.space(),
-                                                          combined_reducer);
+                            combined_reducer_type>::
+      fence(
+          policy.space(),
+          "Kokkos::parallel_reduce: fence due to result being value, not view",
+          combined_reducer);
   combined_reducer.write_value_back_to_original_references(
       value, Impl::_make_reducer_from_arg<space_type>(returnType1),
       Impl::_make_reducer_from_arg<space_type>(returnType2),
@@ -596,7 +596,7 @@ auto parallel_reduce(PolicyType const& policy, Functor const& functor,
                      ReturnType1&& returnType1, ReturnType2&& returnType2,
                      ReturnTypes&&... returnTypes) noexcept ->
     typename std::enable_if<
-        Kokkos::Impl::is_execution_policy<PolicyType>::value>::type {
+        Kokkos::is_execution_policy<PolicyType>::value>::type {
   //----------------------------------------
   Kokkos::parallel_reduce("", policy, functor,
                           std::forward<ReturnType1>(returnType1),
diff --git a/packages/kokkos/core/src/impl/Kokkos_ConcurrentBitset.hpp b/packages/kokkos/core/src/impl/Kokkos_ConcurrentBitset.hpp
index c02f4acddacb41f5fb01c50536f6a426738fac99..dafe57f8da71cd22ea09a4e93a84f3196b24ca5c 100644
--- a/packages/kokkos/core/src/impl/Kokkos_ConcurrentBitset.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_ConcurrentBitset.hpp
@@ -138,15 +138,15 @@ struct concurrent_bitset {
     // when is full at the atomic_fetch_add(+1)
     // then a release occurs before the atomic_fetch_add(-1).
 
-    const uint32_t state =
-        (uint32_t)Kokkos::atomic_fetch_add((volatile int *)buffer, 1);
+    const uint32_t state = (uint32_t)Kokkos::atomic_fetch_add(
+        reinterpret_cast<volatile int *>(buffer), 1);
 
     const uint32_t state_error = state_header != (state & state_header_mask);
 
     const uint32_t state_bit_used = state & state_used_mask;
 
     if (state_error || (bit_bound <= state_bit_used)) {
-      Kokkos::atomic_fetch_add((volatile int *)buffer, -1);
+      Kokkos::atomic_fetch_add(reinterpret_cast<volatile int *>(buffer), -1);
       return state_error ? type(-2, -2) : type(-1, -1);
     }
 
@@ -222,15 +222,15 @@ struct concurrent_bitset {
     // when is full at the atomic_fetch_add(+1)
     // then a release occurs before the atomic_fetch_add(-1).
 
-    const uint32_t state =
-        (uint32_t)Kokkos::atomic_fetch_add((volatile int *)buffer, 1);
+    const uint32_t state = (uint32_t)Kokkos::atomic_fetch_add(
+        reinterpret_cast<volatile int *>(buffer), 1);
 
     const uint32_t state_error = state_header != (state & state_header_mask);
 
     const uint32_t state_bit_used = state & state_used_mask;
 
     if (state_error || (bit_bound <= state_bit_used)) {
-      Kokkos::atomic_fetch_add((volatile int *)buffer, -1);
+      Kokkos::atomic_fetch_add(reinterpret_cast<volatile int *>(buffer), -1);
       return state_error ? type(-2, -2) : type(-1, -1);
     }
 
@@ -300,7 +300,8 @@ struct concurrent_bitset {
     // Do not update count until bit clear is visible
     Kokkos::memory_fence();
 
-    const int count = Kokkos::atomic_fetch_add((volatile int *)buffer, -1);
+    const int count =
+        Kokkos::atomic_fetch_add(reinterpret_cast<volatile int *>(buffer), -1);
 
     // Flush the store-release
     Kokkos::memory_fence();
@@ -336,7 +337,8 @@ struct concurrent_bitset {
     // Do not update count until bit clear is visible
     Kokkos::memory_fence();
 
-    const int count = Kokkos::atomic_fetch_add((volatile int *)buffer, -1);
+    const int count =
+        Kokkos::atomic_fetch_add(reinterpret_cast<volatile int *>(buffer), -1);
 
     return (count & state_used_mask) - 1;
   }
diff --git a/packages/kokkos/core/src/impl/Kokkos_Core.cpp b/packages/kokkos/core/src/impl/Kokkos_Core.cpp
index b4769fbeaa53be8353df315ede634708da1b297d..a1f9d336329fff0426863f28f493854e7f8091f3 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Core.cpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Core.cpp
@@ -130,6 +130,11 @@ void ExecSpaceManager::static_fence() {
     to_fence.second->fence();
   }
 }
+void ExecSpaceManager::static_fence(const std::string& name) {
+  for (auto& to_fence : exec_space_factory_list) {
+    to_fence.second->fence(name);
+  }
+}
 void ExecSpaceManager::print_configuration(std::ostream& msg,
                                            const bool detail) {
   for (auto& to_print : exec_space_factory_list) {
@@ -506,11 +511,6 @@ void pre_initialize_internal(const InitArguments& args) {
   declare_configuration_metadata("options", "KOKKOS_ENABLE_LIBRT", "yes");
 #else
   declare_configuration_metadata("options", "KOKKOS_ENABLE_LIBRT", "no");
-#endif
-#ifdef KOKKOS_ENABLE_MPI
-  declare_configuration_metadata("options", "KOKKOS_ENABLE_MPI", "yes");
-#else
-  declare_configuration_metadata("options", "KOKKOS_ENABLE_MPI", "no");
 #endif
   declare_configuration_metadata("architecture", "Default Device",
                                  typeid(Kokkos::DefaultExecutionSpace).name());
@@ -564,7 +564,9 @@ void finalize_internal(const bool all_spaces = false) {
   g_tune_internals = false;
 }
 
-void fence_internal() { Impl::ExecSpaceManager::get_instance().static_fence(); }
+void fence_internal(const std::string& name) {
+  Impl::ExecSpaceManager::get_instance().static_fence(name);
+}
 
 bool check_arg(char const* arg, char const* expected) {
   std::size_t arg_len = std::strlen(arg);
@@ -1092,7 +1094,8 @@ void finalize_all() {
   Impl::finalize_internal(all_spaces);
 }
 
-void fence() { Impl::fence_internal(); }
+void fence() { Impl::fence_internal("Kokkos::fence: Unnamed Global Fence"); }
+void fence(const std::string& name) { Impl::fence_internal(name); }
 
 void print_helper(std::ostringstream& out,
                   const std::map<std::string, std::string>& print_me) {
diff --git a/packages/kokkos/core/src/impl/Kokkos_EBO.hpp b/packages/kokkos/core/src/impl/Kokkos_EBO.hpp
index a124511c07e2fcb50c8392e56f7c5393262a3af7..dc8e5e4d830623b4fa794966da4b724467e181dc 100644
--- a/packages/kokkos/core/src/impl/Kokkos_EBO.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_EBO.hpp
@@ -79,20 +79,6 @@ struct EBOBaseImpl;
 
 template <class T, template <class...> class CtorNotOnDevice>
 struct EBOBaseImpl<T, true, CtorNotOnDevice> {
-  /*
-   * Workaround for constexpr in C++11: we need to still call T(args...), but we
-   * can't do so in the body of a constexpr function (in C++11), and there's no
-   * data member to construct into. But we can construct into an argument
-   * of a delegating constructor...
-   */
-  // TODO @minor DSH the destructor gets called too early with this workaround
-  struct _constexpr_14_workaround_tag {};
-  struct _constexpr_14_workaround_no_device_tag {};
-  KOKKOS_FORCEINLINE_FUNCTION
-  constexpr EBOBaseImpl(_constexpr_14_workaround_tag, T&&) noexcept {}
-  inline constexpr EBOBaseImpl(_constexpr_14_workaround_no_device_tag,
-                               T&&) noexcept {}
-
   template <
       class... Args, class _ignored = void,
       typename std::enable_if<std::is_void<_ignored>::value &&
@@ -100,10 +86,7 @@ struct EBOBaseImpl<T, true, CtorNotOnDevice> {
                                   !CtorNotOnDevice<Args...>::value,
                               int>::type = 0>
   KOKKOS_FORCEINLINE_FUNCTION constexpr explicit EBOBaseImpl(
-      Args&&... args) noexcept(noexcept(T(std::forward<Args>(args)...)))
-      // still call the constructor
-      : EBOBaseImpl(_constexpr_14_workaround_tag{},
-                    T(std::forward<Args>(args)...)) {}
+      Args&&...) noexcept {}
 
   template <
       class... Args, class _ignored = void,
@@ -111,11 +94,7 @@ struct EBOBaseImpl<T, true, CtorNotOnDevice> {
                                   std::is_constructible<T, Args...>::value &&
                                   CtorNotOnDevice<Args...>::value,
                               long>::type = 0>
-  inline constexpr explicit EBOBaseImpl(Args&&... args) noexcept(
-      noexcept(T(std::forward<Args>(args)...)))
-      // still call the constructor
-      : EBOBaseImpl(_constexpr_14_workaround_no_device_tag{},
-                    T(std::forward<Args>(args)...)) {}
+  inline constexpr explicit EBOBaseImpl(Args&&...) noexcept {}
 
   KOKKOS_DEFAULTED_FUNCTION
   constexpr EBOBaseImpl(EBOBaseImpl const&) = default;
@@ -124,19 +103,16 @@ struct EBOBaseImpl<T, true, CtorNotOnDevice> {
   constexpr EBOBaseImpl(EBOBaseImpl&&) = default;
 
   KOKKOS_DEFAULTED_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  EBOBaseImpl& operator=(EBOBaseImpl const&) = default;
+  constexpr EBOBaseImpl& operator=(EBOBaseImpl const&) = default;
 
   KOKKOS_DEFAULTED_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  EBOBaseImpl& operator=(EBOBaseImpl&&) = default;
+  constexpr EBOBaseImpl& operator=(EBOBaseImpl&&) = default;
 
   KOKKOS_DEFAULTED_FUNCTION
   ~EBOBaseImpl() = default;
 
   KOKKOS_INLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  T& _ebo_data_member() & { return *reinterpret_cast<T*>(this); }
+  constexpr T& _ebo_data_member() & { return *reinterpret_cast<T*>(this); }
 
   KOKKOS_INLINE_FUNCTION
   constexpr T const& _ebo_data_member() const& {
@@ -154,8 +130,9 @@ struct EBOBaseImpl<T, true, CtorNotOnDevice> {
   }
 
   KOKKOS_INLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  T&& _ebo_data_member() && { return std::move(*reinterpret_cast<T*>(this)); }
+  constexpr T&& _ebo_data_member() && {
+    return std::move(*reinterpret_cast<T*>(this));
+  }
 };
 
 template <class T, template <class...> class CTorsNotOnDevice>
@@ -191,12 +168,10 @@ struct EBOBaseImpl<T, false, CTorsNotOnDevice> {
   constexpr EBOBaseImpl(EBOBaseImpl&&) noexcept = default;
 
   KOKKOS_DEFAULTED_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  EBOBaseImpl& operator=(EBOBaseImpl const&) = default;
+  constexpr EBOBaseImpl& operator=(EBOBaseImpl const&) = default;
 
   KOKKOS_DEFAULTED_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  EBOBaseImpl& operator=(EBOBaseImpl&&) = default;
+  constexpr EBOBaseImpl& operator=(EBOBaseImpl&&) = default;
 
   KOKKOS_DEFAULTED_FUNCTION
   ~EBOBaseImpl() = default;
@@ -232,8 +207,7 @@ struct StandardLayoutNoUniqueAddressMemberEmulation
   using ebo_base_t::ebo_base_t;
 
   KOKKOS_FORCEINLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  T& no_unique_address_data_member() & {
+  constexpr T& no_unique_address_data_member() & {
     return this->ebo_base_t::_ebo_data_member();
   }
 
@@ -253,8 +227,7 @@ struct StandardLayoutNoUniqueAddressMemberEmulation
   }
 
   KOKKOS_FORCEINLINE_FUNCTION
-  KOKKOS_CONSTEXPR_14
-  T&& no_unique_address_data_member() && {
+  constexpr T&& no_unique_address_data_member() && {
     return this->ebo_base_t::_ebo_data_member();
   }
 };
diff --git a/packages/kokkos/core/src/impl/Kokkos_Error.cpp b/packages/kokkos/core/src/impl/Kokkos_Error.cpp
index dfb9f3a51cdbd9aa7e189e21f5956806d53823b5..9c8024cbd03ee9230b1ed27468c7cb82aadc5d97 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Error.cpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Error.cpp
@@ -138,6 +138,9 @@ void Experimental::RawMemoryAllocationFailure::print_error_message(
     case AllocationMechanism::SYCLMallocShared:
       o << "sycl::malloc_shared().";
       break;
+    case AllocationMechanism::SYCLMallocHost:
+      o << "sycl::malloc_host().";
+      break;
   }
   append_additional_error_information(o);
   o << ")" << std::endl;
diff --git a/packages/kokkos/core/src/impl/Kokkos_Error.hpp b/packages/kokkos/core/src/impl/Kokkos_Error.hpp
index 5db459734631ddff5d0a29963a9ec04b9ec549ea..dc9bfe2b5a9e0eb66dc2a6ae43fd296726e7a458 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Error.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Error.hpp
@@ -97,7 +97,8 @@ class RawMemoryAllocationFailure : public std::bad_alloc {
     HIPMalloc,
     HIPHostMalloc,
     SYCLMallocDevice,
-    SYCLMallocShared
+    SYCLMallocShared,
+    SYCLMallocHost
   };
 
  private:
@@ -218,31 +219,41 @@ KOKKOS_IMPL_ABORT_NORETURN KOKKOS_INLINE_FUNCTION void abort(
 
 #if !defined(NDEBUG) || defined(KOKKOS_ENFORCE_CONTRACTS) || \
     defined(KOKKOS_ENABLE_DEBUG)
-#define KOKKOS_EXPECTS(...)                                               \
-  {                                                                       \
-    if (!bool(__VA_ARGS__)) {                                             \
-      ::Kokkos::abort(                                                    \
-          "Kokkos contract violation:\n  "                                \
-          "  Expected precondition `" #__VA_ARGS__ "` evaluated false."); \
-    }                                                                     \
+#define KOKKOS_EXPECTS(...)                                                    \
+  {                                                                            \
+    if (!bool(__VA_ARGS__)) {                                                  \
+      ::Kokkos::abort(                                                         \
+          "Kokkos contract violation:\n  "                                     \
+          "  Expected precondition `" #__VA_ARGS__                             \
+          "` evaluated false.\n"                                               \
+          "Error at " KOKKOS_IMPL_TOSTRING(__FILE__) ":" KOKKOS_IMPL_TOSTRING( \
+              __LINE__) " \n");                                                \
+    }                                                                          \
   }
-#define KOKKOS_ENSURES(...)                                               \
-  {                                                                       \
-    if (!bool(__VA_ARGS__)) {                                             \
-      ::Kokkos::abort(                                                    \
-          "Kokkos contract violation:\n  "                                \
-          "  Ensured postcondition `" #__VA_ARGS__ "` evaluated false."); \
-    }                                                                     \
+#define KOKKOS_ENSURES(...)                                                    \
+  {                                                                            \
+    if (!bool(__VA_ARGS__)) {                                                  \
+      ::Kokkos::abort(                                                         \
+          "Kokkos contract violation:\n  "                                     \
+          "  Ensured postcondition `" #__VA_ARGS__                             \
+          "` evaluated false.\n"                                               \
+          "Error at " KOKKOS_IMPL_TOSTRING(__FILE__) ":" KOKKOS_IMPL_TOSTRING( \
+              __LINE__) " \n");                                                \
+    }                                                                          \
   }
-// some projects already define this for themselves, so don't mess them up
+// some projects already define this for themselves, so don't mess
+// them up
 #ifndef KOKKOS_ASSERT
-#define KOKKOS_ASSERT(...)                                             \
-  {                                                                    \
-    if (!bool(__VA_ARGS__)) {                                          \
-      ::Kokkos::abort(                                                 \
-          "Kokkos contract violation:\n  "                             \
-          "  Asserted condition `" #__VA_ARGS__ "` evaluated false."); \
-    }                                                                  \
+#define KOKKOS_ASSERT(...)                                                     \
+  {                                                                            \
+    if (!bool(__VA_ARGS__)) {                                                  \
+      ::Kokkos::abort(                                                         \
+          "Kokkos contract violation:\n  "                                     \
+          "  Asserted condition `" #__VA_ARGS__                                \
+          "` evaluated false.\n"                                               \
+          "Error at " KOKKOS_IMPL_TOSTRING(__FILE__) ":" KOKKOS_IMPL_TOSTRING( \
+              __LINE__) " \n");                                                \
+    }                                                                          \
   }
 #endif  // ifndef KOKKOS_ASSERT
 #else   // not debug mode
diff --git a/packages/kokkos/core/src/impl/Kokkos_ExecSpaceInitializer.hpp b/packages/kokkos/core/src/impl/Kokkos_ExecSpaceInitializer.hpp
index a922e7e3f9b19d0413487674f847b539e9d4f10a..1a0b10e40fe5e280746c3c0443202a4413585a0c 100644
--- a/packages/kokkos/core/src/impl/Kokkos_ExecSpaceInitializer.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_ExecSpaceInitializer.hpp
@@ -55,6 +55,7 @@ class ExecSpaceInitializerBase {
   virtual void initialize(const InitArguments &args)                     = 0;
   virtual void finalize(const bool all_spaces)                           = 0;
   virtual void fence()                                                   = 0;
+  virtual void fence(const std::string &)                                = 0;
   virtual void print_configuration(std::ostream &msg, const bool detail) = 0;
   ExecSpaceInitializerBase()          = default;
   virtual ~ExecSpaceInitializerBase() = default;
diff --git a/packages/kokkos/core/src/impl/Kokkos_FunctorAdapter.hpp b/packages/kokkos/core/src/impl/Kokkos_FunctorAdapter.hpp
index 22e88ebc4fc57d4e7132bca0be2aa55f5bfc5f69..5de92fc45741234aafaa97fb0c31dc11aa9d9c10 100644
--- a/packages/kokkos/core/src/impl/Kokkos_FunctorAdapter.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_FunctorAdapter.hpp
@@ -48,7 +48,6 @@
 #include <cstddef>
 #include <Kokkos_Core_fwd.hpp>
 #include <impl/Kokkos_Traits.hpp>
-#include <impl/Kokkos_Tags.hpp>
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
@@ -1335,7 +1334,10 @@ struct FunctorValueTraits<FunctorType, ArgTag,
   using functor_type = FunctorType;
 
   static_assert(
-      IS_VOID || IS_REJECT || 0 == (sizeof(ValueType) % sizeof(int)),
+      IS_VOID || IS_REJECT ||
+          ((sizeof(ValueType) > sizeof(int))
+               ? 0 == sizeof(ValueType) % sizeof(int)
+               : true),
       "Reduction functor's value_type deduced from functor::operator() "
       "requires: 0 == sizeof(value_type) % sizeof(int)");
 
@@ -1902,17 +1904,6 @@ struct FunctorFinalFunction {
   KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ArgTag const&,
                                                         value_type&));
 
-  // KOKKOS_INLINE_FUNCTION static void enable_if( void (FunctorType::*)( ArgTag
-  // , value_type volatile & ) const ); KOKKOS_INLINE_FUNCTION static void
-  // enable_if( void (FunctorType::*)( ArgTag const & , value_type volatile & )
-  // const ); KOKKOS_INLINE_FUNCTION static void enable_if( void
-  // (FunctorType::*)( ArgTag         , value_type volatile & ) );
-  // KOKKOS_INLINE_FUNCTION static void enable_if( void (FunctorType::*)( ArgTag
-  // const & , value_type volatile & ) ); KOKKOS_INLINE_FUNCTION static void
-  // enable_if( void (             *)( ArgTag         , value_type volatile & )
-  // ); KOKKOS_INLINE_FUNCTION static void enable_if( void (             *)(
-  // ArgTag const & , value_type volatile & ) );
-
   KOKKOS_INLINE_FUNCTION static void enable_if(
       void (FunctorType::*)(ArgTag, value_type const&) const);
   KOKKOS_INLINE_FUNCTION static void enable_if(
@@ -1925,17 +1916,6 @@ struct FunctorFinalFunction {
                                                         value_type const&));
   KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ArgTag const&,
                                                         value_type const&));
-
-  // KOKKOS_INLINE_FUNCTION static void enable_if( void (FunctorType::*)( ArgTag
-  // , value_type const volatile & ) const ); KOKKOS_INLINE_FUNCTION static void
-  // enable_if( void (FunctorType::*)( ArgTag const & , value_type const
-  // volatile & ) const ); KOKKOS_INLINE_FUNCTION static void enable_if( void
-  // (FunctorType::*)( ArgTag         , value_type const volatile & ) );
-  // KOKKOS_INLINE_FUNCTION static void enable_if( void (FunctorType::*)( ArgTag
-  // const & , value_type const volatile & ) ); KOKKOS_INLINE_FUNCTION static
-  // void enable_if( void (             *)( ArgTag         , value_type const
-  // volatile & ) ); KOKKOS_INLINE_FUNCTION static void enable_if( void ( *)(
-  // ArgTag const & , value_type const volatile & ) );
 };
 
 // Compatible functions for 'final' function and value_type is an array
@@ -1956,17 +1936,6 @@ struct FunctorFinalFunction<FunctorType, ArgTag, true> {
   KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ArgTag const&,
                                                         value_type*));
 
-  // KOKKOS_INLINE_FUNCTION static void enable_if( void (FunctorType::*)( ArgTag
-  // , value_type volatile * ) const ); KOKKOS_INLINE_FUNCTION static void
-  // enable_if( void (FunctorType::*)( ArgTag const & , value_type volatile * )
-  // const ); KOKKOS_INLINE_FUNCTION static void enable_if( void
-  // (FunctorType::*)( ArgTag         , value_type volatile * ) );
-  // KOKKOS_INLINE_FUNCTION static void enable_if( void (FunctorType::*)( ArgTag
-  // const & , value_type volatile * ) ); KOKKOS_INLINE_FUNCTION static void
-  // enable_if( void (             *)( ArgTag         , value_type volatile * )
-  // ); KOKKOS_INLINE_FUNCTION static void enable_if( void (             *)(
-  // ArgTag const & , value_type volatile * ) );
-
   KOKKOS_INLINE_FUNCTION static void enable_if(
       void (FunctorType::*)(ArgTag, value_type const*) const);
   KOKKOS_INLINE_FUNCTION static void enable_if(
@@ -1979,17 +1948,6 @@ struct FunctorFinalFunction<FunctorType, ArgTag, true> {
                                                         value_type const*));
   KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ArgTag const&,
                                                         value_type const*));
-
-  // KOKKOS_INLINE_FUNCTION static void enable_if( void (FunctorType::*)( ArgTag
-  // , value_type const volatile * ) const ); KOKKOS_INLINE_FUNCTION static void
-  // enable_if( void (FunctorType::*)( ArgTag const & , value_type const
-  // volatile * ) const ); KOKKOS_INLINE_FUNCTION static void enable_if( void
-  // (FunctorType::*)( ArgTag         , value_type const volatile * ) );
-  // KOKKOS_INLINE_FUNCTION static void enable_if( void (FunctorType::*)( ArgTag
-  // const & , value_type const volatile * ) ); KOKKOS_INLINE_FUNCTION static
-  // void enable_if( void (             *)( ArgTag         , value_type const
-  // volatile * ) ); KOKKOS_INLINE_FUNCTION static void enable_if( void ( *)(
-  // ArgTag const & , value_type const volatile * ) );
 };
 
 template <class FunctorType>
@@ -2109,89 +2067,4 @@ struct FunctorFinal<
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
 
-namespace Kokkos {
-namespace Impl {
-
-template <class FunctorType, class ArgTag,
-          class ReferenceType =
-              typename FunctorValueTraits<FunctorType, ArgTag>::reference_type>
-struct FunctorApplyFunction {
-  KOKKOS_INLINE_FUNCTION static void enable_if(
-      void (FunctorType::*)(ArgTag, ReferenceType) const);
-  KOKKOS_INLINE_FUNCTION static void enable_if(
-      void (FunctorType::*)(ArgTag const&, ReferenceType) const);
-  KOKKOS_INLINE_FUNCTION static void enable_if(
-      void (FunctorType::*)(ArgTag, ReferenceType));
-  KOKKOS_INLINE_FUNCTION static void enable_if(
-      void (FunctorType::*)(ArgTag const&, ReferenceType));
-  KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ArgTag, ReferenceType));
-  KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ArgTag const&,
-                                                        ReferenceType));
-};
-
-template <class FunctorType, class ReferenceType>
-struct FunctorApplyFunction<FunctorType, void, ReferenceType> {
-  KOKKOS_INLINE_FUNCTION static void enable_if(
-      void (FunctorType::*)(ReferenceType) const);
-  KOKKOS_INLINE_FUNCTION static void enable_if(
-      void (FunctorType::*)(ReferenceType));
-  KOKKOS_INLINE_FUNCTION static void enable_if(void (*)(ReferenceType));
-};
-
-template <class FunctorType>
-struct FunctorApplyFunction<FunctorType, void, void> {
-  KOKKOS_INLINE_FUNCTION static void enable_if(void (FunctorType::*)() const);
-  KOKKOS_INLINE_FUNCTION static void enable_if(void (FunctorType::*)());
-};
-
-template <class FunctorType, class ArgTag, class ReferenceType,
-          class Enable = void>
-struct FunctorApply {
-  KOKKOS_FORCEINLINE_FUNCTION static void apply(const FunctorType&, void*) {}
-};
-
-/* 'apply' function provided for void value */
-template <class FunctorType, class ArgTag>
-struct FunctorApply<
-    FunctorType, ArgTag,
-    void
-    // First  substitution failure when FunctorType::apply does not exist.
-    // Second substitution failure when enable_if( & Functor::apply ) does not
-    // exist
-    ,
-    decltype(FunctorApplyFunction<FunctorType, ArgTag, void>::enable_if(
-        &FunctorType::apply))> {
-  KOKKOS_FORCEINLINE_FUNCTION static void apply(FunctorType& f) { f.apply(); }
-
-  KOKKOS_FORCEINLINE_FUNCTION static void apply(const FunctorType& f) {
-    f.apply();
-  }
-};
-
-/* 'apply' function provided for single value */
-template <class FunctorType, class ArgTag, class T>
-struct FunctorApply<FunctorType, ArgTag,
-                    T&
-                    // First  substitution failure when FunctorType::apply does
-                    // not exist. Second substitution failure when enable_if( &
-                    // Functor::apply ) does not exist
-                    ,
-                    decltype(
-                        FunctorApplyFunction<FunctorType, ArgTag>::enable_if(
-                            &FunctorType::apply))> {
-  KOKKOS_FORCEINLINE_FUNCTION static void apply(const FunctorType& f, void* p) {
-    f.apply(*((T*)p));
-  }
-
-  KOKKOS_FORCEINLINE_FUNCTION static void apply(FunctorType& f, void* p) {
-    f.apply(*((T*)p));
-  }
-};
-
-}  // namespace Impl
-}  // namespace Kokkos
-
-//----------------------------------------------------------------------------
-//----------------------------------------------------------------------------
-
 #endif /* KOKKOS_FUNCTORADAPTER_HPP */
diff --git a/packages/kokkos/core/src/impl/Kokkos_FunctorAnalysis.hpp b/packages/kokkos/core/src/impl/Kokkos_FunctorAnalysis.hpp
index a56d19ee722668389b4b43bc377d79bb7fd9799b..7140154e0f6f276dc928dc5f3a73cda97f6e2cec 100644
--- a/packages/kokkos/core/src/impl/Kokkos_FunctorAnalysis.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_FunctorAnalysis.hpp
@@ -48,7 +48,6 @@
 #include <cstddef>
 #include <Kokkos_Core_fwd.hpp>
 #include <impl/Kokkos_Traits.hpp>
-#include <impl/Kokkos_Tags.hpp>
 
 //----------------------------------------------------------------------------
 //----------------------------------------------------------------------------
@@ -722,14 +721,16 @@ struct FunctorAnalysis {
 
     template <bool IsArray>
     KOKKOS_INLINE_FUNCTION constexpr
-        typename std::enable_if<IsArray, FunctorAnalysis::ValueType*>::type
+        typename std::enable_if<IsArray,
+                                typename FunctorAnalysis::ValueType*>::type
         ref() const noexcept {
       return m_result;
     }
 
     template <bool IsArray>
     KOKKOS_INLINE_FUNCTION constexpr
-        typename std::enable_if<!IsArray, FunctorAnalysis::ValueType&>::type
+        typename std::enable_if<!IsArray,
+                                typename FunctorAnalysis::ValueType&>::type
         ref() const noexcept {
       return *m_result;
     }
diff --git a/packages/kokkos/core/src/impl/Kokkos_HostSharedPtr.hpp b/packages/kokkos/core/src/impl/Kokkos_HostSharedPtr.hpp
index 97286dd07f4ea2ee94f3070768f425e2ef5b7896..3b7b194db58cb693f69d8a6560896565062b9d99 100644
--- a/packages/kokkos/core/src/impl/Kokkos_HostSharedPtr.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_HostSharedPtr.hpp
@@ -47,6 +47,7 @@
 
 #include <Kokkos_Macros.hpp>
 #include <Kokkos_Atomic.hpp>
+#include <impl/Kokkos_Error.hpp>
 
 #include <functional>
 
@@ -92,6 +93,8 @@ class HostSharedPtr {
     // FIXME_OPENMPTARGET requires something like KOKKOS_IMPL_IF_ON_HOST
 #ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST
     if (m_control) Kokkos::atomic_add(&(m_control->m_counter), 1);
+#else
+    m_control = nullptr;
 #endif
   }
 
@@ -115,6 +118,8 @@ class HostSharedPtr {
       // FIXME_OPENMPTARGET
 #ifdef KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST
       if (m_control) Kokkos::atomic_add(&(m_control->m_counter), 1);
+#else
+      m_control = nullptr;
 #endif
     }
     return *this;
@@ -154,6 +159,9 @@ class HostSharedPtr {
     // object pointed to by m_counter and m_element_ptr.
     if (m_control) {
       int const count = Kokkos::atomic_fetch_sub(&(m_control->m_counter), 1);
+      // atomic_fetch_sub might have memory order relaxed so we need to force
+      // synchronization to avoid multiple threads doing the cleanup.
+      Kokkos::memory_fence();
       if (count == 1) {
         (m_control->m_deleter)(m_element_ptr);
         m_element_ptr = nullptr;
diff --git a/packages/kokkos/core/src/impl/Kokkos_HostThreadTeam.cpp b/packages/kokkos/core/src/impl/Kokkos_HostThreadTeam.cpp
index 2e5587e4a342c8c2b167f307f8c8b3a3215f304a..a7f4a652befb148bafff866d8949edb9f2520eaa 100644
--- a/packages/kokkos/core/src/impl/Kokkos_HostThreadTeam.cpp
+++ b/packages/kokkos/core/src/impl/Kokkos_HostThreadTeam.cpp
@@ -74,8 +74,8 @@ void HostThreadTeamData::organize_pool(HostThreadTeamData *members[],
     }
 
     {
-      HostThreadTeamData **const pool =
-          (HostThreadTeamData **)(root_scratch + m_pool_members);
+      HostThreadTeamData **const pool = reinterpret_cast<HostThreadTeamData **>(
+          root_scratch + m_pool_members);
 
       // team size == 1, league size == pool_size
 
@@ -136,7 +136,8 @@ int HostThreadTeamData::organize_team(const int team_size) {
     if (team_size == 1) return 1;  // Already organized in teams of one
 
     HostThreadTeamData *const *const pool =
-        (HostThreadTeamData **)(m_pool_scratch + m_pool_members);
+        reinterpret_cast<HostThreadTeamData **>(m_pool_scratch +
+                                                m_pool_members);
 
     // "league_size" in this context is the number of concurrent teams
     // that the pool can accommodate.  Excess threads are idle.
@@ -239,7 +240,8 @@ int HostThreadTeamData::get_work_stealing() noexcept {
 
     if (w.first == -1 && m_steal_rank != m_pool_rank) {
       HostThreadTeamData *const *const pool =
-          (HostThreadTeamData **)(m_pool_scratch + m_pool_members);
+          reinterpret_cast<HostThreadTeamData **>(m_pool_scratch +
+                                                  m_pool_members);
 
       // Attempt from beginning failed, try to steal from end of neighbor
 
@@ -287,23 +289,17 @@ int HostThreadTeamData::get_work_stealing() noexcept {
 
     if (1 < m_team_size) {
       // Must share the work index
-      *((int volatile *)team_reduce()) = w.first;
+      *reinterpret_cast<int volatile *>(team_reduce()) = w.first;
 
       team_rendezvous_release();
     }
   } else if (1 < m_team_size) {
-    w.first = *((int volatile *)team_reduce());
+    w.first = *reinterpret_cast<int volatile *>(team_reduce());
   }
 
   // May exit because successfully stole work and w is good.
   // May exit because no work left to steal and w = (-1,-1).
 
-#if 0
-fprintf(stdout,"HostThreadTeamData::get_work_stealing() pool(%d of %d) %d\n"
-       , m_pool_rank , m_pool_size , w.first );
-fflush(stdout);
-#endif
-
   return w.first;
 }
 
diff --git a/packages/kokkos/core/src/impl/Kokkos_HostThreadTeam.hpp b/packages/kokkos/core/src/impl/Kokkos_HostThreadTeam.hpp
index d4cae7f122ed182cf88522d5d60729a0906cce5b..0652b55bb71cfb3923374e774bbb2db2f58ee90d 100644
--- a/packages/kokkos/core/src/impl/Kokkos_HostThreadTeam.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_HostThreadTeam.hpp
@@ -91,9 +91,18 @@ class HostThreadTeamData {
   //   [ thread_local ]     = [ m_thread_local    .. m_scratch_size )
 
   enum : int { m_pool_members = 0 };
-  enum : int { m_pool_rendezvous = m_pool_members + max_pool_members };
-  enum : int { m_team_rendezvous = m_pool_rendezvous + max_pool_rendezvous };
-  enum : int { m_pool_reduce = m_team_rendezvous + max_team_rendezvous };
+  enum : int {
+    m_pool_rendezvous =
+        static_cast<int>(m_pool_members) + static_cast<int>(max_pool_members)
+  };
+  enum : int {
+    m_team_rendezvous = static_cast<int>(m_pool_rendezvous) +
+                        static_cast<int>(max_pool_rendezvous)
+  };
+  enum : int {
+    m_pool_reduce = static_cast<int>(m_team_rendezvous) +
+                    static_cast<int>(max_team_rendezvous)
+  };
 
   using pair_int_t = Kokkos::pair<int64_t, int64_t>;
 
@@ -120,13 +129,13 @@ class HostThreadTeamData {
   int mutable m_team_rendezvous_step;
 
   HostThreadTeamData* team_member(int r) const noexcept {
-    return ((HostThreadTeamData**)(m_pool_scratch +
-                                   m_pool_members))[m_team_base + r];
+    return (reinterpret_cast<HostThreadTeamData**>(
+        m_pool_scratch + m_pool_members))[m_team_base + r];
   }
 
  public:
   inline bool team_rendezvous() const noexcept {
-    int* ptr = (int*)(m_team_scratch + m_team_rendezvous);
+    int* ptr = reinterpret_cast<int*>(m_team_scratch + m_team_rendezvous);
     HostBarrier::split_arrive(ptr, m_team_size, m_team_rendezvous_step);
     if (m_team_rank != 0) {
       HostBarrier::wait(ptr, m_team_size, m_team_rendezvous_step);
@@ -138,7 +147,7 @@ class HostThreadTeamData {
   }
 
   inline bool team_rendezvous(const int source_team_rank) const noexcept {
-    int* ptr = (int*)(m_team_scratch + m_team_rendezvous);
+    int* ptr = reinterpret_cast<int*>(m_team_scratch + m_team_rendezvous);
     HostBarrier::split_arrive(ptr, m_team_size, m_team_rendezvous_step);
     if (m_team_rank != source_team_rank) {
       HostBarrier::wait(ptr, m_team_size, m_team_rendezvous_step);
@@ -150,12 +159,13 @@ class HostThreadTeamData {
   }
 
   inline void team_rendezvous_release() const noexcept {
-    HostBarrier::split_release((int*)(m_team_scratch + m_team_rendezvous),
-                               m_team_size, m_team_rendezvous_step);
+    HostBarrier::split_release(
+        reinterpret_cast<int*>(m_team_scratch + m_team_rendezvous), m_team_size,
+        m_team_rendezvous_step);
   }
 
   inline int pool_rendezvous() const noexcept {
-    int* ptr = (int*)(m_pool_scratch + m_pool_rendezvous);
+    int* ptr = reinterpret_cast<int*>(m_pool_scratch + m_pool_rendezvous);
     HostBarrier::split_arrive(ptr, m_pool_size, m_pool_rendezvous_step);
     if (m_pool_rank != 0) {
       HostBarrier::wait(ptr, m_pool_size, m_pool_rendezvous_step);
@@ -167,8 +177,9 @@ class HostThreadTeamData {
   }
 
   inline void pool_rendezvous_release() const noexcept {
-    HostBarrier::split_release((int*)(m_pool_scratch + m_pool_rendezvous),
-                               m_pool_size, m_pool_rendezvous_step);
+    HostBarrier::split_release(
+        reinterpret_cast<int*>(m_pool_scratch + m_pool_rendezvous), m_pool_size,
+        m_pool_rendezvous_step);
   }
 
   //----------------------------------------
@@ -230,7 +241,8 @@ class HostThreadTeamData {
   constexpr int pool_size() const { return m_pool_size; }
 
   HostThreadTeamData* pool_member(int r) const noexcept {
-    return ((HostThreadTeamData**)(m_pool_scratch + m_pool_members))[r];
+    return (reinterpret_cast<HostThreadTeamData**>(m_pool_scratch +
+                                                   m_pool_members))[r];
   }
 
   //----------------------------------------
@@ -330,24 +342,11 @@ class HostThreadTeamData {
     team_shared_size = align_to_int64(team_shared_size);
     // thread_local_size = align_to_int64( thread_local_size );
 
-    m_scratch      = (int64_t*)alloc_ptr;
+    m_scratch      = static_cast<int64_t*>(alloc_ptr);
     m_team_reduce  = m_pool_reduce + pool_reduce_size;
     m_team_shared  = m_team_reduce + team_reduce_size;
     m_thread_local = m_team_shared + team_shared_size;
     m_scratch_size = align_to_int64(alloc_size);
-
-#if 0
-fprintf(stdout,"HostThreadTeamData::scratch_assign { %d %d %d %d %d %d %d }\n"
-       , int(m_pool_members)
-       , int(m_pool_rendezvous)
-       , int(m_pool_reduce)
-       , int(m_team_reduce)
-       , int(m_team_shared)
-       , int(m_thread_local)
-       , int(m_scratch_size)
-       );
-fflush(stdout);
-#endif
   }
 
   //----------------------------------------
diff --git a/packages/kokkos/core/src/impl/Kokkos_LinkedListNode.hpp b/packages/kokkos/core/src/impl/Kokkos_LinkedListNode.hpp
index 79aeca5da0691264c4cb215f62e17bdb8dbe95e1..1ed502db5be61a2501b57f08969ff47ce12bdbe5 100644
--- a/packages/kokkos/core/src/impl/Kokkos_LinkedListNode.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_LinkedListNode.hpp
@@ -110,7 +110,7 @@ struct SimpleSinglyLinkedListNode {
   friend struct LinkedListNodeAccess;
 
  public:
-  // KOKKOS_CONSTEXPR_14
+  // constexpr
   KOKKOS_INLINE_FUNCTION
   bool is_enqueued() const noexcept {
     // TODO @tasking @memory_order DSH make this an atomic load with memory
@@ -118,7 +118,7 @@ struct SimpleSinglyLinkedListNode {
     return m_next != reinterpret_cast<pointer_type>(NotEnqueuedValue);
   }
 
-  // KOKKOS_CONSTEXPR_14
+  // constexpr
   KOKKOS_INLINE_FUNCTION
   bool is_enqueued() const volatile noexcept {
     // TODO @tasking @memory_order DSH make this an atomic load with memory
diff --git a/packages/kokkos/core/src/impl/Kokkos_Memory_Fence.hpp b/packages/kokkos/core/src/impl/Kokkos_Memory_Fence.hpp
index 76d553601923fd7282132fbff05ce69a4e576e97..865d1c47faacfe3d6b39d4227bb180bf483c89dd 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Memory_Fence.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Memory_Fence.hpp
@@ -48,7 +48,7 @@
 namespace Kokkos {
 
 //----------------------------------------------------------------------------
-
+#ifndef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
 KOKKOS_FORCEINLINE_FUNCTION
 void memory_fence() {
 #if defined(__CUDA_ARCH__)
@@ -75,6 +75,7 @@ void memory_fence() {
 #error "Error: memory_fence() not defined"
 #endif
 }
+#endif
 
 //////////////////////////////////////////////////////
 // store_fence()
diff --git a/packages/kokkos/core/src/impl/Kokkos_MultipleTaskQueue.hpp b/packages/kokkos/core/src/impl/Kokkos_MultipleTaskQueue.hpp
index fe78cfbacc632d353844a5cb17f89a5d5ba067ff..1c61b73f027aaefa4993aaae7beee3ca9af05110 100644
--- a/packages/kokkos/core/src/impl/Kokkos_MultipleTaskQueue.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_MultipleTaskQueue.hpp
@@ -58,8 +58,7 @@
 
 #include <impl/Kokkos_TaskQueueMemoryManager.hpp>
 #include <impl/Kokkos_TaskQueueCommon.hpp>
-#include <impl/Kokkos_Memory_Fence.hpp>
-#include <impl/Kokkos_Atomic_Increment.hpp>
+#include <Kokkos_Atomic.hpp>
 #include <impl/Kokkos_OptionalRef.hpp>
 #include <impl/Kokkos_LIFO.hpp>
 
@@ -467,7 +466,7 @@ class MultipleTaskQueue final
 
   // TODO @tasking @generalization DSH make this a property-based customization
   // point
-  static /* KOKKOS_CONSTEXPR_14 */ size_t task_queue_allocation_size(
+  static /* constexpr */ size_t task_queue_allocation_size(
       typename base_t::execution_space const& exec_space,
       typename base_t::memory_space const&,
       typename base_t::memory_pool const&) {
diff --git a/packages/kokkos/core/src/impl/Kokkos_Profiling.cpp b/packages/kokkos/core/src/impl/Kokkos_Profiling.cpp
index 94ea6e1a2b10c33a81e4f2c6b7a932577ce6144b..8505e8f51aec744c00dfe2fef3f29d1eb9cb7306 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Profiling.cpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Profiling.cpp
@@ -53,6 +53,7 @@
 #include <array>
 #include <cstring>
 #include <iostream>
+#include <memory>
 #include <stack>
 #include <unordered_map>
 #include <unordered_set>
@@ -70,7 +71,9 @@ void tool_invoked_fence(const uint32_t /* devID */) {
    * Eventually we want to support fencing only
    * a given stream/resource
    */
-  Kokkos::fence();
+  Kokkos::fence(
+      "Kokkos::Tools::Experimental::Impl::tool_invoked_fence: Tool Requested "
+      "Fence");
 }
 }  // namespace Impl
 #ifdef KOKKOS_ENABLE_TUNING
@@ -131,7 +134,8 @@ inline void invoke_kokkosp_callback(
     if (may_require_global_fencing == MayRequireGlobalFencing::Yes &&
         (Kokkos::Tools::Experimental::tool_requirements
              .requires_global_fencing)) {
-      Kokkos::fence();
+      Kokkos::fence(
+          "Kokkos::Tools::invoke_kokkosp_callback: Kokkos Profile Tool Fence");
     }
     (*callback)(std::forward<Args>(args)...);
   }
@@ -432,18 +436,43 @@ void initialize(const std::string& profileLibrary) {
   if (is_initialized) return;
   is_initialized = 1;
 
+  auto invoke_init_callbacks = []() {
+    Experimental::invoke_kokkosp_callback(
+        Kokkos::Tools::Experimental::MayRequireGlobalFencing::No,
+        Kokkos::Tools::Experimental::current_callbacks.init, 0,
+        (uint64_t)KOKKOSP_INTERFACE_VERSION, (uint32_t)0, nullptr);
+
+    Experimental::tool_requirements.requires_global_fencing = true;
+
+    Experimental::invoke_kokkosp_callback(
+        Experimental::MayRequireGlobalFencing::No,
+        Experimental::current_callbacks.request_tool_settings, 1,
+        &Experimental::tool_requirements);
+
+    Experimental::ToolProgrammingInterface actions;
+    actions.fence = &Experimental::Impl::tool_invoked_fence;
+
+    Experimental::invoke_kokkosp_callback(
+        Experimental::MayRequireGlobalFencing::No,
+        Experimental::current_callbacks.provide_tool_programming_interface, 1,
+        actions);
+  };
+
 #ifdef KOKKOS_ENABLE_LIBDL
   void* firstProfileLibrary = nullptr;
 
-  if (profileLibrary.empty()) return;
+  if (profileLibrary.empty()) {
+    invoke_init_callbacks();
+    return;
+  }
 
   char* envProfileLibrary = const_cast<char*>(profileLibrary.c_str());
 
-  char* envProfileCopy =
-      (char*)malloc(sizeof(char) * (strlen(envProfileLibrary) + 1));
-  sprintf(envProfileCopy, "%s", envProfileLibrary);
+  const auto envProfileCopy =
+      std::make_unique<char[]>(strlen(envProfileLibrary) + 1);
+  sprintf(envProfileCopy.get(), "%s", envProfileLibrary);
 
-  char* profileLibraryName = strtok(envProfileCopy, ";");
+  char* profileLibraryName = strtok(envProfileCopy.get(), ";");
 
   if ((profileLibraryName != nullptr) &&
       (strcmp(profileLibraryName, "") != 0)) {
@@ -574,25 +603,8 @@ void initialize(const std::string& profileLibrary) {
 #else
   (void)profileLibrary;
 #endif  // KOKKOS_ENABLE_LIBDL
-  Experimental::invoke_kokkosp_callback(
-      Kokkos::Tools::Experimental::MayRequireGlobalFencing::No,
-      Kokkos::Tools::Experimental::current_callbacks.init, 0,
-      (uint64_t)KOKKOSP_INTERFACE_VERSION, (uint32_t)0, nullptr);
-
-  Experimental::tool_requirements.requires_global_fencing = true;
-
-  Experimental::invoke_kokkosp_callback(
-      Experimental::MayRequireGlobalFencing::No,
-      Experimental::current_callbacks.request_tool_settings, 1,
-      &Experimental::tool_requirements);
 
-  Experimental::ToolProgrammingInterface actions;
-  actions.fence = &Experimental::Impl::tool_invoked_fence;
-
-  Experimental::invoke_kokkosp_callback(
-      Experimental::MayRequireGlobalFencing::No,
-      Experimental::current_callbacks.provide_tool_programming_interface, 1,
-      actions);
+  invoke_init_callbacks();
 
 #ifdef KOKKOS_ENABLE_TUNING
   Experimental::VariableInfo kernel_name;
@@ -656,9 +668,6 @@ void initialize(const std::string& profileLibrary) {
   Experimental::no_profiling.declare_output_type   = nullptr;
   Experimental::no_profiling.request_output_values = nullptr;
   Experimental::no_profiling.end_tuning_context    = nullptr;
-#ifdef KOKKOS_ENABLE_LIBDL
-  free(envProfileCopy);
-#endif
 }
 
 void finalize() {
diff --git a/packages/kokkos/core/src/impl/Kokkos_Profiling.hpp b/packages/kokkos/core/src/impl/Kokkos_Profiling.hpp
index 1ff6a36c3bc3c934e787af30c5bd6568046f15f1..86a4cfa4a8543e58feab3aee24f1a9ac8530bb5e 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Profiling.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Profiling.hpp
@@ -50,9 +50,12 @@
 #include <Kokkos_Macros.hpp>
 #include <Kokkos_Tuners.hpp>
 #include <impl/Kokkos_Profiling_Interface.hpp>
+#include <memory>
+#include <unordered_map>
 #include <map>
 #include <string>
 #include <type_traits>
+#include <mutex>
 namespace Kokkos {
 
 // forward declaration
@@ -135,6 +138,71 @@ Kokkos_Profiling_SpaceHandle make_space_handle(const char* space_name);
 
 namespace Experimental {
 
+namespace Impl {
+struct DirectFenceIDHandle {
+  uint32_t value;
+};
+//
+template <typename Space>
+uint32_t idForInstance(const uintptr_t instance) {
+  static std::mutex instance_mutex;
+  const std::lock_guard<std::mutex> lock(instance_mutex);
+  /** Needed to be a ptr due to initialization order problems*/
+  using map_type = std::map<uintptr_t, uint32_t>;
+
+  static std::shared_ptr<map_type> map;
+  if (map.get() == nullptr) {
+    map = std::make_shared<map_type>(map_type());
+  }
+
+  static uint32_t value = 0;
+  constexpr const uint32_t offset =
+      Kokkos::Tools::Experimental::NumReservedDeviceIDs;
+
+  auto find = map->find(instance);
+  if (find == map->end()) {
+    auto ret         = offset + value++;
+    (*map)[instance] = ret;
+    return ret;
+  }
+
+  return find->second;
+}
+
+template <typename Space, typename FencingFunctor>
+void profile_fence_event(const std::string& name, DirectFenceIDHandle devIDTag,
+                         const FencingFunctor& func) {
+  uint64_t handle = 0;
+  Kokkos::Tools::beginFence(
+      name,
+      Kokkos::Tools::Experimental::device_id_root<Space>() + devIDTag.value,
+      &handle);
+  func();
+  Kokkos::Tools::endFence(handle);
+}
+
+inline uint32_t int_for_synchronization_reason(
+    Kokkos::Tools::Experimental::SpecialSynchronizationCases reason) {
+  switch (reason) {
+    case GlobalDeviceSynchronization: return 0;
+    case DeepCopyResourceSynchronization: return 0x00ffffff;
+  }
+  return 0;
+}
+
+template <typename Space, typename FencingFunctor>
+void profile_fence_event(
+    const std::string& name,
+    Kokkos::Tools::Experimental::SpecialSynchronizationCases reason,
+    const FencingFunctor& func) {
+  uint64_t handle = 0;
+  Kokkos::Tools::beginFence(
+      name, device_id_root<Space>() + int_for_synchronization_reason(reason),
+      &handle);  // TODO: correct ID
+  func();
+  Kokkos::Tools::endFence(handle);
+}
+}  // namespace Impl
 void set_init_callback(initFunction callback);
 void set_finalize_callback(finalizeFunction callback);
 void set_parse_args_callback(parseArgsFunction callback);
diff --git a/packages/kokkos/core/src/impl/Kokkos_Profiling_C_Interface.h b/packages/kokkos/core/src/impl/Kokkos_Profiling_C_Interface.h
index ed8751c50cc04d915b7b3c371a6ec05756ff6087..2c8d1428fc595e0e6724624465ab839f5c80b138 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Profiling_C_Interface.h
+++ b/packages/kokkos/core/src/impl/Kokkos_Profiling_C_Interface.h
@@ -54,7 +54,7 @@
 #include <stdbool.h>
 #endif
 
-#define KOKKOSP_INTERFACE_VERSION 20210225
+#define KOKKOSP_INTERFACE_VERSION 20210623
 
 // Profiling
 
diff --git a/packages/kokkos/core/src/impl/Kokkos_Profiling_Interface.hpp b/packages/kokkos/core/src/impl/Kokkos_Profiling_Interface.hpp
index 7809632f78ddf33d8429b353723736b68e3b7536..a7aec2e6fd53f6a6b37b011452b58750b092f096 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Profiling_Interface.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Profiling_Interface.hpp
@@ -56,6 +56,14 @@
 namespace Kokkos {
 namespace Tools {
 namespace Experimental {
+
+constexpr const uint32_t NumReservedDeviceIDs = 1;
+
+enum SpecialSynchronizationCases : int {
+  GlobalDeviceSynchronization     = 1,
+  DeepCopyResourceSynchronization = 2,
+};
+
 enum struct DeviceType {
   Serial,
   OpenMP,
@@ -68,15 +76,49 @@ enum struct DeviceType {
   Unknown
 };
 
+struct ExecutionSpaceIdentifier {
+  DeviceType type;
+  uint32_t device_id;
+  uint32_t instance_id;
+};
+inline DeviceType devicetype_from_uint32t(const uint32_t in) {
+  switch (in) {
+    case 0: return DeviceType::Serial;
+    case 1: return DeviceType::OpenMP;
+    case 2: return DeviceType::Cuda;
+    case 3: return DeviceType::HIP;
+    case 4: return DeviceType::OpenMPTarget;
+    case 5: return DeviceType::HPX;
+    case 6: return DeviceType::Threads;
+    case 7: return DeviceType::SYCL;
+    default: return DeviceType::Unknown;  // TODO: error out?
+  }
+}
+
+inline ExecutionSpaceIdentifier identifier_from_devid(const uint32_t in) {
+  // ExecutionSpaceIdentifier out;
+  // out.type = in >> 24;
+  // out.device_id = in >> 17;
+  // out.instance_id = ((uint32_t(-1)) << 17 ) & in;
+  return {devicetype_from_uint32t(in >> 24),
+          (~((uint32_t(-1)) << 24)) & (in >> 17),
+          (~((uint32_t(-1)) << 17)) & in};
+}
+
 template <typename ExecutionSpace>
 struct DeviceTypeTraits;
 
 constexpr const size_t device_type_bits = 8;
 constexpr const size_t instance_bits    = 24;
 template <typename ExecutionSpace>
+constexpr uint32_t device_id_root() {
+  constexpr auto device_id =
+      static_cast<uint32_t>(DeviceTypeTraits<ExecutionSpace>::id);
+  return (device_id << instance_bits);
+}
+template <typename ExecutionSpace>
 inline uint32_t device_id(ExecutionSpace const& space) noexcept {
-  auto device_id = static_cast<uint32_t>(DeviceTypeTraits<ExecutionSpace>::id);
-  return (device_id << instance_bits) + space.impl_instance_id();
+  return device_id_root<ExecutionSpace>() + space.impl_instance_id();
 }
 }  // namespace Experimental
 }  // namespace Tools
diff --git a/packages/kokkos/core/src/impl/Kokkos_QuadPrecisionMath.hpp b/packages/kokkos/core/src/impl/Kokkos_QuadPrecisionMath.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b67cede45bfddd657448a054aca3254d627267ce
--- /dev/null
+++ b/packages/kokkos/core/src/impl/Kokkos_QuadPrecisionMath.hpp
@@ -0,0 +1,187 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#ifndef KOKKOS_QUAD_PRECISION_MATH_HPP
+#define KOKKOS_QUAD_PRECISION_MATH_HPP
+
+#include <Kokkos_Macros.hpp>
+
+#if defined(KOKKOS_ENABLE_LIBQUADMATH)
+
+#include <Kokkos_NumericTraits.hpp>
+
+#include <quadmath.h>
+
+#if !(defined(__FLOAT128__) || defined(__SIZEOF_FLOAT128__))
+#error __float128 not supported on this host
+#endif
+
+//<editor-fold desc="numeric traits __float128 specializations">
+namespace Kokkos {
+namespace Experimental {
+#if defined(KOKKOS_ENABLE_CXX17)
+#define KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(TRAIT, TYPE, VALUE_TYPE, VALUE) \
+  template <>                                                                \
+  struct TRAIT<TYPE> {                                                       \
+    static constexpr VALUE_TYPE value = VALUE;                               \
+  };                                                                         \
+  template <>                                                                \
+  inline constexpr auto TRAIT##_v<TYPE> = TRAIT<TYPE>::value;
+#else
+#define KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(TRAIT, TYPE, VALUE_TYPE, VALUE) \
+  template <>                                                                \
+  struct TRAIT<TYPE> {                                                       \
+    static constexpr VALUE_TYPE value = VALUE;                               \
+  };
+#endif
+
+// clang-format off
+// Numeric distinguished value traits
+// Workaround GCC bug https://godbolt.org/z/qWb5oe4dx
+// error: '__builtin_huge_valq()' is not a constant expression
+#if defined(KOKKOS_COMPILER_GNU) && (KOKKOS_COMPILER_GNU >= 710)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(infinity,       __float128, __float128, HUGE_VALQ)
+#endif
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(finite_min,     __float128, __float128, -FLT128_MAX)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(finite_max,     __float128, __float128, FLT128_MAX)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(epsilon,        __float128, __float128, FLT128_EPSILON)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(round_error,    __float128, __float128, static_cast<__float128>(0.5))
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(norm_min,       __float128, __float128, FLT128_MIN)
+
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(digits,         __float128,        int, FLT128_MANT_DIG)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(digits10,       __float128,        int, FLT128_DIG)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(max_digits10,   __float128,        int, 36)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(radix,          __float128,        int, 2)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(min_exponent,   __float128,        int, FLT128_MIN_EXP)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(max_exponent,   __float128,        int, FLT128_MAX_EXP)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(min_exponent10, __float128,        int, FLT128_MIN_10_EXP)
+KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT(max_exponent10, __float128,        int, FLT128_MAX_10_EXP)
+// clang-format on
+
+#undef KOKKOS_IMPL_SPECIALIZE_NUMERIC_TRAIT
+}  // namespace Experimental
+}  // namespace Kokkos
+//</editor-fold>
+
+namespace Kokkos {
+template <>
+struct reduction_identity<__float128> {
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static __float128 sum() {
+    return static_cast<__float128>(0.0);
+  }
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static __float128 prod() {
+    return static_cast<__float128>(1.0);
+  }
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static __float128 max() {
+    return -FLT128_MAX;
+  }
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static __float128 min() {
+    return FLT128_MAX;
+  }
+};
+}  // namespace Kokkos
+
+//<editor-fold desc="Common mathematical functions __float128 overloads">
+namespace Kokkos {
+namespace Experimental {
+// clang-format off
+// Basic operations
+inline __float128 fabs(__float128 x) { return ::fabsq(x); }
+inline __float128 fmod(__float128 x, __float128 y) { return ::fmodq(x, y); }
+inline __float128 remainder(__float128 x, __float128 y) { return ::remainderq(x, y); }
+inline __float128 fmin(__float128 x, __float128 y) { return ::fminq(x, y); }
+inline __float128 fmax(__float128 x, __float128 y) { return ::fmaxq(x, y); }
+inline __float128 fdim(__float128 x, __float128 y) { return ::fdimq(x, y); }
+inline __float128 nanq(char const* arg) { return ::nanq(arg); }
+// Power functions
+inline __float128 pow(__float128 x, __float128 y) { return ::powq(x, y); }
+inline __float128 sqrt(__float128 x) { return ::sqrtq(x); }
+inline __float128 cbrt(__float128 x) { return ::cbrtq(x); }
+inline __float128 hypot(__float128 x, __float128 y) { return ::hypotq(x, y); }
+// Exponential functions
+inline __float128 exp(__float128 x) { return ::expq(x); }
+#if defined(KOKKOS_COMPILER_GNU) && (KOKKOS_COMPILER_GNU >= 910)
+inline __float128 exp2(__float128 x) { return ::exp2q(x); }
+#endif
+inline __float128 expm1(__float128 x) { return ::expm1q(x); }
+inline __float128 log(__float128 x) { return ::logq(x); }
+inline __float128 log10(__float128 x) { return ::log10q(x); }
+inline __float128 log2(__float128 x) { return ::log2q(x); }
+inline __float128 log1p(__float128 x) { return ::log1pq(x); }
+// Trigonometric functions
+inline __float128 sin(__float128 x) { return ::sinq(x); }
+inline __float128 cos(__float128 x) { return ::cosq(x); }
+inline __float128 tan(__float128 x) { return ::tanq(x); }
+inline __float128 asin(__float128 x) { return ::asinq(x); }
+inline __float128 acos(__float128 x) { return ::acosq(x); }
+inline __float128 atan(__float128 x) { return ::atanq(x); }
+inline __float128 atan2(__float128 x, __float128 y) { return ::atan2q(x, y); }
+// Hyperbolic functions
+inline __float128 sinh(__float128 x) { return ::sinhq(x); }
+inline __float128 cosh(__float128 x) { return ::coshq(x); }
+inline __float128 tanh(__float128 x) { return ::tanhq(x); }
+inline __float128 asinh(__float128 x) { return ::asinhq(x); }
+inline __float128 acosh(__float128 x) { return ::acoshq(x); }
+inline __float128 atanh(__float128 x) { return ::atanhq(x); }
+// Error and gamma functions
+inline __float128 erf(__float128 x) { return ::erfq(x); }
+inline __float128 erfc(__float128 x) { return ::erfcq(x); }
+inline __float128 tgamma(__float128 x) { return ::tgammaq(x); }
+inline __float128 lgamma(__float128 x) { return ::lgammaq(x); }
+// Nearest integer floating point operations
+inline __float128 ceil(__float128 x) { return ::ceilq(x); }
+inline __float128 floor(__float128 x) { return ::floorq(x); }
+inline __float128 trunc(__float128 x) { return ::truncq(x); }
+inline __float128 nearbyint(__float128 x) { return ::nearbyintq(x); }
+// Classification and comparison
+inline bool isfinite(__float128 x) { return !::isinfq(x); }  // isfiniteq not provided
+inline bool isinf(__float128 x) { return ::isinfq(x); }
+inline bool isnan(__float128 x) { return ::isnanq(x); }
+}  // namespace Experimental
+}  // namespace Kokkos
+//</editor-fold>
+
+#endif
+
+#endif
diff --git a/packages/kokkos/core/src/impl/Kokkos_Serial.cpp b/packages/kokkos/core/src/impl/Kokkos_Serial.cpp
index 4bd037906506bd27654067d5c2fda99fb59684ca..c49e838d8f0b0961b9dfd2bc76c07b5370cb2629 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Serial.cpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Serial.cpp
@@ -58,28 +58,59 @@
 
 namespace Kokkos {
 namespace Impl {
-namespace {
 
-HostThreadTeamData g_serial_thread_team_data;
+bool SerialInternal::is_initialized() { return m_is_initialized; }
 
-bool g_serial_is_initialized = false;
+void SerialInternal::initialize() {
+  if (is_initialized()) return;
 
-}  // namespace
+  Impl::SharedAllocationRecord<void, void>::tracking_enable();
+
+  // Init the array of locks used for arbitrarily sized atomics
+  Impl::init_lock_array_host_space();
+
+  m_is_initialized = true;
+}
+
+void SerialInternal::finalize() {
+  if (m_thread_team_data.scratch_buffer()) {
+    m_thread_team_data.disband_team();
+    m_thread_team_data.disband_pool();
+
+    Kokkos::HostSpace space;
+
+    space.deallocate(m_thread_team_data.scratch_buffer(),
+                     m_thread_team_data.scratch_bytes());
+
+    m_thread_team_data.scratch_assign(nullptr, 0, 0, 0, 0, 0);
+  }
+
+  Kokkos::Profiling::finalize();
+
+  m_is_initialized = false;
+}
+
+SerialInternal& SerialInternal::singleton() {
+  static SerialInternal* self = nullptr;
+  if (!self) {
+    self = new SerialInternal();
+  }
+  return *self;
+}
 
 // Resize thread team data scratch memory
-void serial_resize_thread_team_data(size_t pool_reduce_bytes,
-                                    size_t team_reduce_bytes,
-                                    size_t team_shared_bytes,
-                                    size_t thread_local_bytes) {
+void SerialInternal::resize_thread_team_data(size_t pool_reduce_bytes,
+                                             size_t team_reduce_bytes,
+                                             size_t team_shared_bytes,
+                                             size_t thread_local_bytes) {
   if (pool_reduce_bytes < 512) pool_reduce_bytes = 512;
   if (team_reduce_bytes < 512) team_reduce_bytes = 512;
 
-  const size_t old_pool_reduce = g_serial_thread_team_data.pool_reduce_bytes();
-  const size_t old_team_reduce = g_serial_thread_team_data.team_reduce_bytes();
-  const size_t old_team_shared = g_serial_thread_team_data.team_shared_bytes();
-  const size_t old_thread_local =
-      g_serial_thread_team_data.thread_local_bytes();
-  const size_t old_alloc_bytes = g_serial_thread_team_data.scratch_bytes();
+  const size_t old_pool_reduce  = m_thread_team_data.pool_reduce_bytes();
+  const size_t old_team_reduce  = m_thread_team_data.team_reduce_bytes();
+  const size_t old_team_shared  = m_thread_team_data.team_shared_bytes();
+  const size_t old_thread_local = m_thread_team_data.thread_local_bytes();
+  const size_t old_alloc_bytes  = m_thread_team_data.scratch_bytes();
 
   // Allocate if any of the old allocation is tool small:
 
@@ -92,12 +123,12 @@ void serial_resize_thread_team_data(size_t pool_reduce_bytes,
     Kokkos::HostSpace space;
 
     if (old_alloc_bytes) {
-      g_serial_thread_team_data.disband_team();
-      g_serial_thread_team_data.disband_pool();
+      m_thread_team_data.disband_team();
+      m_thread_team_data.disband_pool();
 
       space.deallocate("Kokkos::Serial::scratch_mem",
-                       g_serial_thread_team_data.scratch_buffer(),
-                       g_serial_thread_team_data.scratch_bytes());
+                       m_thread_team_data.scratch_buffer(),
+                       m_thread_team_data.scratch_bytes());
     }
 
     if (pool_reduce_bytes < old_pool_reduce) {
@@ -125,56 +156,37 @@ void serial_resize_thread_team_data(size_t pool_reduce_bytes,
       Kokkos::Impl::throw_runtime_exception(failure.get_error_message());
     }
 
-    g_serial_thread_team_data.scratch_assign(
-        ((char*)ptr), alloc_bytes, pool_reduce_bytes, team_reduce_bytes,
-        team_shared_bytes, thread_local_bytes);
+    m_thread_team_data.scratch_assign(static_cast<char*>(ptr), alloc_bytes,
+                                      pool_reduce_bytes, team_reduce_bytes,
+                                      team_shared_bytes, thread_local_bytes);
 
-    HostThreadTeamData* pool[1] = {&g_serial_thread_team_data};
+    HostThreadTeamData* pool[1] = {&m_thread_team_data};
 
-    g_serial_thread_team_data.organize_pool(pool, 1);
-    g_serial_thread_team_data.organize_team(1);
+    m_thread_team_data.organize_pool(pool, 1);
+    m_thread_team_data.organize_team(1);
   }
 }
-
-HostThreadTeamData* serial_get_thread_team_data() {
-  return &g_serial_thread_team_data;
-}
-
 }  // namespace Impl
-}  // namespace Kokkos
 
-/*--------------------------------------------------------------------------*/
-
-namespace Kokkos {
+Serial::Serial()
+#ifdef KOKKOS_IMPL_WORKAROUND_ICE_IN_TRILINOS_WITH_OLD_INTEL_COMPILERS
+    : m_space_instance(&Impl::SerialInternal::singleton()) {
+}
+#else
+    : m_space_instance(&Impl::SerialInternal::singleton(),
+                       [](Impl::SerialInternal*) {}) {
+}
+#endif
 
-bool Serial::impl_is_initialized() { return Impl::g_serial_is_initialized; }
+bool Serial::impl_is_initialized() {
+  return Impl::SerialInternal::singleton().is_initialized();
+}
 
 void Serial::impl_initialize() {
-  Impl::SharedAllocationRecord<void, void>::tracking_enable();
-
-  // Init the array of locks used for arbitrarily sized atomics
-  Impl::init_lock_array_host_space();
-
-  Impl::g_serial_is_initialized = true;
+  Impl::SerialInternal::singleton().initialize();
 }
 
-void Serial::impl_finalize() {
-  if (Impl::g_serial_thread_team_data.scratch_buffer()) {
-    Impl::g_serial_thread_team_data.disband_team();
-    Impl::g_serial_thread_team_data.disband_pool();
-
-    Kokkos::HostSpace space;
-
-    space.deallocate(Impl::g_serial_thread_team_data.scratch_buffer(),
-                     Impl::g_serial_thread_team_data.scratch_bytes());
-
-    Impl::g_serial_thread_team_data.scratch_assign(nullptr, 0, 0, 0, 0, 0);
-  }
-
-  Kokkos::Profiling::finalize();
-
-  Impl::g_serial_is_initialized = false;
-}
+void Serial::impl_finalize() { Impl::SerialInternal::singleton().finalize(); }
 
 const char* Serial::name() { return "Serial"; }
 
@@ -198,6 +210,9 @@ void SerialSpaceInitializer::finalize(const bool) {
 }
 
 void SerialSpaceInitializer::fence() { Kokkos::Serial::impl_static_fence(); }
+void SerialSpaceInitializer::fence(const std::string& name) {
+  Kokkos::Serial::impl_static_fence(name);
+}
 
 void SerialSpaceInitializer::print_configuration(std::ostream& msg,
                                                  const bool detail) {
diff --git a/packages/kokkos/core/src/impl/Kokkos_Serial_Task.hpp b/packages/kokkos/core/src/impl/Kokkos_Serial_Task.hpp
index 3ac3899acaf9f3695025472fc5f02cb0708f64fb..be732f4486d4618b4f8601d1859be44ce1a31296 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Serial_Task.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Serial_Task.hpp
@@ -76,14 +76,18 @@ class TaskQueueSpecialization<SimpleTaskScheduler<Kokkos::Serial, QueueType> > {
   static void execute(scheduler_type const& scheduler) {
     using task_base_type = typename scheduler_type::task_base_type;
 
+    auto const& serial_execution_space = scheduler.get_execution_space();
+
     // Set default buffers
-    serial_resize_thread_team_data(0,   /* global reduce buffer */
-                                   512, /* team reduce buffer */
-                                   0,   /* team shared buffer */
-                                   0    /* thread local buffer */
-    );
+    serial_execution_space.impl_internal_space_instance()
+        ->resize_thread_team_data(0,   /* global reduce buffer */
+                                  512, /* team reduce buffer */
+                                  0,   /* team shared buffer */
+                                  0    /* thread local buffer */
+        );
 
-    Impl::HostThreadTeamData& self = *Impl::serial_get_thread_team_data();
+    auto& self = serial_execution_space.impl_internal_space_instance()
+                     ->m_thread_team_data;
 
     auto& queue         = scheduler.queue();
     auto team_scheduler = scheduler.get_team_scheduler(0);
@@ -147,9 +151,11 @@ class TaskQueueSpecializationConstrained<
 
     task_base_type* const end = (task_base_type*)task_base_type::EndTag;
 
-    Impl::HostThreadTeamData* const data = Impl::serial_get_thread_team_data();
+    execution_space serial_execution_space;
+    auto& data = serial_execution_space.impl_internal_space_instance()
+                     ->m_thread_team_data;
 
-    member_type exec(scheduler, *data);
+    member_type exec(scheduler, data);
 
     // Loop until no runnable task
 
@@ -181,18 +187,22 @@ class TaskQueueSpecializationConstrained<
 
     task_base_type* const end = (task_base_type*)task_base_type::EndTag;
 
+    execution_space serial_execution_space;
+
     // Set default buffers
-    serial_resize_thread_team_data(0,   /* global reduce buffer */
-                                   512, /* team reduce buffer */
-                                   0,   /* team shared buffer */
-                                   0    /* thread local buffer */
-    );
+    serial_execution_space.impl_internal_space_instance()
+        ->resize_thread_team_data(0,   /* global reduce buffer */
+                                  512, /* team reduce buffer */
+                                  0,   /* team shared buffer */
+                                  0    /* thread local buffer */
+        );
 
     auto* const queue = scheduler.m_queue;
 
-    Impl::HostThreadTeamData* const data = Impl::serial_get_thread_team_data();
+    auto& data = serial_execution_space.impl_internal_space_instance()
+                     ->m_thread_team_data;
 
-    member_type exec(scheduler, *data);
+    member_type exec(scheduler, data);
 
     // Loop until all queues are empty
     while (0 < queue->m_ready_count) {
@@ -210,16 +220,6 @@ class TaskQueueSpecializationConstrained<
 
         (*task->m_apply)(task, &exec);
 
-#if 0
-        printf( "TaskQueue<Serial>::executed: 0x%lx { 0x%lx 0x%lx %d %d %d }\n"
-        , uintptr_t(task)
-        , uintptr_t(task->m_wait)
-        , uintptr_t(task->m_next)
-        , task->m_task_type
-        , task->m_priority
-        , task->m_ref_count );
-#endif
-
         // If a respawn then re-enqueue otherwise the task is complete
         // and all tasks waiting on this task are updated.
         queue->complete(task);
diff --git a/packages/kokkos/core/src/impl/Kokkos_SharedAlloc.cpp b/packages/kokkos/core/src/impl/Kokkos_SharedAlloc.cpp
index 917ae72081c6a5eee98b4e02827097446cb29e0b..3efff98e459e8a8b92983d195a7a4486672bdce4 100644
--- a/packages/kokkos/core/src/impl/Kokkos_SharedAlloc.cpp
+++ b/packages/kokkos/core/src/impl/Kokkos_SharedAlloc.cpp
@@ -259,6 +259,9 @@ SharedAllocationRecord<void, void>* SharedAllocationRecord<
     while ((root_next = Kokkos::atomic_exchange(&arg_record->m_root->m_next,
                                                 zero)) == nullptr)
       ;
+    // We need a memory_fence() here so that the following update
+    // is properly sequenced
+    Kokkos::memory_fence();
 
     arg_record->m_next->m_prev = arg_record->m_prev;
 
diff --git a/packages/kokkos/core/src/impl/Kokkos_SimpleTaskScheduler.hpp b/packages/kokkos/core/src/impl/Kokkos_SimpleTaskScheduler.hpp
index 0773a0914befe4e9db3b3b79ae3c446bcb0f3ad1..7f222c92ca704908e7e6be05229c976d113395f9 100644
--- a/packages/kokkos/core/src/impl/Kokkos_SimpleTaskScheduler.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_SimpleTaskScheduler.hpp
@@ -55,7 +55,6 @@
 //----------------------------------------------------------------------------
 
 #include <Kokkos_MemoryPool.hpp>
-#include <impl/Kokkos_Tags.hpp>
 
 #include <Kokkos_Future.hpp>
 #include <impl/Kokkos_TaskQueue.hpp>
diff --git a/packages/kokkos/core/src/impl/Kokkos_SingleTaskQueue.hpp b/packages/kokkos/core/src/impl/Kokkos_SingleTaskQueue.hpp
index a0eccffb627f39f1810978aa0d3ab25c9458e4e8..0584cd29eb70470f4c206317d35a57f24893a518 100644
--- a/packages/kokkos/core/src/impl/Kokkos_SingleTaskQueue.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_SingleTaskQueue.hpp
@@ -58,8 +58,7 @@
 
 #include <impl/Kokkos_TaskQueueMemoryManager.hpp>
 #include <impl/Kokkos_TaskQueueCommon.hpp>
-#include <impl/Kokkos_Memory_Fence.hpp>
-#include <impl/Kokkos_Atomic_Increment.hpp>
+#include <Kokkos_Atomic.hpp>
 #include <impl/Kokkos_OptionalRef.hpp>
 #include <impl/Kokkos_LIFO.hpp>
 
diff --git a/packages/kokkos/core/src/impl/Kokkos_Tags.hpp b/packages/kokkos/core/src/impl/Kokkos_Tags.hpp
deleted file mode 100644
index eea4c938661afa00f4dad929312bf6cfa2b83776..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/src/impl/Kokkos_Tags.hpp
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#ifndef KOKKOS_TAGS_HPP
-#define KOKKOS_TAGS_HPP
-
-#include <impl/Kokkos_Traits.hpp>
-#include <Kokkos_Core_fwd.hpp>
-#include <type_traits>
-
-//----------------------------------------------------------------------------
-//----------------------------------------------------------------------------
-
-/** KOKKOS_IMPL_HAS_TYPE( Type )
- *
- * defines a meta-function that check if a type expose an internal alias which
- * matches Type
- *
- * e.g.
- *   KOKKOS_IMPL_HAS_TYPE( array_layout );
- *   struct Foo { using array_layout = void; };
- *   have_array_layout<Foo>::value == 1;
- */
-#define KOKKOS_IMPL_HAS_TYPE(TYPE)                                             \
-  template <typename T>                                                        \
-  struct have_##TYPE {                                                         \
-   private:                                                                    \
-    template <typename U, typename = void>                                     \
-    struct X : std::false_type {};                                             \
-    template <typename U>                                                      \
-    struct X<U, typename std::conditional<true, void, typename X::TYPE>::type> \
-        : std::true_type {};                                                   \
-                                                                               \
-   public:                                                                     \
-    using type = typename X<T>::type;                                          \
-    enum : bool { value = type::value };                                       \
-  };
-
-//----------------------------------------------------------------------------
-//----------------------------------------------------------------------------
-
-namespace Kokkos {
-namespace Impl {
-
-template <typename T>
-using is_void = std::is_same<void, T>;
-
-}
-}  // namespace Kokkos
-
-//----------------------------------------------------------------------------
-//----------------------------------------------------------------------------
-
-#endif
diff --git a/packages/kokkos/core/src/impl/Kokkos_TaskBase.hpp b/packages/kokkos/core/src/impl/Kokkos_TaskBase.hpp
index 2d0f62a563712a1182849fd8dc43349f6996a42e..06581052a8f687aeaff85d1368fd271407f0c36e 100644
--- a/packages/kokkos/core/src/impl/Kokkos_TaskBase.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_TaskBase.hpp
@@ -203,14 +203,17 @@ class TaskBase {
 
     // Assign dependence to m_next.  It will be processed in the subsequent
     // call to schedule.  Error if the dependence is reset.
-    if (lock != Kokkos::atomic_exchange(&m_next, dep)) {
+    if (lock != Kokkos::Impl::desul_atomic_exchange(
+                    &m_next, dep, Kokkos::Impl::MemoryOrderSeqCst(),
+                    Kokkos::Impl::MemoryScopeDevice())) {
       Kokkos::abort("TaskScheduler ERROR: resetting task dependence");
     }
-
     if (nullptr != dep) {
       // The future may be destroyed upon returning from this call
       // so increment reference count to track this assignment.
-      Kokkos::atomic_increment(&(dep->m_ref_count));
+      Kokkos::Impl::desul_atomic_inc(&(dep->m_ref_count),
+                                     Kokkos::Impl::MemoryOrderSeqCst(),
+                                     Kokkos::Impl::MemoryScopeDevice());
     }
   }
 
diff --git a/packages/kokkos/core/src/impl/Kokkos_TaskNode.hpp b/packages/kokkos/core/src/impl/Kokkos_TaskNode.hpp
index 42afa93cdcc4db4f4c0223d7b85f5edb8256ee31..caf1d0a84b82e3e6b121476c8cfd0213775dd69f 100644
--- a/packages/kokkos/core/src/impl/Kokkos_TaskNode.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_TaskNode.hpp
@@ -151,6 +151,7 @@ class ReferenceCountedBase {
   bool decrement_and_check_reference_count() {
     // TODO @tasking @memory_order DSH memory order
     auto old_count = Kokkos::atomic_fetch_add(&m_ref_count, -1);
+    Kokkos::memory_fence();
 
     KOKKOS_ASSERT(old_count > 0 && "reference count greater less than zero!");
 
@@ -158,7 +159,11 @@ class ReferenceCountedBase {
   }
 
   KOKKOS_INLINE_FUNCTION
-  void increment_reference_count() { Kokkos::atomic_increment(&m_ref_count); }
+  void increment_reference_count() {
+    Kokkos::Impl::desul_atomic_inc(&m_ref_count,
+                                   Kokkos::Impl::MemoryOrderSeqCst(),
+                                   Kokkos::Impl::MemoryScopeDevice());
+  }
 };
 
 template <class TaskQueueTraits, class SchedulingInfo>
diff --git a/packages/kokkos/core/src/impl/Kokkos_TaskQueue.hpp b/packages/kokkos/core/src/impl/Kokkos_TaskQueue.hpp
index c0d2eca9c106305e1bfdeb8efd634f657df90c8b..e74e84a2e535b5953ae58667a2a7f4b4f53b293d 100644
--- a/packages/kokkos/core/src/impl/Kokkos_TaskQueue.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_TaskQueue.hpp
@@ -58,8 +58,7 @@
 #include <impl/Kokkos_TaskBase.hpp>
 #include <impl/Kokkos_TaskResult.hpp>
 
-#include <impl/Kokkos_Memory_Fence.hpp>
-#include <impl/Kokkos_Atomic_Increment.hpp>
+#include <Kokkos_Atomic.hpp>
 #include <impl/Kokkos_OptionalRef.hpp>
 #include <impl/Kokkos_LIFO.hpp>
 
@@ -188,25 +187,11 @@ class TaskQueue : public TaskQueueBase {
   // Assign task pointer with reference counting of assigned tasks
   KOKKOS_FUNCTION static void assign(task_root_type** const lhs,
                                      task_root_type* const rhs) {
-#if 0
-  {
-    printf( "assign( 0x%lx { 0x%lx %d %d } , 0x%lx { 0x%lx %d %d } )\n"
-          , uintptr_t( lhs ? *lhs : 0 )
-          , uintptr_t( lhs && *lhs ? (*lhs)->m_next : 0 )
-          , int( lhs && *lhs ? (*lhs)->m_task_type : 0 )
-          , int( lhs && *lhs ? (*lhs)->m_ref_count : 0 )
-          , uintptr_t(rhs)
-          , uintptr_t( rhs ? rhs->m_next : 0 )
-          , int( rhs ? rhs->m_task_type : 0 )
-          , int( rhs ? rhs->m_ref_count : 0 )
-          );
-    fflush( stdout );
-  }
-#endif
-
     if (*lhs) decrement(*lhs);
     if (rhs) {
-      Kokkos::atomic_increment(&(rhs->m_ref_count));
+      Kokkos::Impl::desul_atomic_inc(&rhs->m_ref_count,
+                                     Kokkos::Impl::MemoryOrderSeqCst(),
+                                     Kokkos::Impl::MemoryScopeDevice());
     }
 
     // Force write of *lhs
@@ -234,13 +219,7 @@ class TaskQueue : public TaskQueueBase {
 
     using task_type = Impl::Task<execution_space, value_type, FunctorType>;
 
-    enum : size_t { align = (1 << 4), align_mask = align - 1 };
-    enum : size_t { task_size = sizeof(task_type) };
-    enum : size_t { result_size = Impl::TaskResult<value_type>::size };
-    enum : size_t {
-      alloc_size = ((task_size + align_mask) & ~align_mask) +
-                   ((result_size + align_mask) & ~align_mask)
-    };
+    constexpr size_t task_size = sizeof(task_type);
 
     return m_memory.allocate_block_size(task_size);
   }
diff --git a/packages/kokkos/core/src/impl/Kokkos_TaskQueueCommon.hpp b/packages/kokkos/core/src/impl/Kokkos_TaskQueueCommon.hpp
index cae06d4ea5ca17b5924a7bbf8c415d6f0a3ab070..757e5f98864bc3c74faa8f4bdfffb795e68aab60 100644
--- a/packages/kokkos/core/src/impl/Kokkos_TaskQueueCommon.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_TaskQueueCommon.hpp
@@ -57,8 +57,7 @@
 #include <impl/Kokkos_TaskResult.hpp>
 
 #include <impl/Kokkos_TaskQueueMemoryManager.hpp>
-#include <impl/Kokkos_Memory_Fence.hpp>
-#include <impl/Kokkos_Atomic_Increment.hpp>
+#include <Kokkos_Atomic.hpp>
 #include <impl/Kokkos_OptionalRef.hpp>
 #include <impl/Kokkos_LIFO.hpp>
 
@@ -88,6 +87,7 @@ class TaskQueueCommonMixin {
   // <editor-fold desc="Constructors, destructor, and assignment"> {{{2
 
   TaskQueueCommonMixin() : m_ready_count(0) {
+    Kokkos::memory_fence();
     // TODO @tasking @memory_order DSH figure out if I need this store to be
     // atomic
   }
@@ -158,14 +158,17 @@ class TaskQueueCommonMixin {
   KOKKOS_INLINE_FUNCTION
   void _increment_ready_count() {
     // TODO @tasking @memory_order DSH memory order
-    Kokkos::atomic_increment(&this->m_ready_count);
+    Kokkos::Impl::desul_atomic_inc(&this->m_ready_count,
+                                   Kokkos::Impl::MemoryOrderSeqCst(),
+                                   Kokkos::Impl::MemoryScopeDevice());
   }
 
   KOKKOS_INLINE_FUNCTION
   void _decrement_ready_count() {
     // TODO @tasking @memory_order DSH memory order
-    Kokkos::atomic_decrement(&this->m_ready_count);
-    Kokkos::memory_fence();
+    Kokkos::Impl::desul_atomic_dec(&this->m_ready_count,
+                                   Kokkos::Impl::MemoryOrderSeqCst(),
+                                   Kokkos::Impl::MemoryScopeDevice());
   }
 
  public:
@@ -476,7 +479,7 @@ class TaskQueueCommonMixin {
   }
 
   template <class ExecutionSpace, class MemorySpace, class MemoryPool>
-  static /* KOKKOS_CONSTEXPR_14 */ size_t task_queue_allocation_size(
+  static /* constexpr */ size_t task_queue_allocation_size(
       ExecutionSpace const&, MemorySpace const&, MemoryPool const&)
   // requires Same<ExecutionSpace, typename Derived::execution_space>
   //            && Same<MemorySpace, typename Derived::memory_space>
diff --git a/packages/kokkos/core/src/impl/Kokkos_TaskQueueMemoryManager.hpp b/packages/kokkos/core/src/impl/Kokkos_TaskQueueMemoryManager.hpp
index 6e2481f93567a671a5ad66f3536b45c009286eca..3a71aa17e69042c791e9e7302c3c14cdca91b8aa 100644
--- a/packages/kokkos/core/src/impl/Kokkos_TaskQueueMemoryManager.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_TaskQueueMemoryManager.hpp
@@ -56,8 +56,7 @@
 #include <impl/Kokkos_TaskBase.hpp>
 #include <impl/Kokkos_TaskResult.hpp>
 
-#include <impl/Kokkos_Memory_Fence.hpp>
-#include <impl/Kokkos_Atomic_Increment.hpp>
+#include <Kokkos_Atomic.hpp>
 #include <impl/Kokkos_OptionalRef.hpp>
 #include <impl/Kokkos_LIFO.hpp>
 
@@ -103,8 +102,9 @@ class TaskQueueMemoryManager : public TaskQueueBase {
     } else {
       void* data = m_pool.allocate(static_cast<size_t>(requested_size));
 
-      // Kokkos::atomic_increment(&m_accum_alloc); // memory_order_relaxed
-      Kokkos::atomic_increment(&m_count_alloc);  // memory_order_relaxed
+      Kokkos::Impl::desul_atomic_inc(
+          &m_count_alloc, Kokkos::Impl::MemoryOrderSeqCst(),
+          Kokkos::Impl::MemoryScopeDevice());  // TODO? memory_order_relaxed
       // TODO @tasking @minor DSH make this thread safe? (otherwise, it's just
       // an approximation, which is probably fine...)
       if (m_max_alloc < m_count_alloc) m_max_alloc = m_count_alloc;
@@ -200,7 +200,9 @@ class TaskQueueMemoryManager : public TaskQueueBase {
   KOKKOS_INLINE_FUNCTION void deallocate(
       PoolAllocatedObjectBase<CountType>&& obj) {
     m_pool.deallocate((void*)&obj, 1);
-    Kokkos::atomic_decrement(&m_count_alloc);  // memory_order_relaxed
+    Kokkos::Impl::desul_atomic_dec(
+        &m_count_alloc, Kokkos::Impl::MemoryOrderSeqCst(),
+        Kokkos::Impl::MemoryScopeDevice());  // TODO? memory_order_relaxed
   }
 
   KOKKOS_INLINE_FUNCTION
diff --git a/packages/kokkos/core/src/impl/Kokkos_TaskQueueMultiple.hpp b/packages/kokkos/core/src/impl/Kokkos_TaskQueueMultiple.hpp
index efee3d051dc8fb4e112219527bb322404ff4dfe6..5f98e8d85e9214289ce43f98b920ec27da4a672f 100644
--- a/packages/kokkos/core/src/impl/Kokkos_TaskQueueMultiple.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_TaskQueueMultiple.hpp
@@ -59,9 +59,7 @@
 #include <impl/Kokkos_TaskResult.hpp>
 #include <impl/Kokkos_TaskQueue.hpp>
 
-#include <impl/Kokkos_Memory_Fence.hpp>
-#include <impl/Kokkos_Atomic_Increment.hpp>
-#include <impl/Kokkos_Atomic_Decrement.hpp>
+#include <Kokkos_Atomic.hpp>
 
 #include <string>
 #include <typeinfo>
@@ -159,8 +157,14 @@ class TaskQueueMultiple : public TaskQueue<ExecSpace, MemorySpace> {
               // task stolen.
               // first increment our ready count, then decrement the ready count
               // on the other queue:
-              Kokkos::atomic_increment(&this->m_ready_count);
-              Kokkos::atomic_decrement(&steal_from.m_ready_count);
+              Kokkos::Impl::desul_atomic_inc(
+                  &this->m_ready_count, Kokkos::Impl::MemoryOrderSeqCst(),
+                  Kokkos::Impl::MemoryScopeDevice());  // TODO?
+                                                       // memory_order_relaxed
+              Kokkos::Impl::desul_atomic_dec(
+                  &steal_from.m_ready_count, Kokkos::Impl::MemoryOrderSeqCst(),
+                  Kokkos::Impl::MemoryScopeDevice());  // TODO?
+                                                       // memory_order_relaxed
               return rv;
             }
           }
diff --git a/packages/kokkos/core/src/impl/Kokkos_TaskQueue_impl.hpp b/packages/kokkos/core/src/impl/Kokkos_TaskQueue_impl.hpp
index a87e5f72721f95f06d1c9f90c17e92d0e1fec2fb..324227cf5e615f184492de9471fa0f78700ae11a 100644
--- a/packages/kokkos/core/src/impl/Kokkos_TaskQueue_impl.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_TaskQueue_impl.hpp
@@ -105,6 +105,7 @@ KOKKOS_FUNCTION void TaskQueue<ExecSpace, MemorySpace>::decrement(
   task_root_type volatile &t = *task;
 
   const int count = Kokkos::atomic_fetch_add(&(t.m_ref_count), -1);
+  Kokkos::memory_fence();
 
 #if KOKKOS_IMPL_DEBUG_TASKDAG_SCHEDULING
   if (1 == count) {
@@ -146,8 +147,9 @@ KOKKOS_FUNCTION void *TaskQueue<ExecSpace, MemorySpace>::allocate(size_t n) {
   void *const p = m_memory.allocate(n);
 
   if (p) {
-    // Kokkos::atomic_increment( & m_accum_alloc );
-    Kokkos::atomic_increment(&m_count_alloc);
+    Kokkos::Impl::desul_atomic_inc(
+        &m_count_alloc, Kokkos::Impl::MemoryOrderSeqCst(),
+        Kokkos::Impl::MemoryScopeDevice());  // TODO? memory_order_relaxed
 
     // if ( m_max_alloc < m_count_alloc ) m_max_alloc = m_count_alloc ;
   }
@@ -159,7 +161,9 @@ template <typename ExecSpace, typename MemorySpace>
 KOKKOS_FUNCTION void TaskQueue<ExecSpace, MemorySpace>::deallocate(void *p,
                                                                    size_t n) {
   m_memory.deallocate(p, n);
-  Kokkos::atomic_decrement(&m_count_alloc);
+  Kokkos::Impl::desul_atomic_dec(
+      &m_count_alloc, Kokkos::Impl::MemoryOrderSeqCst(),
+      Kokkos::Impl::MemoryScopeDevice());  // TODO? memory_order_relaxed
 }
 
 //----------------------------------------------------------------------------
@@ -210,7 +214,9 @@ KOKKOS_FUNCTION bool TaskQueue<ExecSpace, MemorySpace>::push_task(
     //     *queue = task;
     //   }
     //   old_head = *queue;
-    old_head = Kokkos::atomic_compare_exchange(queue, old_head, task);
+    old_head = Kokkos::Impl::desul_atomic_compare_exchange(
+        const_cast<task_root_type **>(queue), old_head, task,
+        Kokkos::Impl::MemoryOrderSeqCst(), Kokkos::Impl::MemoryScopeDevice());
 
     if (old_head_tmp == old_head) return true;
   }
@@ -258,7 +264,10 @@ TaskQueue<ExecSpace, MemorySpace>::pop_ready_task(
 
     task_root_type *const x = task;
 
-    task = Kokkos::atomic_compare_exchange(queue, x, lock);
+    //    task = Kokkos::atomic_compare_exchange(queue, x, lock);
+    task = Kokkos::Impl::desul_atomic_compare_exchange(
+        const_cast<task_root_type **>(queue), x, lock,
+        Kokkos::Impl::MemoryOrderSeqCst(), Kokkos::Impl::MemoryScopeDevice());
 
     if (x == task) {
       // CAS succeeded and queue is locked
@@ -274,6 +283,8 @@ TaskQueue<ExecSpace, MemorySpace>::pop_ready_task(
       // This thread has exclusive access to
       // the queue and the popped task's m_next.
 
+      Kokkos::memory_fence();
+
       task_root_type *volatile &next = task->m_next;
 
       // This algorithm is not lockfree because a adversarial scheduler could
@@ -400,7 +411,9 @@ KOKKOS_FUNCTION void TaskQueue<ExecSpace, MemorySpace>::schedule_runnable(
     // to track number of ready + executing tasks.
     // The ready count will be decremented when the task is complete.
 
-    Kokkos::atomic_increment(&m_ready_count);
+    Kokkos::Impl::desul_atomic_inc(
+        &m_ready_count, Kokkos::Impl::MemoryOrderSeqCst(),
+        Kokkos::Impl::MemoryScopeDevice());  // TODO? memory_order_relaxed
 
     task_root_type *volatile *const ready_queue =
         &m_ready[t.m_priority][t.m_task_type];
@@ -553,8 +566,9 @@ KOKKOS_FUNCTION void TaskQueue<ExecSpace, MemorySpace>::reschedule(
 
   task_root_type *const zero = nullptr;
   task_root_type *const lock = (task_root_type *)task_root_type::LockTag;
-
-  if (lock != Kokkos::atomic_exchange(&task->m_next, zero)) {
+  if (lock != Kokkos::Impl::desul_atomic_exchange(
+                  &task->m_next, zero, Kokkos::Impl::MemoryOrderSeqCst(),
+                  Kokkos::Impl::MemoryScopeDevice())) {
     Kokkos::abort("TaskScheduler::respawn ERROR: already respawned");
   }
 }
@@ -601,8 +615,9 @@ KOKKOS_FUNCTION void TaskQueue<ExecSpace, MemorySpace>::complete(
 
     // Stop other tasks from adding themselves to this task's wait queue
     // by locking the head of this task's wait queue.
-
-    task_root_type *x = Kokkos::atomic_exchange(&t.m_wait, lock);
+    task_root_type *x = Kokkos::Impl::desul_atomic_exchange(
+        const_cast<task_root_type **>(&t.m_wait), lock,
+        Kokkos::Impl::MemoryOrderSeqCst(), Kokkos::Impl::MemoryScopeDevice());
 
     if (x != (task_root_type *)lock) {
       // This thread has transitioned this 'task' to complete.
@@ -645,7 +660,9 @@ KOKKOS_FUNCTION void TaskQueue<ExecSpace, MemorySpace>::complete(
     // A runnable task was popped from a ready queue and executed.
     // If respawned into a ready queue then the ready count was incremented
     // so decrement whether respawned or not.
-    Kokkos::atomic_decrement(&m_ready_count);
+    Kokkos::Impl::desul_atomic_dec(
+        &m_ready_count, Kokkos::Impl::MemoryOrderSeqCst(),
+        Kokkos::Impl::MemoryScopeDevice());  // TODO? memory_order_relaxed
   }
 }
 
diff --git a/packages/kokkos/core/src/impl/Kokkos_TaskTeamMember.hpp b/packages/kokkos/core/src/impl/Kokkos_TaskTeamMember.hpp
index 2faab5794907ecd43ccead5f74e09e414a8f3541..f53dfe5a96621a0e31d3deb95bb83ac9bef35907 100644
--- a/packages/kokkos/core/src/impl/Kokkos_TaskTeamMember.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_TaskTeamMember.hpp
@@ -55,7 +55,6 @@
 //----------------------------------------------------------------------------
 
 #include <Kokkos_MemoryPool.hpp>
-#include <impl/Kokkos_Tags.hpp>
 
 #include <Kokkos_Future.hpp>
 #include <impl/Kokkos_TaskQueue.hpp>
diff --git a/packages/kokkos/core/src/impl/Kokkos_Timer.hpp b/packages/kokkos/core/src/impl/Kokkos_Timer.hpp
index e8004ff85258975d3f36ee2a9345414e27ea09ad..6edf571d7892e4260fc7d617a1a86a63d265baa2 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Timer.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Timer.hpp
@@ -45,8 +45,13 @@
 #ifndef KOKKOS_IMPLWALLTIME_HPP
 #define KOKKOS_IMPLWALLTIME_HPP
 
+#include <Kokkos_Macros.hpp>
+
+KOKKOS_IMPL_WARNING("This file is deprecated. Use <Kokkos_Timer.hpp> instead.")
+
 #include <Kokkos_Timer.hpp>
 
+#ifdef KOKKOS_ENABLE_DEPRECATED_CODE_3
 namespace Kokkos {
 namespace Impl {
 
@@ -54,10 +59,11 @@ namespace Impl {
  *   Timer promoted from Impl to Kokkos ns
  *   This file included for backwards compatibility
  */
-
-using Kokkos::Timer;
+using Timer KOKKOS_DEPRECATED_WITH_COMMENT("Use Kokkos::Timer instead!") =
+    Kokkos::Timer;
 
 }  // namespace Impl
 }  // namespace Kokkos
+#endif
 
 #endif /* #ifndef KOKKOS_IMPLWALLTIME_HPP */
diff --git a/packages/kokkos/core/src/impl/Kokkos_Utilities.hpp b/packages/kokkos/core/src/impl/Kokkos_Utilities.hpp
index cb8cf281ae06fe0a71862b47428a2ffa12f4bd67..bea7c2c9d1e56a61802bc47c60f00e82496c8061 100644
--- a/packages/kokkos/core/src/impl/Kokkos_Utilities.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_Utilities.hpp
@@ -65,13 +65,6 @@ struct identity {
 template <typename T>
 using identity_t = typename identity<T>::type;
 
-struct not_a_type {
-  not_a_type()                  = delete;
-  ~not_a_type()                 = delete;
-  not_a_type(not_a_type const&) = delete;
-  void operator=(not_a_type const&) = delete;
-};
-
 #if defined(__cpp_lib_void_t)
 // since C++17
 using std::void_t;
@@ -158,6 +151,112 @@ struct destruct_delete {
 template <class...>
 struct type_list;
 
+//------------------------------------------------------------------------------
+// <editor-fold desc="type_list_remove_first"> {{{2
+
+// Currently linear complexity; if we use this a lot, maybe make it better?
+
+template <class Entry, class InList, class OutList>
+struct _type_list_remove_first_impl;
+
+template <class Entry, class T, class... Ts, class... OutTs>
+struct _type_list_remove_first_impl<Entry, type_list<T, Ts...>,
+                                    type_list<OutTs...>>
+    : _type_list_remove_first_impl<Entry, type_list<Ts...>,
+                                   type_list<OutTs..., T>> {};
+
+template <class Entry, class... Ts, class... OutTs>
+struct _type_list_remove_first_impl<Entry, type_list<Entry, Ts...>,
+                                    type_list<OutTs...>>
+    : _type_list_remove_first_impl<Entry, type_list<>,
+                                   type_list<OutTs..., Ts...>> {};
+
+template <class Entry, class... OutTs>
+struct _type_list_remove_first_impl<Entry, type_list<>, type_list<OutTs...>>
+    : identity<type_list<OutTs...>> {};
+
+template <class Entry, class List>
+struct type_list_remove_first
+    : _type_list_remove_first_impl<Entry, List, type_list<>> {};
+
+// </editor-fold> end type_list_remove_first }}}2
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// <editor-fold desc="type_list_any"> {{{2
+
+template <template <class> class UnaryPred, class List>
+struct type_list_any;
+
+#ifdef KOKKOS_ENABLE_CXX17
+template <template <class> class UnaryPred, class... Ts>
+struct type_list_any<UnaryPred, type_list<Ts...>>
+    : std::bool_constant<(UnaryPred<Ts>::value || ...)> {};
+#else
+template <template <class> class UnaryPred, class T, class... Ts>
+struct type_list_any<UnaryPred, type_list<T, Ts...>> {
+  using type = typename std::conditional_t<
+      UnaryPred<T>::value, std::true_type,
+      type_list_any<UnaryPred, type_list<Ts...>>>::type;
+  static constexpr auto value = type::value;
+};
+
+template <template <class> class UnaryPred>
+struct type_list_any<UnaryPred, type_list<>> : std::false_type {};
+
+#endif
+
+// </editor-fold> end type_list_any }}}2
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// <editor-fold desc="concat_type_list"> {{{2
+//  concat_type_list combines types in multiple type_lists
+
+// forward declaration
+template <typename... T>
+struct concat_type_list;
+
+// alias
+template <typename... T>
+using concat_type_list_t = typename concat_type_list<T...>::type;
+
+// final instantiation
+template <typename... T>
+struct concat_type_list<type_list<T...>> {
+  using type = type_list<T...>;
+};
+
+// combine consecutive type_lists
+template <typename... T, typename... U, typename... Tail>
+struct concat_type_list<type_list<T...>, type_list<U...>, Tail...>
+    : concat_type_list<type_list<T..., U...>, Tail...> {};
+// </editor-fold> end concat_type_list }}}2
+//------------------------------------------------------------------------------
+
+//------------------------------------------------------------------------------
+// <editor-fold desc="filter_type_list"> {{{2
+//  filter_type_list generates type-list of types which satisfy
+//  PredicateT<T>::value == ValueT
+
+template <template <typename> class PredicateT, typename TypeListT,
+          bool ValueT = true>
+struct filter_type_list;
+
+template <template <typename> class PredicateT, typename... T, bool ValueT>
+struct filter_type_list<PredicateT, type_list<T...>, ValueT> {
+  using type =
+      concat_type_list_t<std::conditional_t<PredicateT<T>::value == ValueT,
+                                            type_list<T>, type_list<>>...>;
+};
+
+template <template <typename> class PredicateT, typename T, bool ValueT = true>
+using filter_type_list_t =
+    typename filter_type_list<PredicateT, T, ValueT>::type;
+
+// </editor-fold> end filter_type_list }}}2
+//------------------------------------------------------------------------------
+
 // </editor-fold> end type_list }}}1
 //==============================================================================
 
diff --git a/packages/kokkos/core/src/impl/Kokkos_VLAEmulation.hpp b/packages/kokkos/core/src/impl/Kokkos_VLAEmulation.hpp
index 41607a2a8e7fedc56fd92fdfa9a472bda10bc547..ace826dd5a7f726cd9e0e2b3ce14b081a26680f2 100644
--- a/packages/kokkos/core/src/impl/Kokkos_VLAEmulation.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_VLAEmulation.hpp
@@ -130,20 +130,20 @@ struct ObjectWithVLAEmulation {
   // CRTP boilerplate
 
   KOKKOS_FORCEINLINE_FUNCTION
-  /* KOKKOS_CONSTEXPR_14 */
+  /* constexpr */
   Derived* _this() noexcept {
     return VLAEmulationAccess::_cast_to_derived(this);
   }
 
   KOKKOS_FORCEINLINE_FUNCTION
-  /* KOKKOS_CONSTEXPR_14 */
+  /* constexpr */
   Derived const* _this() const noexcept {
     return VLAEmulationAccess::_cast_to_derived(this);
   }
 
   // Note: can't be constexpr because of reinterpret_cast
   KOKKOS_FORCEINLINE_FUNCTION
-  /* KOKKOS_CONSTEXPR_14 */
+  /* constexpr */
   vla_value_type* _vla_pointer() noexcept {
     // The data starts right after the aligned storage of Derived
     return reinterpret_cast<vla_value_type*>(_this() + 1);
@@ -151,7 +151,7 @@ struct ObjectWithVLAEmulation {
 
   // Note: can't be constexpr because of reinterpret_cast
   KOKKOS_FORCEINLINE_FUNCTION
-  /* KOKKOS_CONSTEXPR_14 */
+  /* constexpr */
   vla_value_type const* _vla_pointer() const noexcept {
     // The data starts right after the aligned storage of Derived
     return reinterpret_cast<vla_value_type const*>(_this() + 1);
@@ -159,7 +159,7 @@ struct ObjectWithVLAEmulation {
 
  public:
   KOKKOS_INLINE_FUNCTION
-  static /* KOKKOS_CONSTEXPR_14 */ size_t required_allocation_size(
+  static /* constexpr */ size_t required_allocation_size(
       vla_entry_count_type num_vla_entries) {
     KOKKOS_EXPECTS(num_vla_entries >= 0);
     return sizeof(Derived) + num_vla_entries * sizeof(VLAValueType);
diff --git a/packages/kokkos/core/src/impl/Kokkos_ViewCtor.hpp b/packages/kokkos/core/src/impl/Kokkos_ViewCtor.hpp
index b9e32a04e09afcf1a5fcbeba0bd81257631f7714..797b3f584b6234b290a809e7f1c502e04249f058 100644
--- a/packages/kokkos/core/src/impl/Kokkos_ViewCtor.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_ViewCtor.hpp
@@ -144,10 +144,10 @@ struct ViewCtorProp<typename std::enable_if<is_view_label<Label>::value>::type,
 };
 
 template <typename Space>
-struct ViewCtorProp<typename std::enable_if<
-                        Kokkos::Impl::is_memory_space<Space>::value ||
-                        Kokkos::Impl::is_execution_space<Space>::value>::type,
-                    Space> {
+struct ViewCtorProp<
+    typename std::enable_if<Kokkos::is_memory_space<Space>::value ||
+                            Kokkos::is_execution_space<Space>::value>::type,
+    Space> {
   ViewCtorProp()                     = default;
   ViewCtorProp(const ViewCtorProp &) = default;
   ViewCtorProp &operator=(const ViewCtorProp &) = default;
@@ -207,10 +207,10 @@ template <typename... P>
 struct ViewCtorProp : public ViewCtorProp<void, P>... {
  private:
   using var_memory_space =
-      Kokkos::Impl::has_condition<void, Kokkos::Impl::is_memory_space, P...>;
+      Kokkos::Impl::has_condition<void, Kokkos::is_memory_space, P...>;
 
   using var_execution_space =
-      Kokkos::Impl::has_condition<void, Kokkos::Impl::is_execution_space, P...>;
+      Kokkos::Impl::has_condition<void, Kokkos::is_execution_space, P...>;
 
   struct VOIDDUMMY {};
 
@@ -270,7 +270,6 @@ struct ViewCtorProp : public ViewCtorProp<void, P>... {
 
 namespace Kokkos {
 
-/* For backward compatibility */
 namespace Impl {
 struct ViewAllocateWithoutInitializingBackwardCompat {};
 
@@ -291,7 +290,6 @@ struct ViewCtorProp<WithoutInitializing_t, std::string,
 };
 } /* namespace Impl */
 
-/*[[deprecated(Use Kokkos::alloc(Kokkos::WithoutInitializing, label) instead]]*/
 using ViewAllocateWithoutInitializing =
     Impl::ViewCtorProp<Impl::WithoutInitializing_t, std::string,
                        Impl::ViewAllocateWithoutInitializingBackwardCompat>;
diff --git a/packages/kokkos/core/src/impl/Kokkos_ViewMapping.hpp b/packages/kokkos/core/src/impl/Kokkos_ViewMapping.hpp
index a380a306931f4150e95b6f433c8bb076b091c456..9523118748f09907933764aea6ca94a085e68a9d 100644
--- a/packages/kokkos/core/src/impl/Kokkos_ViewMapping.hpp
+++ b/packages/kokkos/core/src/impl/Kokkos_ViewMapping.hpp
@@ -49,6 +49,7 @@
 #include <initializer_list>
 
 #include <Kokkos_Core_fwd.hpp>
+#include <Kokkos_DetectionIdiom.hpp>
 #include <Kokkos_Pair.hpp>
 #include <Kokkos_Layout.hpp>
 #include <Kokkos_Extents.hpp>
@@ -862,7 +863,7 @@ struct ViewDataAnalysis {
 namespace Kokkos {
 namespace Impl {
 
-template <class Dimension, class Layout, typename Enable = void>
+template <class Dimension, class Layout, class Enable = void>
 struct ViewOffset {
   using is_mapping_plugin = std::false_type;
 };
@@ -1389,7 +1390,8 @@ struct ViewOffset<
     KOKKOS_INLINE_FUNCTION
     static constexpr size_t stride(size_t const N) {
       return ((align != 0) &&
-              ((Kokkos::Impl::MEMORY_ALIGNMENT_THRESHOLD * align) < N) &&
+              ((static_cast<int>(Kokkos::Impl::MEMORY_ALIGNMENT_THRESHOLD) *
+                static_cast<int>(align)) < N) &&
               ((N % div_ok) != 0))
                  ? N + align - (N % div_ok)
                  : N;
@@ -2022,7 +2024,8 @@ struct ViewOffset<
     KOKKOS_INLINE_FUNCTION
     static constexpr size_t stride(size_t const N) {
       return ((align != 0) &&
-              ((Kokkos::Impl::MEMORY_ALIGNMENT_THRESHOLD * align) < N) &&
+              ((static_cast<int>(Kokkos::Impl::MEMORY_ALIGNMENT_THRESHOLD) *
+                static_cast<int>(align)) < N) &&
               ((N % div_ok) != 0))
                  ? N + align - (N % div_ok)
                  : N;
@@ -2816,6 +2819,22 @@ struct ViewDataHandle<
 namespace Kokkos {
 namespace Impl {
 
+template <typename T>
+inline bool is_zero_byte(const T& t) {
+  using comparison_type = std::conditional_t<
+      sizeof(T) % sizeof(long long int) == 0, long long int,
+      std::conditional_t<
+          sizeof(T) % sizeof(long int) == 0, long int,
+          std::conditional_t<
+              sizeof(T) % sizeof(int) == 0, int,
+              std::conditional_t<sizeof(T) % sizeof(short int) == 0, short int,
+                                 char>>>>;
+  const auto* const ptr = reinterpret_cast<const comparison_type*>(&t);
+  for (std::size_t i = 0; i < sizeof(T) / sizeof(comparison_type); ++i)
+    if (ptr[i] != 0) return false;
+  return true;
+}
+
 //----------------------------------------------------------------------------
 
 /*
@@ -2826,16 +2845,16 @@ namespace Impl {
  *  called from the shared memory tracking destruction.
  *  Secondarily to have two fewer partial specializations.
  */
-template <class ExecSpace, class ValueType,
+template <class DeviceType, class ValueType,
           bool IsScalar = std::is_scalar<ValueType>::value>
 struct ViewValueFunctor;
 
-template <class ExecSpace, class ValueType>
-struct ViewValueFunctor<ExecSpace, ValueType, false /* is_scalar */> {
+template <class DeviceType, class ValueType>
+struct ViewValueFunctor<DeviceType, ValueType, false /* is_scalar */> {
+  using ExecSpace  = typename DeviceType::execution_space;
   using PolicyType = Kokkos::RangePolicy<ExecSpace, Kokkos::IndexType<int64_t>>;
-  using Exec       = typename ExecSpace::execution_space;
 
-  Exec space;
+  ExecSpace space;
   ValueType* ptr;
   size_t n;
   bool destroy;
@@ -2864,11 +2883,50 @@ struct ViewValueFunctor<ExecSpace, ValueType, false /* is_scalar */> {
         destroy(false),
         name(std::move(arg_name)) {}
 
-  void execute(bool arg) {
+  template <typename Dummy = ValueType>
+  std::enable_if_t<std::is_trivial<Dummy>::value &&
+                   std::is_trivially_copy_assignable<ValueType>::value>
+  construct_dispatch() {
+    ValueType value{};
+    if (Impl::is_zero_byte(value)) {
+      uint64_t kpID = 0;
+      if (Kokkos::Profiling::profileLibraryLoaded()) {
+        // We are not really using parallel_for here but using beginParallelFor
+        // instead of begin_parallel_for (and adding "via memset") is the best
+        // we can do to indicate that this is not supposed to be tunable (and
+        // doesn't really execute a parallel_for).
+        Kokkos::Profiling::beginParallelFor(
+            "Kokkos::View::initialization [" + name + "] via memset",
+            Kokkos::Profiling::Experimental::device_id(space), &kpID);
+      }
+
+      (void)ZeroMemset<ExecSpace, ValueType*, typename DeviceType::memory_space,
+                       Kokkos::MemoryTraits<Kokkos::Unmanaged>>(
+          space,
+          Kokkos::View<ValueType*, typename DeviceType::memory_space,
+                       Kokkos::MemoryTraits<Kokkos::Unmanaged>>(ptr, n),
+          value);
+
+      if (Kokkos::Profiling::profileLibraryLoaded()) {
+        Kokkos::Profiling::endParallelFor(kpID);
+      }
+    } else {
+      parallel_for_implementation(false);
+    }
+  }
+
+  template <typename Dummy = ValueType>
+  std::enable_if_t<!(std::is_trivial<Dummy>::value &&
+                     std::is_trivially_copy_assignable<ValueType>::value)>
+  construct_dispatch() {
+    parallel_for_implementation(false);
+  }
+
+  void parallel_for_implementation(bool arg) {
     destroy = arg;
-    PolicyType policy(0, n);
-    std::string functor_name;
     if (!space.in_parallel()) {
+      PolicyType policy(0, n);
+      std::string functor_name;
       uint64_t kpID = 0;
       if (Kokkos::Profiling::profileLibraryLoaded()) {
         functor_name =
@@ -2877,6 +2935,7 @@ struct ViewValueFunctor<ExecSpace, ValueType, false /* is_scalar */> {
         Kokkos::Tools::Impl::begin_parallel_for(policy, *this, functor_name,
                                                 kpID);
       }
+
 #ifdef KOKKOS_ENABLE_CUDA
       if (std::is_same<ExecSpace, Kokkos::Cuda>::value) {
         Kokkos::Impl::cuda_prefetch_pointer(space, ptr, sizeof(ValueType) * n,
@@ -2886,7 +2945,7 @@ struct ViewValueFunctor<ExecSpace, ValueType, false /* is_scalar */> {
       const Kokkos::Impl::ParallelFor<ViewValueFunctor, PolicyType> closure(
           *this, policy);
       closure.execute();
-      space.fence();
+      space.fence("Kokkos::Impl::ViewValueFunctor: View init/destroy fence");
       if (Kokkos::Profiling::profileLibraryLoaded()) {
         Kokkos::Tools::Impl::end_parallel_for(policy, *this, functor_name,
                                               kpID);
@@ -2896,13 +2955,14 @@ struct ViewValueFunctor<ExecSpace, ValueType, false /* is_scalar */> {
     }
   }
 
-  void construct_shared_allocation() { execute(false); }
+  void construct_shared_allocation() { construct_dispatch(); }
 
-  void destroy_shared_allocation() { execute(true); }
+  void destroy_shared_allocation() { parallel_for_implementation(true); }
 };
 
-template <class ExecSpace, class ValueType>
-struct ViewValueFunctor<ExecSpace, ValueType, true /* is_scalar */> {
+template <class DeviceType, class ValueType>
+struct ViewValueFunctor<DeviceType, ValueType, true /* is_scalar */> {
+  using ExecSpace  = typename DeviceType::execution_space;
   using PolicyType = Kokkos::RangePolicy<ExecSpace, Kokkos::IndexType<int64_t>>;
 
   ExecSpace space;
@@ -2921,12 +2981,54 @@ struct ViewValueFunctor<ExecSpace, ValueType, true /* is_scalar */> {
                    size_t const arg_n, std::string arg_name)
       : space(arg_space), ptr(arg_ptr), n(arg_n), name(std::move(arg_name)) {}
 
-  void construct_shared_allocation() {
-    if (!space.in_parallel()) {
+  template <typename Dummy = ValueType>
+  std::enable_if_t<std::is_trivial<Dummy>::value &&
+                   std::is_trivially_copy_assignable<Dummy>::value>
+  construct_shared_allocation() {
+    // Shortcut for zero initialization
+    ValueType value{};
+    if (Impl::is_zero_byte(value)) {
       uint64_t kpID = 0;
       if (Kokkos::Profiling::profileLibraryLoaded()) {
+        // We are not really using parallel_for here but using beginParallelFor
+        // instead of begin_parallel_for (and adding "via memset") is the best
+        // we can do to indicate that this is not supposed to be tunable (and
+        // doesn't really execute a parallel_for).
         Kokkos::Profiling::beginParallelFor(
-            "Kokkos::View::initialization [" + name + "]", 0, &kpID);
+            "Kokkos::View::initialization [" + name + "] via memset",
+            Kokkos::Profiling::Experimental::device_id(space), &kpID);
+      }
+
+      (void)ZeroMemset<ExecSpace, ValueType*, typename DeviceType::memory_space,
+                       Kokkos::MemoryTraits<Kokkos::Unmanaged>>(
+          space,
+          Kokkos::View<ValueType*, typename DeviceType::memory_space,
+                       Kokkos::MemoryTraits<Kokkos::Unmanaged>>(ptr, n),
+          value);
+
+      if (Kokkos::Profiling::profileLibraryLoaded()) {
+        Kokkos::Profiling::endParallelFor(kpID);
+      }
+    } else {
+      parallel_for_implementation();
+    }
+  }
+
+  template <typename Dummy = ValueType>
+  std::enable_if_t<!(std::is_trivial<Dummy>::value &&
+                     std::is_trivially_copy_assignable<Dummy>::value)>
+  construct_shared_allocation() {
+    parallel_for_implementation();
+  }
+
+  void parallel_for_implementation() {
+    if (!space.in_parallel()) {
+      PolicyType policy(0, n);
+      std::string functor_name = "Kokkos::View::initialization [" + name + "]";
+      uint64_t kpID            = 0;
+      if (Kokkos::Profiling::profileLibraryLoaded()) {
+        Kokkos::Tools::Impl::begin_parallel_for(policy, *this, functor_name,
+                                                kpID);
       }
 #ifdef KOKKOS_ENABLE_CUDA
       if (std::is_same<ExecSpace, Kokkos::Cuda>::value) {
@@ -2937,9 +3039,11 @@ struct ViewValueFunctor<ExecSpace, ValueType, true /* is_scalar */> {
       const Kokkos::Impl::ParallelFor<ViewValueFunctor, PolicyType> closure(
           *this, PolicyType(0, n));
       closure.execute();
-      space.fence();
+      space.fence(
+          "Kokkos::Impl::ViewValueFunctor: Fence after setting values in view");
       if (Kokkos::Profiling::profileLibraryLoaded()) {
-        Kokkos::Profiling::endParallelFor(kpID);
+        Kokkos::Tools::Impl::end_parallel_for(policy, *this, functor_name,
+                                              kpID);
       }
     } else {
       for (size_t i = 0; i < n; ++i) operator()(i);
@@ -3232,7 +3336,9 @@ class ViewMapping<
     using execution_space = typename alloc_prop::execution_space;
     using memory_space    = typename Traits::memory_space;
     using value_type      = typename Traits::value_type;
-    using functor_type    = ViewValueFunctor<execution_space, value_type>;
+    using functor_type =
+        ViewValueFunctor<Kokkos::Device<execution_space, memory_space>,
+                         value_type>;
     using record_type =
         Kokkos::Impl::SharedAllocationRecord<memory_space, functor_type>;
 
@@ -3314,17 +3420,10 @@ class ViewMapping<
                            Kokkos::LayoutStride>::value))))>::type> {
  private:
   enum {
-    is_assignable_space =
-#if 1
-        Kokkos::Impl::MemorySpaceAccess<
-            typename DstTraits::memory_space,
-            typename SrcTraits::memory_space>::assignable
-  };
-#else
-        std::is_same<typename DstTraits::memory_space,
-                     typename SrcTraits::memory_space>::value
+    is_assignable_space = Kokkos::Impl::MemorySpaceAccess<
+        typename DstTraits::memory_space,
+        typename SrcTraits::memory_space>::assignable
   };
-#endif
 
   enum {
     is_assignable_value_type =
@@ -3728,7 +3827,7 @@ class ViewMapping<
 
   template <class MemoryTraits>
   struct apply {
-    static_assert(Kokkos::Impl::is_memory_traits<MemoryTraits>::value, "");
+    static_assert(Kokkos::is_memory_traits<MemoryTraits>::value, "");
 
     using traits_type =
         Kokkos::ViewTraits<data_type, array_layout,
@@ -3842,24 +3941,21 @@ struct OperatorBoundsErrorOnDevice<MapType, true> {
    this defined by default.
    The existence of this alias indicates the existence of MapType::is_managed
  */
-template <class T, class Enable = void>
-struct has_printable_label_typedef : public std::false_type {};
-
 template <class T>
-struct has_printable_label_typedef<T,
-                                   void_t<typename T::printable_label_typedef>>
-    : public std::true_type {};
+using printable_label_typedef_t = typename T::printable_label_typedef;
 
-template <class MapType>
-KOKKOS_INLINE_FUNCTION void operator_bounds_error_on_device(MapType const&,
-                                                            std::false_type) {
+template <class Map>
+KOKKOS_FUNCTION
+    std::enable_if_t<!is_detected<printable_label_typedef_t, Map>::value>
+    operator_bounds_error_on_device(Map const&) {
   Kokkos::abort("View bounds error");
 }
 
-template <class MapType>
-KOKKOS_INLINE_FUNCTION void operator_bounds_error_on_device(MapType const& map,
-                                                            std::true_type) {
-  OperatorBoundsErrorOnDevice<MapType>::run(map);
+template <class Map>
+KOKKOS_FUNCTION
+    std::enable_if_t<is_detected<printable_label_typedef_t, Map>::value>
+    operator_bounds_error_on_device(Map const& map) {
+  OperatorBoundsErrorOnDevice<Map>::run(map);
 }
 
 #endif  // ! defined( KOKKOS_ACTIVE_EXECUTION_MEMORY_SPACE_HOST )
@@ -3885,8 +3981,7 @@ KOKKOS_INLINE_FUNCTION void view_verify_operator_bounds(
        This check should cover the case of Views that don't
        have the Unmanaged trait but were initialized by pointer. */
     if (tracker.m_tracker.has_record()) {
-      operator_bounds_error_on_device<MapType>(
-          map, has_printable_label_typedef<MapType>());
+      operator_bounds_error_on_device(map);
     } else {
       Kokkos::abort("View bounds error");
     }
diff --git a/packages/kokkos/core/src/setup/Kokkos_Setup_SYCL.hpp b/packages/kokkos/core/src/setup/Kokkos_Setup_SYCL.hpp
index a5f5406746befc984f17f815e04bac63f0fadff4..d964baa8fb0f5e1b105d244740b74e32d1bdd69e 100644
--- a/packages/kokkos/core/src/setup/Kokkos_Setup_SYCL.hpp
+++ b/packages/kokkos/core/src/setup/Kokkos_Setup_SYCL.hpp
@@ -62,10 +62,10 @@ void sink(Args&&... args) {
     Kokkos::ImplSYCL::sink(__VA_ARGS__);   \
   } while (0)
 #else
-#define KOKKOS_IMPL_DO_NOT_USE_PRINTF(format, ...)                       \
-  do {                                                                   \
-    static const __attribute__((opencl_constant)) char fmt[] = (format); \
-    sycl::ONEAPI::experimental::printf(fmt, ##__VA_ARGS__);              \
+#define KOKKOS_IMPL_DO_NOT_USE_PRINTF(format, ...)                \
+  do {                                                            \
+    const __attribute__((opencl_constant)) char fmt[] = (format); \
+    sycl::ONEAPI::experimental::printf(fmt, ##__VA_ARGS__);       \
   } while (0)
 #endif
 #endif
diff --git a/packages/kokkos/core/src/traits/Kokkos_ExecutionSpaceTrait.hpp b/packages/kokkos/core/src/traits/Kokkos_ExecutionSpaceTrait.hpp
index 4467b2e03c486d07d80c3fee66e6c3b50c42256e..e12d1f6a49d37f4f595214be00713c2ccb4166ef 100644
--- a/packages/kokkos/core/src/traits/Kokkos_ExecutionSpaceTrait.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_ExecutionSpaceTrait.hpp
@@ -56,6 +56,11 @@ namespace Impl {
 //==============================================================================
 // <editor-fold desc="trait specification"> {{{1
 
+template <class T>
+struct show_extra_execution_space_erroneously_given_to_execution_policy;
+template <>
+struct show_extra_execution_space_erroneously_given_to_execution_policy<void> {
+};
 struct ExecutionSpaceTrait : TraitSpecificationBase<ExecutionSpaceTrait> {
   struct base_traits {
     static constexpr auto execution_space_is_defaulted = true;
@@ -63,32 +68,30 @@ struct ExecutionSpaceTrait : TraitSpecificationBase<ExecutionSpaceTrait> {
     using execution_space = Kokkos::DefaultExecutionSpace;
   };
   template <class T>
-  using trait_matches_specification = is_execution_space<T>;
-};
-
-// </editor-fold> end trait specification }}}1
-//==============================================================================
+  using trait_matches_specification = Kokkos::is_execution_space<T>;
+  template <class ExecSpace, class AnalyzeNextTrait>
+  struct mixin_matching_trait : AnalyzeNextTrait {
+    using base_t = AnalyzeNextTrait;
+    using base_t::base_t;
 
-//==============================================================================
-// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
+    static constexpr auto show_execution_space_error_in_compilation_message =
+        show_extra_execution_space_erroneously_given_to_execution_policy<
+            std::conditional_t<base_t::execution_space_is_defaulted, void,
+                               typename base_t::execution_space>>{};
+    static_assert(base_t::execution_space_is_defaulted,
+                  "Kokkos Error: More than one execution space given. Search "
+                  "compiler output for 'show_extra_execution_space' to see the "
+                  "type of the errant tag.");
 
-template <class ExecutionSpace, class... Traits>
-struct AnalyzeExecPolicy<
-    std::enable_if_t<Kokkos::is_execution_space<ExecutionSpace>::value>,
-    ExecutionSpace, Traits...> : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
-  using base_t::base_t;
+    static constexpr auto execution_space_is_defaulted = false;
 
-  static_assert(base_t::execution_space_is_defaulted,
-                "Kokkos Error: More than one execution space given");
-
-  static constexpr bool execution_space_is_defaulted = false;
-
-  using execution_space = ExecutionSpace;
+    using execution_space = ExecSpace;
+  };
 };
 
-// </editor-fold> end AnalyzeExecPolicy specializations }}}1
+// </editor-fold> end trait specification }}}1
 //==============================================================================
+
 }  // end namespace Impl
 }  // end namespace Kokkos
 
diff --git a/packages/kokkos/core/src/traits/Kokkos_GraphKernelTrait.hpp b/packages/kokkos/core/src/traits/Kokkos_GraphKernelTrait.hpp
index eb649dc0887a2aab8c88feae8156676b70a7cdf7..b57dfbbc07ccc0e2391b2fdb5b6ec577ed552cc2 100644
--- a/packages/kokkos/core/src/traits/Kokkos_GraphKernelTrait.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_GraphKernelTrait.hpp
@@ -61,6 +61,12 @@ struct GraphKernelTrait : TraitSpecificationBase<GraphKernelTrait> {
   struct base_traits {
     using is_graph_kernel = std::false_type;
   };
+  template <class, class AnalyzeNextTrait>
+  struct mixin_matching_trait : AnalyzeNextTrait {
+    using base_t = AnalyzeNextTrait;
+    using base_t::base_t;
+    using is_graph_kernel = std::true_type;
+  };
   template <class T>
   using trait_matches_specification = std::is_same<T, IsGraphKernelTag>;
 };
@@ -68,19 +74,6 @@ struct GraphKernelTrait : TraitSpecificationBase<GraphKernelTrait> {
 // </editor-fold> end trait specification }}}1
 //==============================================================================
 
-//==============================================================================
-// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
-
-template <class... Traits>
-struct AnalyzeExecPolicy<void, Impl::IsGraphKernelTag, Traits...>
-    : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
-  using base_t::base_t;
-  using is_graph_kernel = std::true_type;
-};
-
-// </editor-fold> end AnalyzeExecPolicy specializations }}}1
-//==============================================================================
 }  // end namespace Impl
 }  // end namespace Kokkos
 
diff --git a/packages/kokkos/core/src/traits/Kokkos_IndexTypeTrait.hpp b/packages/kokkos/core/src/traits/Kokkos_IndexTypeTrait.hpp
index e15adc17116cb66481f90acc0b9ba5a83ec1ab52..63446375fbd529e82d71acf2bac5ef12fba238af 100644
--- a/packages/kokkos/core/src/traits/Kokkos_IndexTypeTrait.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_IndexTypeTrait.hpp
@@ -46,54 +46,71 @@
 #define KOKKOS_KOKKOS_INDEXTYPETRAIT_HPP
 
 #include <Kokkos_Macros.hpp>
-#include <Kokkos_Concepts.hpp>  // IndexType, is_index_type
+#include <Kokkos_Concepts.hpp>  // IndexType
 #include <traits/Kokkos_PolicyTraitAdaptor.hpp>
 #include <traits/Kokkos_Traits_fwd.hpp>
 
 namespace Kokkos {
 namespace Impl {
 
+template <class Trait, class AnalyzeNextTrait>
+struct IndexTypePolicyMixin;
+
 //==============================================================================
 // <editor-fold desc="trait specification"> {{{1
 
+template <class T>
+struct show_extra_index_type_erroneously_given_to_execution_policy;
+template <>
+struct show_extra_index_type_erroneously_given_to_execution_policy<void> {};
 struct IndexTypeTrait : TraitSpecificationBase<IndexTypeTrait> {
   struct base_traits {
     static constexpr bool index_type_is_defaulted = true;
     using index_type = dependent_policy_trait_default;
   };
-  template <class T>
-  using trait_matches_specification =
-      std::integral_constant<bool, std::is_integral<T>::value ||
-                                       is_index_type<T>::value>;
+  template <class IdxType, class AnalyzeNextTrait>
+  using mixin_matching_trait = IndexTypePolicyMixin<IdxType, AnalyzeNextTrait>;
 };
 
 // </editor-fold> end trait specification }}}1
 //==============================================================================
 
 //==============================================================================
-// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
+// <editor-fold desc="IndexTypePolicyMixin specializations"> {{{1
 
 // Index type given as IndexType template
-template <class IntegralIndexType, class... Traits>
-struct AnalyzeExecPolicy<void, Kokkos::IndexType<IntegralIndexType>, Traits...>
-    : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
+template <class IntegralIndexType, class AnalyzeNextTrait>
+struct IndexTypePolicyMixin<Kokkos::IndexType<IntegralIndexType>,
+                            AnalyzeNextTrait> : AnalyzeNextTrait {
+  using base_t = AnalyzeNextTrait;
   using base_t::base_t;
+  static constexpr auto show_index_type_error_in_compilation_message =
+      show_extra_index_type_erroneously_given_to_execution_policy<
+          std::conditional_t<base_t::index_type_is_defaulted, void,
+                             typename base_t::schedule_type>>{};
   static_assert(base_t::index_type_is_defaulted,
-                "Kokkos Error: More than one index type given");
+                "Kokkos Error: More than one index type given. Search "
+                "compiler output for 'show_extra_index_type' to see the "
+                "type of the errant tag.");
   static constexpr bool index_type_is_defaulted = false;
   using index_type = Kokkos::IndexType<IntegralIndexType>;
 };
 
-// IndexType given as an integral type directly
-template <class IntegralIndexType, class... Traits>
-struct AnalyzeExecPolicy<
-    std::enable_if_t<std::is_integral<IntegralIndexType>::value>,
-    IntegralIndexType, Traits...> : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
+// IndexType given as an integral type directly (the matcher already checks
+// this, so we don't have specialize to re-check it here)
+template <class IntegralIndexType, class AnalyzeNextTrait>
+struct IndexTypePolicyMixin : AnalyzeNextTrait {
+  using base_t = AnalyzeNextTrait;
   using base_t::base_t;
+  static constexpr auto show_index_type_error_in_compilation_message =
+      show_extra_index_type_erroneously_given_to_execution_policy<
+          std::conditional_t<base_t::index_type_is_defaulted, void,
+                             typename base_t::schedule_type>>{};
   static_assert(base_t::index_type_is_defaulted,
-                "Kokkos Error: More than one index type given");
+                "Kokkos Error: More than one index type given. Search "
+                "compiler output for 'show_extra_index_type' to see the "
+                "type of the errant tag.");
+  static_assert(std::is_integral<IntegralIndexType>::value, "");
   static constexpr bool index_type_is_defaulted = false;
   using index_type = Kokkos::IndexType<IntegralIndexType>;
 };
@@ -101,6 +118,22 @@ struct AnalyzeExecPolicy<
 // </editor-fold> end AnalyzeExecPolicy specializations }}}1
 //==============================================================================
 
+//==============================================================================
+// <editor-fold desc="PolicyTraitMatcher specialization"> {{{1
+
+template <class IntegralIndexType>
+struct PolicyTraitMatcher<IndexTypeTrait, IndexType<IntegralIndexType>>
+    : std::true_type {};
+
+template <class IntegralIndexType>
+struct PolicyTraitMatcher<
+    IndexTypeTrait, IntegralIndexType,
+    std::enable_if_t<std::is_integral<IntegralIndexType>::value>>
+    : std::true_type {};
+
+// </editor-fold> end PolicyTraitMatcher specialization"> }}}1
+//==============================================================================
+
 }  // end namespace Impl
 }  // end namespace Kokkos
 
diff --git a/packages/kokkos/core/src/traits/Kokkos_IterationPatternTrait.hpp b/packages/kokkos/core/src/traits/Kokkos_IterationPatternTrait.hpp
index 30e07039a405d61f2c78217284f9036a0a533f06..b05f3b29e976c503c120c4f59dc4ed81b01822f7 100644
--- a/packages/kokkos/core/src/traits/Kokkos_IterationPatternTrait.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_IterationPatternTrait.hpp
@@ -45,8 +45,11 @@
 #ifndef KOKKOS_KOKKOS_ITERATIONPATTERNTRAIT_HPP
 #define KOKKOS_KOKKOS_ITERATIONPATTERNTRAIT_HPP
 
-#include <Kokkos_Concepts.hpp>  // is_iteration_pattern
-#include <type_traits>          // is_void
+#include <Kokkos_Concepts.hpp>                   // is_iteration_pattern
+#include <traits/Kokkos_PolicyTraitAdaptor.hpp>  // TraitSpecificationBase
+#include <Kokkos_Rank.hpp>                       // Rank
+#include <Kokkos_Layout.hpp>                     // Iterate
+#include <type_traits>                           // is_void
 
 namespace Kokkos {
 namespace Impl {
@@ -54,32 +57,42 @@ namespace Impl {
 //==============================================================================
 // <editor-fold desc="trait specification"> {{{1
 
+template <class T>
+struct show_extra_iteration_pattern_erroneously_given_to_execution_policy;
+template <>
+struct show_extra_iteration_pattern_erroneously_given_to_execution_policy<
+    void> {};
 struct IterationPatternTrait : TraitSpecificationBase<IterationPatternTrait> {
   struct base_traits {
     using iteration_pattern = void;  // TODO set default iteration pattern
   };
-  template <class T>
-  using trait_matches_specification = is_iteration_pattern<T>;
+  template <class IterPattern, class AnalyzeNextTrait>
+  struct mixin_matching_trait : AnalyzeNextTrait {
+    using base_t = AnalyzeNextTrait;
+    using base_t::base_t;
+    static constexpr auto show_iteration_pattern_error_in_compilation_message =
+        show_extra_iteration_pattern_erroneously_given_to_execution_policy<
+            typename base_t::iteration_pattern>{};
+    static_assert(
+        std::is_void<typename base_t::iteration_pattern>::value,
+        "Kokkos Error: More than one index type given. Search "
+        "compiler output for 'show_extra_iteration_pattern' to see the "
+        "type of the errant tag.");
+    using iteration_pattern = IterPattern;
+  };
 };
 
 // </editor-fold> end trait specification }}}1
 //==============================================================================
 
 //==============================================================================
-// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
+// <editor-fold desc="PolicyTraitMatcher specialization"> {{{1
 
-template <class IterationPattern, class... Traits>
-struct AnalyzeExecPolicy<
-    std::enable_if_t<is_iteration_pattern<IterationPattern>::value>,
-    IterationPattern, Traits...> : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
-  using base_t::base_t;
-  static_assert(std::is_void<typename base_t::iteration_pattern>::value,
-                "Kokkos Error: More than one iteration pattern given");
-  using iteration_pattern = IterationPattern;
-};
+template <unsigned N, Iterate OuterDir, Iterate InnerDir>
+struct PolicyTraitMatcher<IterationPatternTrait, Rank<N, OuterDir, InnerDir>>
+    : std::true_type {};
 
-// </editor-fold> end AnalyzeExecPolicy specializations }}}1
+// </editor-fold> end  }}}1
 //==============================================================================
 
 }  // end namespace Impl
diff --git a/packages/kokkos/core/src/traits/Kokkos_LaunchBoundsTrait.hpp b/packages/kokkos/core/src/traits/Kokkos_LaunchBoundsTrait.hpp
index 73ae8e27e2eca54412b4cbab464b1760c93d7aed..06836bef8bff6ffc19d470766e0caa0a739a43c2 100644
--- a/packages/kokkos/core/src/traits/Kokkos_LaunchBoundsTrait.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_LaunchBoundsTrait.hpp
@@ -62,29 +62,33 @@ struct LaunchBoundsTrait : TraitSpecificationBase<LaunchBoundsTrait> {
 
     using launch_bounds = LaunchBounds<>;
   };
-  template <class T>
-  using trait_matches_specification = is_launch_bounds<T>;
+  template <class LaunchBoundParam, class AnalyzeNextTrait>
+  struct mixin_matching_trait : AnalyzeNextTrait {
+    using base_t = AnalyzeNextTrait;
+    using base_t::base_t;
+
+    static constexpr bool launch_bounds_is_defaulted = false;
+
+    static_assert(base_t::launch_bounds_is_defaulted,
+                  "Kokkos Error: More than one launch_bounds given");
+
+    using launch_bounds = LaunchBoundParam;
+  };
 };
 
 // </editor-fold> end trait specification }}}1
 //==============================================================================
 
 //==============================================================================
-// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
+// <editor-fold desc="PolicyTraitMatcher specialization"> {{{1
 
-template <unsigned int MaxT, unsigned int MinB, class... Traits>
-struct AnalyzeExecPolicy<void, Kokkos::LaunchBounds<MaxT, MinB>, Traits...>
-    : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
-  using base_t::base_t;
-  static_assert(base_t::launch_bounds_is_defaulted,
-                "Kokkos Error: More than one launch_bounds given");
-  static constexpr bool launch_bounds_is_defaulted = false;
-  using launch_bounds = Kokkos::LaunchBounds<MaxT, MinB>;
-};
+template <unsigned int maxT, unsigned int minB>
+struct PolicyTraitMatcher<LaunchBoundsTrait, LaunchBounds<maxT, minB>>
+    : std::true_type {};
 
-// </editor-fold> end AnalyzeExecPolicy specializations }}}1
+// </editor-fold> end PolicyTraitMatcher specialization }}}1
 //==============================================================================
+
 }  // end namespace Impl
 }  // end namespace Kokkos
 
diff --git a/packages/kokkos/core/src/traits/Kokkos_OccupancyControlTrait.hpp b/packages/kokkos/core/src/traits/Kokkos_OccupancyControlTrait.hpp
index 3deb4a94d54ddeee0a6a0712f107d61674818668..73be14cf8501b3c3bff4a2386e2f75e1ffb00f19 100644
--- a/packages/kokkos/core/src/traits/Kokkos_OccupancyControlTrait.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_OccupancyControlTrait.hpp
@@ -82,6 +82,9 @@ struct MaximizeOccupancy {
 
 namespace Impl {
 
+template <class Policy, class AnalyzeNextTrait>
+struct OccupancyControlPolicyMixin;
+
 //==============================================================================
 // <editor-fold desc="Occupancy control trait specification"> {{{1
 
@@ -94,6 +97,9 @@ struct OccupancyControlTrait : TraitSpecificationBase<OccupancyControlTrait> {
       return occupancy_control{};
     }
   };
+  template <class OccControl, class AnalyzeNextTrait>
+  using mixin_matching_trait =
+      OccupancyControlPolicyMixin<OccControl, AnalyzeNextTrait>;
   template <class T>
   using trait_matches_specification = std::integral_constant<
       bool,
@@ -105,39 +111,33 @@ struct OccupancyControlTrait : TraitSpecificationBase<OccupancyControlTrait> {
 //==============================================================================
 
 //==============================================================================
-// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
-
-// The DesiredOccupancy case has runtime storage, so we need to handle copies
-// and assignments
-template <class... Traits>
-struct AnalyzeExecPolicy<void, Kokkos::Experimental::DesiredOccupancy,
-                         Traits...> : AnalyzeExecPolicy<void, Traits...> {
- public:
-  using base_t            = AnalyzeExecPolicy<void, Traits...>;
+// <editor-fold desc="OccupancyControlPolicyMixin specializations"> {{{1
+
+template <class AnalyzeNextTrait>
+struct OccupancyControlPolicyMixin<Kokkos::Experimental::DesiredOccupancy,
+                                   AnalyzeNextTrait> : AnalyzeNextTrait {
+  using base_t            = AnalyzeNextTrait;
   using occupancy_control = Kokkos::Experimental::DesiredOccupancy;
   static constexpr bool experimental_contains_desired_occupancy = true;
 
-  template <class OccControl>
-  using with_occupancy_control = AnalyzeExecPolicy<void, OccControl, Traits...>;
-
   // Treat this as private, but make it public so that MSVC will still treat
   // this as a standard layout class and make it the right size: storage for a
   // stateful desired occupancy
   //   private:
-  occupancy_control m_desired_occupancy;
+  occupancy_control m_desired_occupancy = occupancy_control{};
 
-  AnalyzeExecPolicy() = default;
+  OccupancyControlPolicyMixin() = default;
   // Converting constructor
   // Just rely on the convertibility of occupancy_control to transfer the data
   template <class Other>
-  AnalyzeExecPolicy(ExecPolicyTraitsWithDefaults<Other> const& other)
+  OccupancyControlPolicyMixin(ExecPolicyTraitsWithDefaults<Other> const& other)
       : base_t(other),
         m_desired_occupancy(other.impl_get_occupancy_control()) {}
 
   // Converting assignment operator
   // Just rely on the convertibility of occupancy_control to transfer the data
   template <class Other>
-  AnalyzeExecPolicy& operator=(
+  OccupancyControlPolicyMixin& operator=(
       ExecPolicyTraitsWithDefaults<Other> const& other) {
     *static_cast<base_t*>(this) = other;
     this->impl_set_desired_occupancy(
@@ -160,16 +160,16 @@ struct AnalyzeExecPolicy<void, Kokkos::Experimental::DesiredOccupancy,
   }
 };
 
-template <class... Traits>
-struct AnalyzeExecPolicy<void, Kokkos::Experimental::MaximizeOccupancy,
-                         Traits...> : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
+template <class AnalyzeNextTrait>
+struct OccupancyControlPolicyMixin<Kokkos::Experimental::MaximizeOccupancy,
+                                   AnalyzeNextTrait> : AnalyzeNextTrait {
+  using base_t = AnalyzeNextTrait;
   using base_t::base_t;
   using occupancy_control = Kokkos::Experimental::MaximizeOccupancy;
   static constexpr bool experimental_contains_desired_occupancy = false;
 };
 
-// </editor-fold> end AnalyzeExecPolicy specializations }}}1
+// </editor-fold> end OccupancyControlPolicyMixin specializations }}}1
 //==============================================================================
 
 }  // end namespace Impl
diff --git a/packages/kokkos/core/src/traits/Kokkos_PolicyTraitAdaptor.hpp b/packages/kokkos/core/src/traits/Kokkos_PolicyTraitAdaptor.hpp
index b087dac85559bd6dc67c983bdaad1a6675cfde9b..e500dd4e831abaa03479e9fb1e2fb67595107a9e 100644
--- a/packages/kokkos/core/src/traits/Kokkos_PolicyTraitAdaptor.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_PolicyTraitAdaptor.hpp
@@ -73,7 +73,7 @@ namespace Impl {
 // something that we can default to in the unspecialized case, just like we
 // do for AnalyzeExecPolicy
 template <class TraitSpec, class Trait, class Enable = void>
-struct PolicyTraitMatcher;
+struct PolicyTraitMatcher : std::false_type {};
 
 template <class TraitSpec, class Trait>
 struct PolicyTraitMatcher<
diff --git a/packages/kokkos/core/src/traits/Kokkos_PolicyTraitMatcher.hpp b/packages/kokkos/core/src/traits/Kokkos_PolicyTraitMatcher.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..31927320bf6fe77cc133e49a80e7f741574165a8
--- /dev/null
+++ b/packages/kokkos/core/src/traits/Kokkos_PolicyTraitMatcher.hpp
@@ -0,0 +1,77 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#include <impl/Kokkos_Utilities.hpp>  // type_list
+
+#include <traits/Kokkos_Traits_fwd.hpp>
+
+#ifndef KOKKOS_KOKKOS_POLICYTRAITMATCHER_HPP
+#define KOKKOS_KOKKOS_POLICYTRAITMATCHER_HPP
+
+namespace Kokkos {
+namespace Impl {
+
+//==============================================================================
+// <editor-fold desc="PolicyTraitMatcher"> {{{1
+
+// To handle the WorkTag case, we need more than just a predicate; we need
+// something that we can default to in the unspecialized case, just like we
+// do for AnalyzeExecPolicy
+template <class TraitSpec, class Trait, class Enable = void>
+struct PolicyTraitMatcher : std::false_type {};
+
+template <class TraitSpec, class Trait>
+struct PolicyTraitMatcher<
+    TraitSpec, Trait,
+    std::enable_if_t<
+        TraitSpec::template trait_matches_specification<Trait>::value>>
+    : std::true_type {};
+
+// </editor-fold> end PolicyTraitMatcher }}}1
+//==============================================================================
+
+}  // end namespace Impl
+}  // end namespace Kokkos
+
+#endif  // KOKKOS_KOKKOS_POLICYTRAITMATCHER_HPP
diff --git a/packages/kokkos/core/src/traits/Kokkos_ScheduleTrait.hpp b/packages/kokkos/core/src/traits/Kokkos_ScheduleTrait.hpp
index 74bab6fce2a632269a804971af3e50348e34c8b2..3e578f9060ab22b8707adc8797d401226a52ff44 100644
--- a/packages/kokkos/core/src/traits/Kokkos_ScheduleTrait.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_ScheduleTrait.hpp
@@ -57,34 +57,43 @@ namespace Impl {
 //==============================================================================
 // <editor-fold desc="trait specification"> {{{1
 
+template <class T>
+struct show_extra_schedule_type_erroneously_given_to_execution_policy;
+template <>
+struct show_extra_schedule_type_erroneously_given_to_execution_policy<void> {};
 struct ScheduleTrait : TraitSpecificationBase<ScheduleTrait> {
   struct base_traits {
     static constexpr auto schedule_type_is_defaulted = true;
 
     using schedule_type = Schedule<Static>;
   };
-  template <class T>
-  using trait_matches_specification = is_schedule_type<T>;
+  template <class Sched, class AnalyzeNextTrait>
+  struct mixin_matching_trait : AnalyzeNextTrait {
+    using base_t = AnalyzeNextTrait;
+    using base_t::base_t;
+    using schedule_type = Sched;
+    static constexpr auto show_schedule_type_error_in_compilation_message =
+        show_extra_schedule_type_erroneously_given_to_execution_policy<
+            std::conditional_t<base_t::schedule_type_is_defaulted, void,
+                               typename base_t::schedule_type>>{};
+    static_assert(base_t::schedule_type_is_defaulted,
+                  "Kokkos Error: More than one schedule type given. Search "
+                  "compiler output for 'show_extra_schedule_type' to see the "
+                  "type of the errant tag.");
+    static constexpr bool schedule_type_is_defaulted = false;
+  };
 };
 
 // </editor-fold> end trait specification }}}1
 //==============================================================================
 
 //==============================================================================
-// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
-
-template <class ScheduleType, class... Traits>
-struct AnalyzeExecPolicy<void, Kokkos::Schedule<ScheduleType>, Traits...>
-    : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
-  using base_t::base_t;
-  static_assert(base_t::schedule_type_is_defaulted,
-                "Kokkos Error: More than one schedule type given");
-  static constexpr bool schedule_type_is_defaulted = false;
-  using schedule_type = Kokkos::Schedule<ScheduleType>;
-};
+// <editor-fold desc="PolicyTraitMatcher specialization"> {{{1
+
+template <class Sched>
+struct PolicyTraitMatcher<ScheduleTrait, Schedule<Sched>> : std::true_type {};
 
-// </editor-fold> end AnalyzeExecPolicy specializations }}}1
+// </editor-fold> end PolicyTraitMatcher specialization }}}1
 //==============================================================================
 
 }  // end namespace Impl
diff --git a/packages/kokkos/core/src/traits/Kokkos_Traits_fwd.hpp b/packages/kokkos/core/src/traits/Kokkos_Traits_fwd.hpp
index b8b9a0ca2d889b08116528803d0c1b096060ecad..b8289ca6188846884277ca514db453145f1cb3c6 100644
--- a/packages/kokkos/core/src/traits/Kokkos_Traits_fwd.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_Traits_fwd.hpp
@@ -51,9 +51,15 @@ namespace Impl {
 template <class Enable, class... TraitsList>
 struct AnalyzeExecPolicy;
 
+template <class Enable, class TraitSpecList, class... Traits>
+struct AnalyzeExecPolicyUseMatcher;
+
 template <class AnalysisResults>
 struct ExecPolicyTraitsWithDefaults;
 
+template <class TraitSpec, class Trait, class Enable>
+struct PolicyTraitMatcher;
+
 template <class TraitSpec, template <class...> class PolicyTemplate,
           class AlreadyProcessedList, class ToProcessList, class NewTrait,
           class Enable = void>
@@ -67,6 +73,40 @@ struct PolicyTraitAdaptor;
 // traits
 struct dependent_policy_trait_default;
 
+//==============================================================================
+// <editor-fold desc="Execution policy trait specifications"> {{{1
+
+struct ExecutionSpaceTrait;
+struct IndexTypeTrait;
+struct ScheduleTrait;
+struct IterationPatternTrait;
+struct WorkItemPropertyTrait;
+struct LaunchBoundsTrait;
+struct OccupancyControlTrait;
+struct GraphKernelTrait;
+struct WorkTagTrait;
+
+// Keep these sorted by frequency of use to reduce compilation time
+//
+// clang-format off
+using execution_policy_trait_specifications =
+  type_list<
+    ExecutionSpaceTrait,
+    IndexTypeTrait,
+    ScheduleTrait,
+    IterationPatternTrait,
+    WorkItemPropertyTrait,
+    LaunchBoundsTrait,
+    OccupancyControlTrait,
+    GraphKernelTrait,
+    // This one has to be last, unfortunately:
+    WorkTagTrait
+  >;
+// clang-format on
+
+// </editor-fold> end Execution policy trait specifications }}}1
+//==============================================================================
+
 }  // end namespace Impl
 }  // end namespace Kokkos
 
diff --git a/packages/kokkos/core/src/traits/Kokkos_WorkItemPropertyTrait.hpp b/packages/kokkos/core/src/traits/Kokkos_WorkItemPropertyTrait.hpp
index 2656316fb934333655d0370f4dc3d40eea7bbb86..35671d19b02bb72c777b77717beced94d152beb3 100644
--- a/packages/kokkos/core/src/traits/Kokkos_WorkItemPropertyTrait.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_WorkItemPropertyTrait.hpp
@@ -60,6 +60,12 @@ struct WorkItemPropertyTrait : TraitSpecificationBase<WorkItemPropertyTrait> {
   struct base_traits {
     using work_item_property = Kokkos::Experimental::WorkItemProperty::None_t;
   };
+  template <class WorkItemProp, class AnalyzeNextTrait>
+  struct mixin_matching_trait : AnalyzeNextTrait {
+    using base_t = AnalyzeNextTrait;
+    using base_t::base_t;
+    using work_item_property = WorkItemProp;
+  };
   template <class T>
   using trait_matches_specification =
       Kokkos::Experimental::is_work_item_property<T>;
@@ -68,26 +74,6 @@ struct WorkItemPropertyTrait : TraitSpecificationBase<WorkItemPropertyTrait> {
 // </editor-fold> end trait specification }}}1
 //==============================================================================
 
-//==============================================================================
-// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
-
-template <class Property, class... Traits>
-struct AnalyzeExecPolicy<
-    std::enable_if_t<
-        Kokkos::Experimental::is_work_item_property<Property>::value>,
-    Property, Traits...> : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
-  using base_t::base_t;
-  static_assert(
-      std::is_same<typename base_t::work_item_property,
-                   Kokkos::Experimental::WorkItemProperty::None_t>::value,
-      "Kokkos Error: More than one work item property given");
-  using work_item_property = Property;
-};
-
-// </editor-fold> end AnalyzeExecPolicy specializations }}}1
-//==============================================================================
-
 }  // end namespace Impl
 
 namespace Experimental {
diff --git a/packages/kokkos/core/src/traits/Kokkos_WorkTagTrait.hpp b/packages/kokkos/core/src/traits/Kokkos_WorkTagTrait.hpp
index 877005756a703b067c07c6f57c3fc4212f7484ca..424e5c405b70cff9f73ef5756b5dca41e9d3d618 100644
--- a/packages/kokkos/core/src/traits/Kokkos_WorkTagTrait.hpp
+++ b/packages/kokkos/core/src/traits/Kokkos_WorkTagTrait.hpp
@@ -49,6 +49,7 @@
 #include <Kokkos_Concepts.hpp>  // is_execution_space
 #include <traits/Kokkos_PolicyTraitAdaptor.hpp>
 #include <traits/Kokkos_Traits_fwd.hpp>
+#include <impl/Kokkos_Utilities.hpp>  // type_list_any, type_list_remove_first
 
 namespace Kokkos {
 namespace Impl {
@@ -56,68 +57,65 @@ namespace Impl {
 //==============================================================================
 // <editor-fold desc="trait specification"> {{{1
 
+template <class T>
+struct show_extra_work_tag_erroneously_given_to_execution_policy;
+template <>
+struct show_extra_work_tag_erroneously_given_to_execution_policy<void> {};
+
+using _exec_policy_traits_without_work_tag = typename type_list_remove_first<
+    WorkTagTrait, execution_policy_trait_specifications>::type;
+
+template <class Trait>
+struct _trait_matches_spec_predicate {
+  template <class TraitSpec>
+  struct apply {
+    using type = typename PolicyTraitMatcher<TraitSpec, Trait>::type;
+    static constexpr bool value = type::value;
+  };
+};
+
 struct WorkTagTrait : TraitSpecificationBase<WorkTagTrait> {
   struct base_traits {
     using work_tag = void;
   };
+  template <class WorkTag, class AnalyzeNextTrait>
+  struct mixin_matching_trait : AnalyzeNextTrait {
+    using base_t = AnalyzeNextTrait;
+    using base_t::base_t;
+    using work_tag = WorkTag;
+    static constexpr auto show_work_tag_error_in_compilation_message =
+        show_extra_work_tag_erroneously_given_to_execution_policy<
+            typename base_t::work_tag>{};
+    static_assert(
+        std::is_void<typename base_t::work_tag>::value,
+        "Kokkos Error: More than one work tag given. Search compiler output "
+        "for 'show_extra_work_tag' to see the type of the errant tag.");
+  };
+  // Since we don't have subsumption in pre-C++20, we need to have the work tag
+  // "trait" handling code ensure that none of the other conditions are met.
+  // * Compile time cost complexity note: at first glance it looks like this
+  //   "rechecks" all of the other trait specs when used in the context of the
+  //   full list of execution policy traits, but actually since we've already
+  //   checked all of them to get to the end of the list, the compiler will
+  //   have already generated those definitions, so there should be little extra
+  //   cost to this. However, in the scenario where we use work tag in isolation
+  //   (like if we were to add a `require()`-like thing that changes the work
+  //   tag of an existing execution policy instance), we need to check all of
+  //   the other traits to make sure that we're not replacing something else,
+  //   given that the concept of a work tag is basically unconstrained and could
+  //   be anything.  This should still be as efficient at compile time as the
+  //   old code that just did a big long series of nested std::conditionals, but
+  //   we should benchmark this assumption if it becomes a problem.
+  template <class T>
+  using trait_matches_specification = std::integral_constant<
+      bool, !std::is_void<T>::value &&
+                !type_list_any<_trait_matches_spec_predicate<T>::template apply,
+                               _exec_policy_traits_without_work_tag>::value>;
 };
 
 // </editor-fold> end trait specification }}}1
 //==============================================================================
 
-//==============================================================================
-// <editor-fold desc="AnalyzeExecPolicy specializations"> {{{1
-
-// Since we don't have subsumption in pre-C++20, we need to have the work tag
-// "trait" handling code be unspecialized, so we handle it instead in a class
-// with a different name.
-template <class... Traits>
-struct AnalyzeExecPolicyHandleWorkTag : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
-  using base_t::base_t;
-};
-
-template <class WorkTag, class... Traits>
-struct AnalyzeExecPolicyHandleWorkTag<WorkTag, Traits...>
-    : AnalyzeExecPolicy<void, Traits...> {
-  using base_t = AnalyzeExecPolicy<void, Traits...>;
-  using base_t::base_t;
-  static_assert(std::is_void<typename base_t::work_tag>::value,
-                "Kokkos Error: More than one work tag given");
-  using work_tag = WorkTag;
-};
-
-// This only works if this is not a partial specialization, so we have to
-// do the partial specialization elsewhere
-template <class Enable, class... Traits>
-struct AnalyzeExecPolicy : AnalyzeExecPolicyHandleWorkTag<Traits...> {
-  using base_t = AnalyzeExecPolicyHandleWorkTag<Traits...>;
-  using base_t::base_t;
-};
-
-// </editor-fold> end AnalyzeExecPolicy specializations }}}1
-//==============================================================================
-
-//==============================================================================
-// <editor-fold desc="PolicyTraitMatcher specializations"> {{{1
-
-// In order to match the work tag trait the work tag "matcher" needs to be
-// unspecialized and the logic needs to be handled in a differently-named class,
-// just like above.
-template <class TraitSpec, class Trait>
-struct PolicyTraitMatcherHandleWorkTag : std::false_type {};
-
-template <class Trait>
-struct PolicyTraitMatcherHandleWorkTag<WorkTagTrait, Trait>
-    : std::integral_constant<bool, !std::is_void<Trait>::value> {};
-
-template <class TraitSpec, class Trait, class Enable>
-struct PolicyTraitMatcher /* unspecialized! */
-    : PolicyTraitMatcherHandleWorkTag<TraitSpec, Trait> {};
-
-// </editor-fold> end PolicyTraitMatcher specializations }}}1
-//==============================================================================
-
 }  // end namespace Impl
 }  // end namespace Kokkos
 
diff --git a/packages/kokkos/core/unit_test/CMakeLists.txt b/packages/kokkos/core/unit_test/CMakeLists.txt
index 5826208851090933ee296988287a6a633eb2c476..89b8ff1e4f0a8004ecd4c2f06d72544123107d03 100644
--- a/packages/kokkos/core/unit_test/CMakeLists.txt
+++ b/packages/kokkos/core/unit_test/CMakeLists.txt
@@ -41,10 +41,10 @@ SET(KOKKOS_OPENMP_FEATURE_LEVEL 999)
 SET(KOKKOS_OPENMP_NAME OpenMP)
 
 # FIXME_OPENMPTARGET - The NVIDIA HPC compiler nvc++ only compiles the first 8 incremental tests for the OpenMPTarget backend.
-IF(KOKKOS_CXX_COMPILER_ID STREQUAL PGI OR KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC)
-  SET(KOKKOS_OPENMPTARGET_FEATURE_LEVEL 8)
+IF(KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC)
+  SET(KOKKOS_OPENMPTARGET_FEATURE_LEVEL 10)
 ELSE()
-  SET(KOKKOS_OPENMPTARGET_FEATURE_LEVEL 13)
+  SET(KOKKOS_OPENMPTARGET_FEATURE_LEVEL 14)
 ENDIF()
 
 SET(KOKKOS_OPENMPTARGET_NAME Experimental::OpenMPTarget)
@@ -65,6 +65,21 @@ KOKKOS_INCLUDE_DIRECTORIES(${CMAKE_CURRENT_BINARY_DIR})
 KOKKOS_INCLUDE_DIRECTORIES(REQUIRED_DURING_INSTALLATION_TESTING ${CMAKE_CURRENT_SOURCE_DIR})
 KOKKOS_INCLUDE_DIRECTORIES(${KOKKOS_SOURCE_DIR}/core/unit_test/category_files)
 
+SET(COMPILE_ONLY_SOURCES
+  TestDetectionIdiom.cpp
+  TestInterOp.cpp
+  TestTypeList.cpp
+)
+# TestInterOp has a dependency on containers
+IF(KOKKOS_HAS_TRILINOS)
+  LIST(REMOVE_ITEM COMPILE_ONLY_SOURCES TestInterOp.cpp)
+ENDIF()
+KOKKOS_ADD_EXECUTABLE(
+  TestCompileOnly
+  SOURCES
+  ${COMPILE_ONLY_SOURCES}
+)
+
 foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;HIP;SYCL)
   # Because there is always an exception to the rule
   if(Tag STREQUAL "Threads")
@@ -98,6 +113,7 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;HIP;SYCL)
         Complex
         Crs
         DeepCopyAlignment
+        ExecutionSpace
         FunctorAnalysis
         Init
         LocalDeepCopy
@@ -107,6 +123,9 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;HIP;SYCL)
         MDRange_c
         HostSharedPtr
         HostSharedPtrAccessOnDevice
+        QuadPrecisionMath
+        ExecSpacePartitioning
+        MathematicalSpecialFunctions
         )
       set(file ${dir}/Test${Tag}_${Name}.cpp)
       # Write to a temporary intermediate file and call configure_file to avoid
@@ -190,7 +209,7 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;HIP;SYCL)
     elseif(Tag STREQUAL "HIP")
       set(TagHostAccessible HIPHostPinned)
     elseif(Tag STREQUAL "SYCL")
-      set(TagHostAccessible SYCLSharedUSMSpace)
+      set(TagHostAccessible SYCLSharedUSM)
     endif()
 
     set(${Tag}_SOURCES2B)
@@ -257,6 +276,43 @@ foreach(Tag Threads;Serial;OpenMP;Cuda;HPX;OpenMPTarget;HIP;SYCL)
   endif()
 endforeach()
 
+foreach(PairDeviceSpace HIP-HostPinned;Cuda-HostPinned;Cuda-UVM;SYCL-HostUSM;SYCL-SharedUSM)
+  string(REGEX REPLACE "([^-]*)-(.*)" "\\1" DEVICE ${PairDeviceSpace})
+  string(REGEX REPLACE "([^-]*)-(.*)" "\\2" SPACE ${PairDeviceSpace})
+
+  string(TOUPPER ${DEVICE} UPPER_DEVICE)
+  string(TOLOWER ${DEVICE} dir)
+
+  if(Kokkos_ENABLE_${UPPER_DEVICE})
+    set(dir ${CMAKE_CURRENT_BINARY_DIR}/${dir})
+    file(MAKE_DIRECTORY ${dir})
+    foreach(Name
+      SharedAlloc
+      ViewAPI_a
+      ViewAPI_b
+      ViewAPI_c
+      ViewAPI_d
+      ViewAPI_e
+      ViewCopy_a
+      ViewCopy_b
+      ViewMapping_a
+      ViewMapping_b
+      ViewMapping_subview
+      )
+      set(file ${dir}/Test${DEVICE}${SPACE}_${Name}.cpp)
+      # Write to a temporary intermediate file and call configure_file to avoid
+      # updating timestamps triggering unnecessary rebuilds on subsequent cmake runs.
+      file(WRITE ${dir}/dummy.cpp
+          "#include <Test${DEVICE}${SPACE}_Category.hpp>\n"
+          "#include <Test${Name}.hpp>\n"
+      )
+      configure_file(${dir}/dummy.cpp ${file})
+      list(APPEND ${DEVICE}_SOURCES3 ${file})
+    endforeach()
+    list(APPEND ${DEVICE}_SOURCES ${${DEVICE}_SOURCES3})
+  endif()
+endforeach()
+
 if(Kokkos_ENABLE_OPENMPTARGET)
   list(REMOVE_ITEM OpenMPTarget_SOURCES
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_AtomicOperations_complexfloat.cpp
@@ -264,9 +320,7 @@ if(Kokkos_ENABLE_OPENMPTARGET)
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_Crs.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_LocalDeepCopy.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_Other.cpp
-    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_Reductions_DeviceView.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_TeamReductionScan.cpp
-    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_TeamTeamSize.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_TeamScan.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_ViewAPI_e.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_ViewCopy_a.cpp
@@ -278,9 +332,16 @@ if(Kokkos_ENABLE_OPENMPTARGET)
 endif()
 
 # FIXME_OPENMPTARGET - Comment non-passing tests with the NVIDIA HPC compiler nvc++
-IF(KOKKOS_ENABLE_OPENMPTARGET
-   AND (KOKKOS_CXX_COMPILER_ID STREQUAL PGI OR KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC))
+IF(KOKKOS_ENABLE_OPENMPTARGET AND KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC)
   list(REMOVE_ITEM OpenMPTarget_SOURCES
+    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_int64_t_reduce.cpp
+    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_int64_t_reduce_dynamic.cpp
+    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_int64_t_reduce_dynamic_view.cpp
+    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_double_reduce.cpp
+    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_double_reduce_dynamic.cpp
+    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_TeamTeamSize.cpp
+    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_Reductions_DeviceView.cpp
+    ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_TeamVectorRange.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_UniqueToken.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_HostSharedPtr.cpp
     ${CMAKE_CURRENT_BINARY_DIR}/openmptarget/TestOpenMPTarget_HostSharedPtrAccessOnDevice.cpp
@@ -370,14 +431,19 @@ if(Kokkos_ENABLE_PTHREAD)
   )
 endif()
 
-if(Kokkos_ENABLE_OPENMP)
+if (Kokkos_ENABLE_OPENMP)
+  set(OpenMP_EXTRA_SOURCES
+    openmp/TestOpenMP_Task.cpp
+  )
+  if (Kokkos_ENABLE_DEPRECATED_CODE_3)
+    list(APPEND OpenMP_EXTRA_SOURCES openmp/TestOpenMP_Task.cpp)
+  endif ()
   KOKKOS_ADD_EXECUTABLE_AND_TEST(
     UnitTest_OpenMP
     SOURCES
     UnitTestMainInit.cpp
     ${OpenMP_SOURCES}
-    openmp/TestOpenMP_PartitionMaster.cpp
-    openmp/TestOpenMP_Task.cpp
+    ${OpenMP_EXTRA_SOURCES}
   )
   KOKKOS_ADD_EXECUTABLE_AND_TEST(
     UnitTest_OpenMPInterOp
@@ -463,28 +529,7 @@ if(Kokkos_ENABLE_CUDA)
       UnitTestMainInit.cpp
       cuda/TestCuda_Task.cpp
       cuda/TestCuda_TeamScratchStreams.cpp
-      cuda/TestCudaHostPinned_SharedAlloc.cpp
-      cuda/TestCudaHostPinned_ViewAPI_a.cpp
-      cuda/TestCudaHostPinned_ViewAPI_b.cpp
-      cuda/TestCudaHostPinned_ViewAPI_c.cpp
-      cuda/TestCudaHostPinned_ViewAPI_d.cpp
-      cuda/TestCudaHostPinned_ViewAPI_e.cpp
-      cuda/TestCudaHostPinned_ViewCopy_a.cpp
-      cuda/TestCudaHostPinned_ViewCopy_b.cpp
-      cuda/TestCudaHostPinned_ViewMapping_a.cpp
-      cuda/TestCudaHostPinned_ViewMapping_b.cpp
-      cuda/TestCudaHostPinned_ViewMapping_subview.cpp
-      cuda/TestCudaUVM_SharedAlloc.cpp
-      cuda/TestCudaUVM_ViewAPI_a.cpp
-      cuda/TestCudaUVM_ViewAPI_b.cpp
-      cuda/TestCudaUVM_ViewAPI_c.cpp
-      cuda/TestCudaUVM_ViewAPI_d.cpp
-      cuda/TestCudaUVM_ViewAPI_e.cpp
-      cuda/TestCudaUVM_ViewCopy_a.cpp
-      cuda/TestCudaUVM_ViewCopy_b.cpp
-      cuda/TestCudaUVM_ViewMapping_a.cpp
-      cuda/TestCudaUVM_ViewMapping_b.cpp
-      cuda/TestCudaUVM_ViewMapping_subview.cpp
+      ${Cuda_SOURCES3}
       cuda/TestCuda_Spaces.cpp
   )
 
@@ -524,17 +569,8 @@ if(Kokkos_ENABLE_HIP)
       ${HIP_SOURCES}
       hip/TestHIP_ScanUnit.cpp
       hip/TestHIP_TeamScratchStreams.cpp
-      hip/TestHIPHostPinned_ViewAPI_a.cpp
-      hip/TestHIPHostPinned_ViewAPI_b.cpp
-      hip/TestHIPHostPinned_ViewAPI_c.cpp
-      hip/TestHIPHostPinned_ViewAPI_d.cpp
-      hip/TestHIPHostPinned_ViewAPI_e.cpp
-      hip/TestHIPHostPinned_ViewCopy_a.cpp
-      hip/TestHIPHostPinned_ViewCopy_b.cpp
-      hip/TestHIPHostPinned_ViewMapping_a.cpp
-      hip/TestHIPHostPinned_ViewMapping_b.cpp
-      hip/TestHIPHostPinned_ViewMapping_subview.cpp
       hip/TestHIP_AsyncLauncher.cpp
+      hip/TestHIP_BlocksizeDeduction.cpp
   )
   KOKKOS_ADD_EXECUTABLE_AND_TEST(
     UnitTest_HIPInterOpInit
@@ -595,13 +631,25 @@ if(Kokkos_ENABLE_SYCL)
       ${SYCL_SOURCES2C}
   )
 
- KOKKOS_ADD_EXECUTABLE_AND_TEST(
+  KOKKOS_ADD_EXECUTABLE_AND_TEST(
     UnitTest_SYCL2D
     SOURCES
       UnitTestMainInit.cpp
       ${SYCL_SOURCES2D}
   )
- KOKKOS_ADD_EXECUTABLE_AND_TEST(
+
+  KOKKOS_ADD_EXECUTABLE_AND_TEST(
+    UnitTest_SYCL3
+    SOURCES
+      UnitTestMainInit.cpp
+      # FIXME_SYCL
+      sycl/TestSYCL_Task.cpp
+      sycl/TestSYCL_TeamScratchStreams.cpp
+      ${SYCL_SOURCES3}
+      sycl/TestSYCL_Spaces.cpp
+  )
+
+  KOKKOS_ADD_EXECUTABLE_AND_TEST(
     UnitTest_SYCLInterOpInit
     SOURCES
       UnitTestMain.cpp
@@ -622,8 +670,7 @@ if(Kokkos_ENABLE_SYCL)
 endif()
 
 # FIXME_OPENMPTARGET - Comment non-passing tests with the NVIDIA HPC compiler nvc++
-if (KOKKOS_ENABLE_OPENMPTARGET
-    AND (KOKKOS_CXX_COMPILER_ID STREQUAL PGI OR KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC))
+if (KOKKOS_ENABLE_OPENMPTARGET AND KOKKOS_CXX_COMPILER_ID STREQUAL NVHPC)
   SET(DEFAULT_DEVICE_SOURCES
     UnitTestMainInit.cpp
     default/TestDefaultDeviceType.cpp
@@ -685,11 +732,21 @@ KOKKOS_ADD_ADVANCED_TEST( UnitTest_PushFinalizeHook_terminate
 )
 
   if(KOKKOS_ENABLE_TUNING)
+    KOKKOS_ADD_EXECUTABLE_AND_TEST(
+      UnitTest_TuningBuiltins
+      SOURCES
+      tools/TestBuiltinTuners.cpp
+    )
     KOKKOS_ADD_EXECUTABLE_AND_TEST(
       UnitTest_TuningBasics
       SOURCES
         tools/TestTuning.cpp
     )
+    KOKKOS_ADD_EXECUTABLE_AND_TEST(
+      UnitTest_CategoricalTuner
+      SOURCES
+      tools/TestCategoricalTuner.cpp
+    )
   endif()
   if(NOT Kokkos_ENABLE_OPENMPTARGET)
   KOKKOS_ADD_EXECUTABLE_AND_TEST(
@@ -698,6 +755,11 @@ KOKKOS_ADD_ADVANCED_TEST( UnitTest_PushFinalizeHook_terminate
       tools/TestLogicalSpaces.cpp
   )
   endif()
+  KOKKOS_ADD_EXECUTABLE_AND_TEST(
+    UnitTest_EventCorrectness
+    SOURCES
+    tools/TestEventCorrectness.cpp
+  )
   if(KOKKOS_ENABLE_LIBDL)
 
     KOKKOS_ADD_TEST_LIBRARY(
@@ -745,7 +807,7 @@ KOKKOS_ADD_ADVANCED_TEST( UnitTest_PushFinalizeHook_terminate
       EXE  ProfilingAllCalls
       TOOL kokkosprinter-tool
       ARGS --kokkos-tools-args="-c test delimit"
-      PASS_REGULAR_EXPRESSION "kokkosp_init_library::kokkosp_parse_args:4:KokkosCore_ProfilingAllCalls:-c:test:delimit::.*::kokkosp_allocate_data:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_begin_parallel_for:Kokkos::View::initialization [[]source]:0:0::kokkosp_end_parallel_for:0::kokkosp_allocate_data:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:40::kokkosp_begin_parallel_for:Kokkos::View::initialization [[]destination]:0:0::kokkosp_end_parallel_for:0::kokkosp_begin_deep_copy:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_end_deep_copy::kokkosp_begin_parallel_for:parallel_for:${SIZE_REGEX}:0::kokkosp_end_parallel_for:0::kokkosp_begin_parallel_reduce:parallel_reduce:${SIZE_REGEX}:1${SKIP_SCRATCH_INITIALIZATION_REGEX}::kokkosp_end_parallel_reduce:1::kokkosp_begin_parallel_scan:parallel_scan:${SIZE_REGEX}:2::kokkosp_end_parallel_scan:2::kokkosp_push_profile_region:push_region::kokkosp_pop_profile_region::kokkosp_create_profile_section:created_section:3::kokkosp_start_profile_section:3::kokkosp_stop_profile_section:3::kokkosp_destroy_profile_section:3::kokkosp_profile_event:profiling_event::kokkosp_declare_metadata:dogs:good::kokkosp_deallocate_data:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:40::kokkosp_deallocate_data:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_finalize_library::"
+      PASS_REGULAR_EXPRESSION "kokkosp_init_library::kokkosp_parse_args:4:KokkosCore_ProfilingAllCalls:-c:test:delimit::.*::kokkosp_allocate_data:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_begin_parallel_for:Kokkos::View::initialization [[]source] via memset:[0-9]+:0::kokkosp_end_parallel_for:0::kokkosp_allocate_data:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:40::kokkosp_begin_parallel_for:Kokkos::View::initialization [[]destination] via memset:[0-9]+:0::kokkosp_end_parallel_for:0::kokkosp_begin_deep_copy:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_end_deep_copy::kokkosp_begin_parallel_for:parallel_for:${SIZE_REGEX}:0::kokkosp_end_parallel_for:0::kokkosp_begin_parallel_reduce:parallel_reduce:${SIZE_REGEX}:1${SKIP_SCRATCH_INITIALIZATION_REGEX}::kokkosp_end_parallel_reduce:1::kokkosp_begin_parallel_scan:parallel_scan:${SIZE_REGEX}:2::kokkosp_end_parallel_scan:2::kokkosp_push_profile_region:push_region::kokkosp_pop_profile_region::kokkosp_create_profile_section:created_section:3::kokkosp_start_profile_section:3::kokkosp_stop_profile_section:3::kokkosp_destroy_profile_section:3::kokkosp_profile_event:profiling_event::kokkosp_declare_metadata:dogs:good::kokkosp_deallocate_data:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:40::kokkosp_deallocate_data:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_finalize_library::"
     )
 
     # Above will test that leading/trailing quotes are stripped bc ctest cmd args is:
@@ -762,7 +824,7 @@ KOKKOS_ADD_ADVANCED_TEST( UnitTest_PushFinalizeHook_terminate
       EXE  ProfilingAllCalls
       ARGS [=[--kokkos-tools-args=-c test delimit]=]
             --kokkos-tools-library=$<TARGET_FILE:kokkosprinter-tool>
-      PASS_REGULAR_EXPRESSION "kokkosp_init_library::kokkosp_parse_args:4:KokkosCore_ProfilingAllCalls:-c:test:delimit::.*::kokkosp_allocate_data:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_begin_parallel_for:Kokkos::View::initialization [[]source]:0:0::kokkosp_end_parallel_for:0::kokkosp_allocate_data:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:40::kokkosp_begin_parallel_for:Kokkos::View::initialization [[]destination]:0:0::kokkosp_end_parallel_for:0::kokkosp_begin_deep_copy:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_end_deep_copy::kokkosp_begin_parallel_for:parallel_for:${SIZE_REGEX}:0::kokkosp_end_parallel_for:0::kokkosp_begin_parallel_reduce:parallel_reduce:${SIZE_REGEX}:1${SKIP_SCRATCH_INITIALIZATION_REGEX}::kokkosp_end_parallel_reduce:1::kokkosp_begin_parallel_scan:parallel_scan:${SIZE_REGEX}:2::kokkosp_end_parallel_scan:2::kokkosp_push_profile_region:push_region::kokkosp_pop_profile_region::kokkosp_create_profile_section:created_section:3::kokkosp_start_profile_section:3::kokkosp_stop_profile_section:3::kokkosp_destroy_profile_section:3::kokkosp_profile_event:profiling_event::kokkosp_declare_metadata:dogs:good::kokkosp_deallocate_data:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:40::kokkosp_deallocate_data:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_finalize_library::"
+      PASS_REGULAR_EXPRESSION "kokkosp_init_library::kokkosp_parse_args:4:KokkosCore_ProfilingAllCalls:-c:test:delimit::.*::kokkosp_allocate_data:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_begin_parallel_for:Kokkos::View::initialization [[]source] via memset:[0-9]+:0::kokkosp_end_parallel_for:0::kokkosp_allocate_data:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:40::kokkosp_begin_parallel_for:Kokkos::View::initialization [[]destination] via memset:[0-9]+:0::kokkosp_end_parallel_for:0::kokkosp_begin_deep_copy:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_end_deep_copy::kokkosp_begin_parallel_for:parallel_for:${SIZE_REGEX}:0::kokkosp_end_parallel_for:0::kokkosp_begin_parallel_reduce:parallel_reduce:${SIZE_REGEX}:1${SKIP_SCRATCH_INITIALIZATION_REGEX}::kokkosp_end_parallel_reduce:1::kokkosp_begin_parallel_scan:parallel_scan:${SIZE_REGEX}:2::kokkosp_end_parallel_scan:2::kokkosp_push_profile_region:push_region::kokkosp_pop_profile_region::kokkosp_create_profile_section:created_section:3::kokkosp_start_profile_section:3::kokkosp_stop_profile_section:3::kokkosp_destroy_profile_section:3::kokkosp_profile_event:profiling_event::kokkosp_declare_metadata:dogs:good::kokkosp_deallocate_data:${MEMSPACE_REGEX}:destination:${ADDRESS_REGEX}:40::kokkosp_deallocate_data:${MEMSPACE_REGEX}:source:${ADDRESS_REGEX}:40::kokkosp_finalize_library::"
     )
   endif() #KOKKOS_ENABLE_LIBDL
 if(NOT KOKKOS_HAS_TRILINOS)
diff --git a/packages/kokkos/core/unit_test/Makefile b/packages/kokkos/core/unit_test/Makefile
index 390fc79a4755e46cbd61b28ee54d44814fa501d9..422628221402586ec4829ad5d8b628cbdd3736b1 100644
--- a/packages/kokkos/core/unit_test/Makefile
+++ b/packages/kokkos/core/unit_test/Makefile
@@ -73,6 +73,8 @@ tmp := $(foreach device, $(KOKKOS_DEVICELIST), \
   ) \
 )
 
+GPU_SPACE_TESTS = SharedAlloc ViewAPI_a ViewAPI_b ViewAPI_c ViewAPI_d ViewAPI_e ViewCopy_a ViewCopy_b ViewMapping_a ViewMapping_b ViewMapping_subview
+
 SUBVIEW_TESTS = SubView_a SubView_b SubView_c01 SubView_c02 SubView_c03 SubView_c04 SubView_c05 SubView_c06 SubView_c07 SubView_c08 SubView_c09 SubView_c10 SubView_c11 SubView_c12 SubView_c13
 
 KOKKOS_SUBVIEW_DEVICELIST := $(filter-out Cuda, $(KOKKOS_DEVICELIST))
@@ -94,6 +96,16 @@ ifeq ($(KOKKOS_INTERNAL_USE_CUDA), 1)
       )\
     )
 
+    GPU_SPACES = CudaHostPinned CudaUVM
+    tmp := $(foreach space, $(GPU_SPACES), \
+      tmp2 := $(foreach test, $(GPU_SPACE_TESTS), \
+        $(if $(filter Test$(space)_$(test).cpp, $(shell ls Test$(space)_$(test).cpp 2>/dev/null)),,\
+          $(shell echo "\#include <Test$(space)_Category.hpp>" > Test$(space)_$(test).cpp); \
+          $(shell echo "\#include <Test"$(test)".hpp>" >> Test$(space)_$(test).cpp); \
+        )\
+      )\
+    )
+
     OBJ_CUDA = UnitTestMainInit.o gtest-all.o
     OBJ_CUDA += TestCuda_Init.o
     OBJ_CUDA += TestCuda_SharedAlloc.o TestCudaUVM_SharedAlloc.o TestCudaHostPinned_SharedAlloc.o
@@ -261,6 +273,16 @@ ifeq ($(KOKKOS_INTERNAL_USE_OPENMPTARGET), 1)
 endif
 
 ifeq ($(KOKKOS_INTERNAL_USE_HIP), 1)
+	GPU_SPACES = HIPHostPinned
+	tmp := $(foreach space, $(GPU_SPACES), \
+	  tmp2 := $(foreach test, $(GPU_SPACE_TESTS), \
+	    $(if $(filter Test$(space)_$(test).cpp, $(shell ls Test$(space)_$(test).cpp 2>/dev/null)),,\
+	      $(shell echo "\#include <Test$(space)_Category.hpp>" > Test$(space)_$(test).cpp); \
+	      $(shell echo "\#include <Test"$(test)".hpp>" >> Test$(space)_$(test).cpp); \
+	    )\
+	  )\
+	)
+
 	OBJ_HIP = UnitTestMainInit.o gtest-all.o
 	OBJ_HIP += TestHIP_Init.o
 	OBJ_HIP += TestHIP_Reducers_a.o TestHIP_Reducers_b.o TestHIP_Reducers_c.o TestHIP_Reducers_d.o
diff --git a/packages/kokkos/core/unit_test/TestAtomicOperations.hpp b/packages/kokkos/core/unit_test/TestAtomicOperations.hpp
index 04362125c0648e679f9a1cfb9886ccb84e6b14d5..257ad2e9e5bba73babacd0153ba74f0ab1a2ba15 100644
--- a/packages/kokkos/core/unit_test/TestAtomicOperations.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomicOperations.hpp
@@ -81,6 +81,56 @@ struct InitFunctor {
   InitFunctor(T _init_value) : init_value(_init_value) {}
 };
 
+//---------------------------------------------------
+//--------------atomic_load/store/assign---------------------
+//---------------------------------------------------
+#ifdef KOKKOS_ENABLE_IMPL_DESUL_ATOMICS
+template <class T, class DEVICE_TYPE>
+struct LoadStoreFunctor {
+  using execution_space = DEVICE_TYPE;
+  using type            = Kokkos::View<T, execution_space>;
+
+  type data;
+  T i0;
+  T i1;
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(int) const {
+    T old = Kokkos::atomic_load(&data());
+    if (old != i0)
+      Kokkos::abort("Kokkos Atomic Load didn't get the right value");
+    Kokkos::atomic_store(&data(), i1);
+    Kokkos::atomic_assign(&data(), old);
+  }
+  LoadStoreFunctor(T _i0, T _i1) : i0(_i0), i1(_i1) {}
+};
+#endif
+
+template <class T, class DeviceType>
+bool LoadStoreAtomicTest(T i0, T i1) {
+  using execution_space = typename DeviceType::execution_space;
+  struct InitFunctor<T, execution_space> f_init(i0);
+  typename InitFunctor<T, execution_space>::type data("Data");
+  typename InitFunctor<T, execution_space>::h_type h_data("HData");
+
+  f_init.data = data;
+  Kokkos::parallel_for(1, f_init);
+  execution_space().fence();
+
+#ifdef KOKKOS_ENABLE_DESUL_ATOMICS
+  struct LoadStoreFunctor<T, execution_space> f(i0, i1);
+
+  f.data = data;
+  Kokkos::parallel_for(1, f);
+#else
+  h_data() = i1;
+#endif
+
+  Kokkos::deep_copy(h_data, data);
+
+  return h_data() == i0;
+}
+
 //---------------------------------------------------
 //--------------atomic_fetch_max---------------------
 //---------------------------------------------------
@@ -594,7 +644,10 @@ struct AndFunctor {
   T i1;
 
   KOKKOS_INLINE_FUNCTION
-  void operator()(int) const { Kokkos::atomic_fetch_and(&data(), (T)i1); }
+  void operator()(int) const {
+    T result = Kokkos::atomic_fetch_and(&data(), (T)i1);
+    Kokkos::atomic_and(&data(), result);
+  }
 
   AndFunctor(T _i0, T _i1) : i0(_i0), i1(_i1) {}
 };
@@ -665,7 +718,10 @@ struct OrFunctor {
   T i1;
 
   KOKKOS_INLINE_FUNCTION
-  void operator()(int) const { Kokkos::atomic_fetch_or(&data(), (T)i1); }
+  void operator()(int) const {
+    T result = Kokkos::atomic_fetch_or(&data(), (T)i1);
+    Kokkos::atomic_or(&data(), result);
+  }
 
   OrFunctor(T _i0, T _i1) : i0(_i0), i1(_i1) {}
 };
@@ -954,6 +1010,7 @@ bool AtomicOperationsTestIntegralType(int i0, int i1, int test) {
     case 10: return RShiftAtomicTest<T, DeviceType>((T)i0, (T)i1);
     case 11: return IncAtomicTest<T, DeviceType>((T)i0);
     case 12: return DecAtomicTest<T, DeviceType>((T)i0);
+    case 13: return LoadStoreAtomicTest<T, DeviceType>((T)i0, (T)i1);
   }
 
   return 0;
@@ -966,6 +1023,7 @@ bool AtomicOperationsTestNonIntegralType(int i0, int i1, int test) {
     case 2: return MinAtomicTest<T, DeviceType>((T)i0, (T)i1);
     case 3: return MulAtomicTest<T, DeviceType>((T)i0, (T)i1);
     case 4: return DivAtomicTest<T, DeviceType>((T)i0, (T)i1);
+    case 5: return LoadStoreAtomicTest<T, DeviceType>((T)i0, (T)i1);
   }
 
   return 0;
diff --git a/packages/kokkos/core/unit_test/TestAtomicOperations_double.hpp b/packages/kokkos/core/unit_test/TestAtomicOperations_double.hpp
index ba9937e1c6643bfd8a4decde2c7823061b0fcbe4..303f5b6eb9f77c3767056ac2639122d4b01b9d7a 100644
--- a/packages/kokkos/core/unit_test/TestAtomicOperations_double.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomicOperations_double.hpp
@@ -57,6 +57,8 @@ TEST(TEST_CATEGORY, atomic_operations_double) {
                  double, TEST_EXECSPACE>(start, end - i, 3)));
     ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestNonIntegralType<
                  double, TEST_EXECSPACE>(start, end - i, 4)));
+    ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestNonIntegralType<
+                 double, TEST_EXECSPACE>(start, end - i, 5)));
   }
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestAtomicOperations_float.hpp b/packages/kokkos/core/unit_test/TestAtomicOperations_float.hpp
index aa56b5ff10e770d2964d498f99e25611d85311c6..d3d4916b4ea6d623b010834627f41ebf65161ff7 100644
--- a/packages/kokkos/core/unit_test/TestAtomicOperations_float.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomicOperations_float.hpp
@@ -57,6 +57,8 @@ TEST(TEST_CATEGORY, atomic_operations_float) {
                  float, TEST_EXECSPACE>(start, end - i, 3)));
     ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestNonIntegralType<
                  float, TEST_EXECSPACE>(start, end - i, 4)));
+    ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestNonIntegralType<
+                 float, TEST_EXECSPACE>(start, end - i, 5)));
   }
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestAtomicOperations_int.hpp b/packages/kokkos/core/unit_test/TestAtomicOperations_int.hpp
index f828be6223c7b4e7554252fd04927ce1a3fcb69a..e5f2f334fc2b07e24ed5f77d75a64947c2117e21 100644
--- a/packages/kokkos/core/unit_test/TestAtomicOperations_int.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomicOperations_int.hpp
@@ -71,6 +71,8 @@ TEST(TEST_CATEGORY, atomic_operations_int) {
                  int, TEST_EXECSPACE>(start, end - i, 11)));
     ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
                  int, TEST_EXECSPACE>(start, end - i, 12)));
+    ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
+                 int, TEST_EXECSPACE>(start, end - i, 13)));
   }
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestAtomicOperations_longint.hpp b/packages/kokkos/core/unit_test/TestAtomicOperations_longint.hpp
index eee44c9571cf890b25a9a1b9bb32edd279d3cae7..d4fda70e80cff156aa58bc0294c66be38729f745 100644
--- a/packages/kokkos/core/unit_test/TestAtomicOperations_longint.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomicOperations_longint.hpp
@@ -71,6 +71,8 @@ TEST(TEST_CATEGORY, atomic_operations_long) {
                  long int, TEST_EXECSPACE>(start, end - i, 11)));
     ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
                  long int, TEST_EXECSPACE>(start, end - i, 12)));
+    ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
+                 long int, TEST_EXECSPACE>(start, end - i, 13)));
   }
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestAtomicOperations_longlongint.hpp b/packages/kokkos/core/unit_test/TestAtomicOperations_longlongint.hpp
index 73d4a61d7291f852d1ff2d6607d36a1d0bb2f829..b7fb0cdae5f99f6704dbdd18c2554bcb6b3e5ddf 100644
--- a/packages/kokkos/core/unit_test/TestAtomicOperations_longlongint.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomicOperations_longlongint.hpp
@@ -71,6 +71,8 @@ TEST(TEST_CATEGORY, atomic_operations_longlong) {
                  long long int, TEST_EXECSPACE>(start, end - i, 11)));
     ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
                  long long int, TEST_EXECSPACE>(start, end - i, 12)));
+    ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
+                 long long int, TEST_EXECSPACE>(start, end - i, 13)));
   }
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestAtomicOperations_unsignedint.hpp b/packages/kokkos/core/unit_test/TestAtomicOperations_unsignedint.hpp
index 02f337c57c64633d62d8111c28ae49cee05e80e3..c3c6bc9fb38d9dc9af37bc69c29e60d1fd040cc6 100644
--- a/packages/kokkos/core/unit_test/TestAtomicOperations_unsignedint.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomicOperations_unsignedint.hpp
@@ -71,6 +71,8 @@ TEST(TEST_CATEGORY, atomic_operations_unsigned) {
                  unsigned int, TEST_EXECSPACE>(start, end - i, 11)));
     ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
                  unsigned int, TEST_EXECSPACE>(start, end - i, 12)));
+    ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
+                 unsigned int, TEST_EXECSPACE>(start, end - i, 13)));
   }
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestAtomicOperations_unsignedlongint.hpp b/packages/kokkos/core/unit_test/TestAtomicOperations_unsignedlongint.hpp
index f4340475f573c3c8f4c108f8b7bfacef0d72af4e..f3be4bedb794884998639eb9a313db5079bebdd2 100644
--- a/packages/kokkos/core/unit_test/TestAtomicOperations_unsignedlongint.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomicOperations_unsignedlongint.hpp
@@ -71,6 +71,8 @@ TEST(TEST_CATEGORY, atomic_operations_unsignedlong) {
                  unsigned long int, TEST_EXECSPACE>(start, end - i, 11)));
     ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
                  unsigned long int, TEST_EXECSPACE>(start, end - i, 12)));
+    ASSERT_TRUE((TestAtomicOperations::AtomicOperationsTestIntegralType<
+                 unsigned long int, TEST_EXECSPACE>(start, end - i, 13)));
   }
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestAtomicViews.hpp b/packages/kokkos/core/unit_test/TestAtomicViews.hpp
index b615b407f334a60d187bbc3c27b3c69110acc94c..e029ad81f576f25333470e6078eee9445abba3ec 100644
--- a/packages/kokkos/core/unit_test/TestAtomicViews.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomicViews.hpp
@@ -245,11 +245,11 @@ class TestAtomicViewAPI {
     ASSERT_EQ(ax.use_count(), size_t(4));
     ASSERT_EQ(const_ax.use_count(), ax.use_count());
 
-    ASSERT_FALSE(ax.data() == nullptr);
-    ASSERT_FALSE(const_ax.data() == nullptr);  // referenceable ptr
-    ASSERT_FALSE(unmanaged_ax.data() == nullptr);
-    ASSERT_FALSE(unmanaged_ax_from_ptr_dx.data() == nullptr);
-    ASSERT_FALSE(ay.data() == nullptr);
+    ASSERT_NE(ax.data(), nullptr);
+    ASSERT_NE(const_ax.data(), nullptr);  // referenceable ptr
+    ASSERT_NE(unmanaged_ax.data(), nullptr);
+    ASSERT_NE(unmanaged_ax_from_ptr_dx.data(), nullptr);
+    ASSERT_NE(ay.data(), nullptr);
     //    ASSERT_NE( ax, ay );
     //    Above test results in following runtime error from gtest:
     //    Expected: (ax) != (ay), actual: 32-byte object <30-01 D0-A0 D8-7F
@@ -278,7 +278,7 @@ class TestAtomicViewAPI {
                          Kokkos::MemoryTraits<Kokkos::Atomic> >& arg_const,
       const Kokkos::View<const DataType, device,
                          Kokkos::MemoryTraits<Kokkos::Atomic> >& arg) {
-    ASSERT_TRUE(arg_const == arg);
+    ASSERT_EQ(arg_const, arg);
   }
 
   static void run_test_const() {
@@ -290,8 +290,8 @@ class TestAtomicViewAPI {
     typeX x("X");
     const_typeX xc = x;
 
-    // ASSERT_TRUE( xc == x ); // const xc is referenceable, non-const x is not
-    // ASSERT_TRUE( x == xc );
+    // ASSERT_EQ( xc ,  x ); // const xc is referenceable, non-const x is not
+    // ASSERT_EQ( x ,  xc );
 
     check_auto_conversion_to_const(x, xc);
   }
diff --git a/packages/kokkos/core/unit_test/TestAtomics.hpp b/packages/kokkos/core/unit_test/TestAtomics.hpp
index e41ad5257d64ad3acb3266a0354f18d291662377..f2993914a11560f30cf70aabacf444f3b38e9bfc 100644
--- a/packages/kokkos/core/unit_test/TestAtomics.hpp
+++ b/packages/kokkos/core/unit_test/TestAtomics.hpp
@@ -97,7 +97,7 @@ struct SuperScalar {
   }
 
   KOKKOS_INLINE_FUNCTION
-  SuperScalar operator+(const SuperScalar& src) {
+  SuperScalar operator+(const SuperScalar& src) const {
     SuperScalar tmp = *this;
     for (int i = 0; i < N; i++) {
       tmp.val[i] += src.val[i];
@@ -540,8 +540,6 @@ TEST(TEST_CATEGORY, atomics) {
 
 // FIXME_SYCL atomics for large types to be implemented
 #ifndef KOKKOS_ENABLE_SYCL
-  // FIXME_HIP HIP doesn't yet support atomics for >64bit types properly
-#ifndef KOKKOS_ENABLE_HIP
   ASSERT_TRUE(
       (TestAtomic::Loop<Kokkos::complex<double>, TEST_EXECSPACE>(1, 1)));
   ASSERT_TRUE(
@@ -567,7 +565,6 @@ TEST(TEST_CATEGORY, atomics) {
 #endif
 #endif
 #endif
-#endif
 }
 
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestComplex.hpp b/packages/kokkos/core/unit_test/TestComplex.hpp
index b926058ebf990b0c7d0bff6f4c22b5bd4c12e2e8..be0c1e50d7013efc177f427b481a4b67b1441744 100644
--- a/packages/kokkos/core/unit_test/TestComplex.hpp
+++ b/packages/kokkos/core/unit_test/TestComplex.hpp
@@ -515,4 +515,44 @@ TEST(TEST_CATEGORY, complex_issue_3867) {
 #undef CHECK_POW_COMPLEX_PROMOTION
 }
 
+TEST(TEST_CATEGORY, complex_operations_arithmetic_types_overloads) {
+#define STATIC_ASSERT(cond) static_assert(cond, "")
+
+  STATIC_ASSERT(Kokkos::real(1) == 1.);
+  STATIC_ASSERT(Kokkos::real(2.f) == 2.f);
+  STATIC_ASSERT(Kokkos::real(3.) == 3.);
+  STATIC_ASSERT(Kokkos::real(4.l) == 4.l);
+  STATIC_ASSERT((std::is_same<decltype(Kokkos::real(1)), double>::value));
+  STATIC_ASSERT((std::is_same<decltype(Kokkos::real(2.f)), float>::value));
+  STATIC_ASSERT((std::is_same<decltype(Kokkos::real(3.)), double>::value));
+  STATIC_ASSERT(
+      (std::is_same<decltype(Kokkos::real(4.l)), long double>::value));
+
+  STATIC_ASSERT(Kokkos::imag(1) == 0.);
+  STATIC_ASSERT(Kokkos::imag(2.f) == 0.f);
+  STATIC_ASSERT(Kokkos::imag(3.) == 0.);
+  STATIC_ASSERT(Kokkos::imag(4.l) == 0.l);
+  STATIC_ASSERT((std::is_same<decltype(Kokkos::imag(1)), double>::value));
+  STATIC_ASSERT((std::is_same<decltype(Kokkos::imag(2.f)), float>::value));
+  STATIC_ASSERT((std::is_same<decltype(Kokkos::imag(3.)), double>::value));
+  STATIC_ASSERT(
+      (std::is_same<decltype(Kokkos::real(4.l)), long double>::value));
+
+  // FIXME in principle could be checked at compile time too
+  ASSERT_EQ(Kokkos::conj(1), Kokkos::complex<double>(1));
+  ASSERT_EQ(Kokkos::conj(2.f), Kokkos::complex<float>(2.f));
+  ASSERT_EQ(Kokkos::conj(3.), Kokkos::complex<double>(3.));
+  ASSERT_EQ(Kokkos::conj(4.l), Kokkos::complex<long double>(4.l));
+  STATIC_ASSERT((
+      std::is_same<decltype(Kokkos::conj(1)), Kokkos::complex<double>>::value));
+  STATIC_ASSERT((std::is_same<decltype(Kokkos::conj(2.f)),
+                              Kokkos::complex<float>>::value));
+  STATIC_ASSERT((std::is_same<decltype(Kokkos::conj(3.)),
+                              Kokkos::complex<double>>::value));
+  STATIC_ASSERT((std::is_same<decltype(Kokkos::conj(4.l)),
+                              Kokkos::complex<long double>>::value));
+
+#undef STATIC_ASSERT
+}
+
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestDeepCopyAlignment.hpp b/packages/kokkos/core/unit_test/TestDeepCopyAlignment.hpp
index 49f8daf89eabca9b3aa7e1f06d7a10ceb23a6a24..f487a015fbf261f85bf2b8a0b4755dadcefe2f32 100644
--- a/packages/kokkos/core/unit_test/TestDeepCopyAlignment.hpp
+++ b/packages/kokkos/core/unit_test/TestDeepCopyAlignment.hpp
@@ -296,7 +296,7 @@ struct TestDeepCopyScalarConversion {
 
     int64_t errors = 0;
     Kokkos::deep_copy(errors, error_count);
-    ASSERT_TRUE(errors == 0);
+    ASSERT_EQ(errors, 0);
 
     Kokkos::deep_copy(view_s1_1d, static_cast<Scalar1>(0));
     Kokkos::deep_copy(view_s1_2d, static_cast<Scalar1>(0));
@@ -306,7 +306,7 @@ struct TestDeepCopyScalarConversion {
                                              Kokkos::IndexType<int64_t>>(0, N0),
                          *this);
     Kokkos::deep_copy(errors, error_count);
-    ASSERT_TRUE(errors > 0);
+    ASSERT_GT(errors, 0);
 
     Kokkos::deep_copy(error_count, 0);
     Kokkos::deep_copy(TEST_EXECSPACE(), view_s1_1d, view_s2_1d);
@@ -318,7 +318,7 @@ struct TestDeepCopyScalarConversion {
                          *this);
 
     Kokkos::deep_copy(errors, error_count);
-    ASSERT_TRUE(errors == 0);
+    ASSERT_EQ(errors, 0);
   }
 };
 }  // namespace Impl
diff --git a/packages/kokkos/core/unit_test/TestDefaultDeviceTypeInit.hpp b/packages/kokkos/core/unit_test/TestDefaultDeviceTypeInit.hpp
index 8a9263c8df5fcd1eab370837d9d2de92281e7aaa..90e485998ec08ba98716185298860fb4c407daf2 100644
--- a/packages/kokkos/core/unit_test/TestDefaultDeviceTypeInit.hpp
+++ b/packages/kokkos/core/unit_test/TestDefaultDeviceTypeInit.hpp
@@ -79,7 +79,7 @@ char** init_kokkos_args(bool do_threads, bool do_numa, bool do_device,
   int numa_idx    = (do_other ? 3 : 0) + (do_threads ? 1 : 0);
   int device_idx =
       (do_other ? 3 : 0) + (do_threads ? 1 : 0) + (do_numa ? 1 : 0);
-  int tune_idx = (do_other ? 3 : 0) + (do_threads ? 1 : 0) + (do_numa ? 1 : 0) +
+  int tune_idx = (do_other ? 4 : 0) + (do_threads ? 1 : 0) + (do_numa ? 1 : 0) +
                  (do_device ? 1 : 0);
 
   if (do_threads) {
diff --git a/packages/kokkos/core/unit_test/TestDetectionIdiom.cpp b/packages/kokkos/core/unit_test/TestDetectionIdiom.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f87fda615643c5a75ef5ee4da7349bab0eea40cd
--- /dev/null
+++ b/packages/kokkos/core/unit_test/TestDetectionIdiom.cpp
@@ -0,0 +1,96 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#include <Kokkos_DetectionIdiom.hpp>
+
+#define STATIC_ASSERT(cond) static_assert(cond, "");
+
+void test_nonesuch() {
+  using Kokkos::nonesuch;
+  STATIC_ASSERT(!std::is_constructible<nonesuch>::value);
+  STATIC_ASSERT(!std::is_destructible<nonesuch>::value);
+  STATIC_ASSERT(!std::is_copy_constructible<nonesuch>::value);
+  STATIC_ASSERT(!std::is_move_constructible<nonesuch>::value);
+#ifdef KOKKOS_ENABLE_CXX17
+  STATIC_ASSERT(!std::is_aggregate<nonesuch>::value);
+#endif
+}
+
+#undef STATIC_ASSERT
+
+namespace Example {
+// Example from https://en.cppreference.com/w/cpp/experimental/is_detected
+template <class T>
+using copy_assign_t = decltype(std::declval<T&>() = std::declval<const T&>());
+
+struct Meow {};
+struct Purr {
+  void operator=(const Purr&) = delete;
+};
+
+static_assert(Kokkos::is_detected<copy_assign_t, Meow>::value,
+              "Meow should be copy assignable!");
+static_assert(!Kokkos::is_detected<copy_assign_t, Purr>::value,
+              "Purr should not be copy assignable!");
+static_assert(Kokkos::is_detected_exact<Meow&, copy_assign_t, Meow>::value,
+              "Copy assignment of Meow should return Meow&!");
+
+template <class T>
+using diff_t = typename T::difference_type;
+
+template <class Ptr>
+using difference_type = Kokkos::detected_or_t<std::ptrdiff_t, diff_t, Ptr>;
+
+struct Woof {
+  using difference_type = int;
+};
+struct Bark {};
+
+static_assert(std::is_same<difference_type<Woof>, int>::value,
+              "Woof's difference_type should be int!");
+static_assert(std::is_same<difference_type<Bark>, std::ptrdiff_t>::value,
+              "Bark's difference_type should be ptrdiff_t!");
+}  // namespace Example
+
+int main() {}
diff --git a/packages/kokkos/core/unit_test/TestExecSpacePartitioning.hpp b/packages/kokkos/core/unit_test/TestExecSpacePartitioning.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8f5275d3dd66ce42256bf61637112d026687a3a
--- /dev/null
+++ b/packages/kokkos/core/unit_test/TestExecSpacePartitioning.hpp
@@ -0,0 +1,129 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#include <cstdio>
+#include <stdexcept>
+#include <sstream>
+#include <iostream>
+
+#include <Kokkos_Core.hpp>
+
+namespace Test {
+namespace {
+struct SumFunctor {
+  KOKKOS_INLINE_FUNCTION
+  void operator()(int i, int& lsum) const { lsum += i; }
+};
+
+template <class ExecSpace>
+void check_distinctive(ExecSpace, ExecSpace) {}
+
+#ifdef KOKKOS_ENABLE_CUDA
+void check_distinctive(Kokkos::Cuda exec1, Kokkos::Cuda exec2) {
+  ASSERT_NE(exec1.cuda_stream(), exec2.cuda_stream());
+}
+#endif
+#ifdef KOKKOS_ENABLE_HIP
+void check_distinctive(Kokkos::Experimental::HIP exec1,
+                       Kokkos::Experimental::HIP exec2) {
+  ASSERT_NE(exec1.hip_stream(), exec2.hip_stream());
+}
+#endif
+#ifdef KOKKOS_ENABLE_SYCL
+void check_distinctive(Kokkos::Experimental::SYCL exec1,
+                       Kokkos::Experimental::SYCL exec2) {
+  ASSERT_NE(*exec1.impl_internal_space_instance()->m_queue,
+            *exec2.impl_internal_space_instance()->m_queue);
+}
+#endif
+}  // namespace
+
+void test_partitioning(std::vector<TEST_EXECSPACE>& instances) {
+  check_distinctive(instances[0], instances[1]);
+  int sum1, sum2;
+  int N = 3910;
+  Kokkos::parallel_reduce(
+      Kokkos::RangePolicy<TEST_EXECSPACE>(instances[0], 0, N), SumFunctor(),
+      sum1);
+  Kokkos::parallel_reduce(
+      Kokkos::RangePolicy<TEST_EXECSPACE>(instances[1], 0, N), SumFunctor(),
+      sum2);
+  ASSERT_EQ(sum1, sum2);
+  ASSERT_EQ(sum1, N * (N - 1) / 2);
+
+#if defined(KOKKOS_ENABLE_CUDA) || defined(KOKKOS_ENABLE_HIP) || \
+    defined(KOKKOS_ENABLE_SYCL)
+  // Eliminate unused function warning
+  // (i.e. when compiling for Serial and CUDA, during Serial compilation the
+  // Cuda overload is unused ...)
+  if (sum1 != sum2) {
+#ifdef KOKKOS_ENABLE_CUDA
+    check_distinctive(Kokkos::Cuda(), Kokkos::Cuda());
+#endif
+#ifdef KOKKOS_ENABLE_HIP
+    check_distinctive(Kokkos::Experimental::HIP(), Kokkos::Experimental::HIP());
+#endif
+#ifdef KOKKOS_ENABLE_SYCL
+    check_distinctive(Kokkos::Experimental::SYCL(),
+                      Kokkos::Experimental::SYCL());
+#endif
+  }
+#endif
+}
+
+TEST(TEST_CATEGORY, partitioning_by_args) {
+  auto instances =
+      Kokkos::Experimental::partition_space(TEST_EXECSPACE(), 1, 1.);
+  ASSERT_EQ(int(instances.size()), 2);
+  test_partitioning(instances);
+}
+
+TEST(TEST_CATEGORY, partitioning_by_vector) {
+  std::vector<int> weights{1, 1};
+  auto instances =
+      Kokkos::Experimental::partition_space(TEST_EXECSPACE(), weights);
+  ASSERT_EQ(int(instances.size()), 2);
+  test_partitioning(instances);
+}
+}  // namespace Test
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_a.cpp b/packages/kokkos/core/unit_test/TestExecutionSpace.hpp
similarity index 68%
rename from packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_a.cpp
rename to packages/kokkos/core/unit_test/TestExecutionSpace.hpp
index 316a2b5d0fe0dba2c9b74f3f6f7a6d61342d2c4c..8e4331e809a806f8b7931735e445706abc2e06c5 100644
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_a.cpp
+++ b/packages/kokkos/core/unit_test/TestExecutionSpace.hpp
@@ -42,5 +42,39 @@
 //@HEADER
 */
 
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewAPI_a.hpp>
+#include <cstdio>
+
+#include <gtest/gtest.h>
+
+#include <Kokkos_Core.hpp>
+
+namespace Test {
+
+namespace {
+
+struct StructCopy {
+  Kokkos::DefaultExecutionSpace device;
+  Kokkos::DefaultHostExecutionSpace host;
+};
+
+template <class ExecutionSpace>
+void check_struct_copy() {
+#if defined(KOKKOS_ENABLE_CUDA_LAMBDA) || !defined(KOKKOS_ENABLE_CUDA)
+  // FIXME_OPENMPTARGET nvlink error: Undefined reference to
+  // '_ZSt25__throw_bad_function_callv' in
+  // '/tmp/TestOpenMPTarget_ExecutionSpace-434d81.cubin'
+#ifndef KOKKOS_ENABLE_OPENMPTARGET
+  StructCopy data;
+  parallel_for(
+      Kokkos::RangePolicy<ExecutionSpace>(0, 1), KOKKOS_LAMBDA(int) {
+        StructCopy data2 = data;
+        KOKKOS_IMPL_DO_NOT_USE_PRINTF("%i \n", data2.device.in_parallel());
+      });
+#endif
+#endif
+}
+
+}  // namespace
+
+TEST(TEST_CATEGORY, copy_structure) { check_struct_copy<TEST_EXECSPACE>(); }
+}  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestHalfConversion.hpp b/packages/kokkos/core/unit_test/TestHalfConversion.hpp
index 277fb1b04234e58b0fe3d1639e48fcd1dc51ff86..992f56cc6b833882676f71817bae3b6bd03631d6 100644
--- a/packages/kokkos/core/unit_test/TestHalfConversion.hpp
+++ b/packages/kokkos/core/unit_test/TestHalfConversion.hpp
@@ -53,7 +53,7 @@ void test_half_conversion_type() {
   T base                         = static_cast<T>(3.3);
   Kokkos::Experimental::half_t a = Kokkos::Experimental::cast_to_half(base);
   T b                            = Kokkos::Experimental::cast_from_half<T>(a);
-  ASSERT_TRUE((double(b - base) / double(base)) < epsilon);
+  ASSERT_LT((double(b - base) / double(base)), epsilon);
 
 // TODO: Remove ifndef once https://github.com/kokkos/kokkos/pull/3480 merges
 #ifndef KOKKOS_ENABLE_SYCL
@@ -67,7 +67,7 @@ void test_half_conversion_type() {
       });
 
   Kokkos::deep_copy(b, b_v);
-  ASSERT_TRUE((double(b - base) / double(base)) < epsilon);
+  ASSERT_LT((double(b - base) / double(base)), epsilon);
 #endif  // KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA
 #endif  // KOKKOS_ENABLE_SYCL
 }
diff --git a/packages/kokkos/core/unit_test/TestHalfOperators.hpp b/packages/kokkos/core/unit_test/TestHalfOperators.hpp
index db52a05d5d36d5919e101f60dd7652c92771c885..c4cf8a745701897a2a36c38540a94149650b5e2d 100644
--- a/packages/kokkos/core/unit_test/TestHalfOperators.hpp
+++ b/packages/kokkos/core/unit_test/TestHalfOperators.hpp
@@ -269,6 +269,85 @@ enum OP_TESTS {
   N_OP_TESTS
 };
 
+template <class view_type>
+struct Functor_TestHalfVolatileOperators {
+  volatile half_t h_lhs, h_rhs;
+  view_type actual_lhs, expected_lhs;
+  double d_lhs, d_rhs;
+  Functor_TestHalfVolatileOperators(volatile half_t lhs = half_t(0),
+                                    volatile half_t rhs = half_t(0))
+      : h_lhs(lhs), h_rhs(rhs) {
+    actual_lhs   = view_type("actual_lhs", N_OP_TESTS);
+    expected_lhs = view_type("expected_lhs", N_OP_TESTS);
+    d_lhs        = cast_from_half<double>(h_lhs);
+    d_rhs        = cast_from_half<double>(h_rhs);
+    if (std::is_same<view_type, ViewTypeHost>::value) {
+      auto run_on_host = *this;
+      run_on_host(0);
+    } else {
+      Kokkos::parallel_for("Test::Functor_TestHalfVolatileOperators",
+                           Kokkos::RangePolicy<ExecutionSpace>(0, 1), *this);
+    }
+  }
+
+  KOKKOS_FUNCTION
+  void operator()(int) const {
+    volatile half_t tmp_lhs;
+
+    // Initialze output views to catch missing test invocations
+    for (int i = 0; i < N_OP_TESTS; ++i) {
+      actual_lhs(i)   = 1;
+      expected_lhs(i) = -1;
+    }
+
+    tmp_lhs              = h_lhs;
+    actual_lhs(ASSIGN)   = cast_from_half<double>(tmp_lhs);
+    expected_lhs(ASSIGN) = d_lhs;
+
+    actual_lhs(LT)   = h_lhs < h_rhs;
+    expected_lhs(LT) = d_lhs < d_rhs;
+
+    actual_lhs(LE)   = h_lhs <= h_rhs;
+    expected_lhs(LE) = d_lhs <= d_rhs;
+
+    actual_lhs(NEQ)   = h_lhs != h_rhs;
+    expected_lhs(NEQ) = d_lhs != d_rhs;
+
+    actual_lhs(GT)   = h_lhs > h_rhs;
+    expected_lhs(GT) = d_lhs > d_rhs;
+
+    actual_lhs(GE)   = h_lhs >= h_rhs;
+    expected_lhs(GE) = d_lhs >= d_rhs;
+
+    actual_lhs(EQ)   = h_lhs == h_rhs;
+    expected_lhs(EQ) = d_lhs == d_rhs;
+
+    tmp_lhs = h_lhs;
+    tmp_lhs += h_rhs;
+    actual_lhs(CADD_H_H)   = cast_from_half<double>(tmp_lhs);
+    expected_lhs(CADD_H_H) = d_lhs;
+    expected_lhs(CADD_H_H) += d_rhs;
+
+    tmp_lhs = h_lhs;
+    tmp_lhs -= h_rhs;
+    actual_lhs(CSUB_H_H)   = cast_from_half<double>(tmp_lhs);
+    expected_lhs(CSUB_H_H) = d_lhs;
+    expected_lhs(CSUB_H_H) -= d_rhs;
+
+    tmp_lhs = h_lhs;
+    tmp_lhs *= h_rhs;
+    actual_lhs(CMUL_H_H)   = cast_from_half<double>(tmp_lhs);
+    expected_lhs(CMUL_H_H) = d_lhs;
+    expected_lhs(CMUL_H_H) *= d_rhs;
+
+    tmp_lhs = h_lhs;
+    tmp_lhs /= h_rhs;
+    actual_lhs(CDIV_H_H)   = cast_from_half<double>(tmp_lhs);
+    expected_lhs(CDIV_H_H) = d_lhs;
+    expected_lhs(CDIV_H_H) /= d_rhs;
+  }
+};
+
 template <class view_type>
 struct Functor_TestHalfOperators {
   half_t h_lhs, h_rhs;
@@ -840,8 +919,33 @@ void __test_half_operators(half_t h_lhs, half_t h_rhs) {
                 epsilon);
   }
 
-  // Check whether half_t is trivially copyable
-  ASSERT_TRUE(std::is_trivially_copyable<half_t>::value);
+  // Test partial volatile support
+  volatile half_t _h_lhs = h_lhs;
+  volatile half_t _h_rhs = h_rhs;
+  Functor_TestHalfVolatileOperators<ViewType> f_volatile_device(_h_lhs, _h_rhs);
+  Functor_TestHalfVolatileOperators<ViewTypeHost> f_volatile_host(_h_lhs,
+                                                                  _h_rhs);
+
+  ExecutionSpace().fence();
+  Kokkos::deep_copy(f_device_actual_lhs, f_device.actual_lhs);
+  Kokkos::deep_copy(f_device_expected_lhs, f_device.expected_lhs);
+  for (int op_test = 0; op_test < N_OP_TESTS; op_test++) {
+    // printf("op_test = %d\n", op_test);
+    if (op_test == ASSIGN || op_test == LT || op_test == LE || op_test == NEQ ||
+        op_test == EQ || op_test == GT || op_test == GE ||
+        op_test == CADD_H_H || op_test == CSUB_H_H || op_test == CMUL_H_H ||
+        op_test == CDIV_H_H) {
+      ASSERT_NEAR(f_device_actual_lhs(op_test), f_device_expected_lhs(op_test),
+                  epsilon);
+      ASSERT_NEAR(f_host.actual_lhs(op_test), f_host.expected_lhs(op_test),
+                  epsilon);
+    }
+  }
+
+  // is_trivially_copyable is false with the addition of explicit
+  // copy constructors that are required for supporting reductions
+  // ASSERT_TRUE(std::is_trivially_copyable<half_t>::value);
+
   constexpr size_t n       = 2;
   constexpr size_t n_bytes = sizeof(half_t) * n;
   const half_t h_arr0 = half_t(0x89ab), h_arr1 = half_t(0xcdef);
@@ -854,11 +958,11 @@ void __test_half_operators(half_t h_lhs, half_t h_rhs) {
   h_arr_ptr = reinterpret_cast<char*>(h_arr);
 
   std::memcpy(c_arr, h_arr, n_bytes);
-  for (i = 0; i < n_bytes; i++) ASSERT_TRUE(c_arr[i] == h_arr_ptr[i]);
+  for (i = 0; i < n_bytes; i++) ASSERT_EQ(c_arr[i], h_arr_ptr[i]);
 
   std::memcpy(h_arr, c_arr, n_bytes);
-  ASSERT_TRUE(h_arr[0] == h_arr0);
-  ASSERT_TRUE(h_arr[1] == h_arr1);
+  ASSERT_EQ(h_arr[0], h_arr0);
+  ASSERT_EQ(h_arr[1], h_arr1);
 }
 
 void test_half_operators() {
@@ -870,7 +974,6 @@ void test_half_operators() {
     // TODO: __test_half_operators(h_lhs + cast_to_half(i + 1), half_t(0));
     // TODO: __test_half_operators(half_t(0), h_rhs + cast_to_half(i));
   }
-  // TODO: __test_half_operators(0, 0);
 }
 
 TEST(TEST_CATEGORY, half_operators) { test_half_operators(); }
diff --git a/packages/kokkos/core/unit_test/TestHostSharedPtrAccessOnDevice.hpp b/packages/kokkos/core/unit_test/TestHostSharedPtrAccessOnDevice.hpp
index 18d1ac85188ca17cd7d127d3187103f42402be18..10180251ba582e9d11672ce74b4d22335c9da3d4 100644
--- a/packages/kokkos/core/unit_test/TestHostSharedPtrAccessOnDevice.hpp
+++ b/packages/kokkos/core/unit_test/TestHostSharedPtrAccessOnDevice.hpp
@@ -52,14 +52,17 @@ using Kokkos::Impl::HostSharedPtr;
 namespace {
 
 class Data {
-  Kokkos::Array<char, 64> d;
+  char d[64];
 
  public:
-  KOKKOS_FUNCTION void write(char const* c) {
-    for (int i = 0; i < 64 && c; ++i, ++c) {
-      d[i] = *c;
-    }
+  // Because strncpy is not supported within device code
+  static KOKKOS_FUNCTION void my_strncpy(char* dst, const char* src,
+                                         size_t cnt) {
+    while (cnt-- > 0 && (*dst++ = *src++) != '\0')
+      ;
+    while (cnt-- > 0) *dst++ = '\0';
   }
+  KOKKOS_FUNCTION void write(char const* s) { my_strncpy(d, s, sizeof(d)); }
 };
 
 template <class SmartPtr>
@@ -154,3 +157,135 @@ TEST(TEST_CATEGORY, host_shared_ptr_special_members_on_device) {
   check_special_members_on_device(device_ptr);
 }
 #endif
+
+// FIXME_OPENMPTARGET
+#if defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA) && \
+    !defined(KOKKOS_ENABLE_OPENMPTARGET)
+namespace {
+
+struct Bar {
+  double val;
+};
+
+struct Foo {
+  Foo(bool allocate = false) : ptr(allocate ? new Bar : nullptr) {}
+  Kokkos::Impl::HostSharedPtr<Bar> ptr;
+  int use_count() { return ptr.use_count(); }
+};
+
+template <class DevMemSpace, class HostMemSpace>
+void host_shared_ptr_test_reference_counting() {
+  using ExecSpace = typename DevMemSpace::execution_space;
+  bool is_gpu =
+      !Kokkos::SpaceAccessibility<ExecSpace, Kokkos::HostSpace>::accessible;
+
+  // Create two tracked instances
+  Foo f1(true), f2(true);
+  // Scope Views
+  {
+    Foo* fp_d_ptr =
+        static_cast<Foo*>(Kokkos::kokkos_malloc<DevMemSpace>(sizeof(Foo)));
+    Kokkos::View<Foo, DevMemSpace> fp_d(fp_d_ptr);
+    // If using UVM or on the CPU don't make an extra HostCopy
+    Foo* fp_h_ptr = std::is_same<DevMemSpace, HostMemSpace>::value
+                        ? fp_d_ptr
+                        : static_cast<Foo*>(
+                              Kokkos::kokkos_malloc<HostMemSpace>(sizeof(Foo)));
+    Kokkos::View<Foo, HostMemSpace> fp_h(fp_h_ptr);
+    ASSERT_EQ(1, f1.use_count());
+    ASSERT_EQ(1, f2.use_count());
+
+    // Just for the sake of it initialize the data of the host copy
+    new (fp_h.data()) Foo();
+    // placement new in kernel
+    //  if on GPU: should not increase use_count, fp_d will not be tracked
+    //  if on Host: refcount will increase fp_d is tracked
+    Kokkos::parallel_for(
+        Kokkos::RangePolicy<ExecSpace>(0, 1),
+        KOKKOS_LAMBDA(int) { new (fp_d.data()) Foo(f1); });
+    Kokkos::fence();
+    Kokkos::deep_copy(fp_h, fp_d);
+
+    if (is_gpu)
+      ASSERT_EQ(1, f1.use_count());
+    else
+      ASSERT_EQ(2, f1.use_count());
+    ASSERT_EQ(1, f2.use_count());
+
+    // assignment operator on host, will increase f2 use_count
+    //   if default device is GPU: fp_h was untracked
+    //   if default device is CPU: fp_h was tracked and use_count was 2 for
+    //   aliasing f1, in which case use_count will be decreased here
+    fp_h() = f2;
+    ASSERT_EQ(1, f1.use_count());
+    ASSERT_EQ(2, f2.use_count());
+
+    Kokkos::deep_copy(fp_d, fp_h);
+    ASSERT_EQ(1, f1.use_count());
+    ASSERT_EQ(2, f2.use_count());
+
+    // assignment in kernel:
+    //  If on GPU: should not increase use_count of f1 and fp_d will not be
+    //  tracked.
+    //  If on Host: use_count will increase of f1, fp_d is tracked,
+    //  use_count of f2 goes down.
+    //  Since we are messing with the use count on the device: make host copy
+    //  untracked first. Note if fp_d and fp_h alias each other (e.g. compiling
+    //  for CPU only) that means fp_d() will be untracked too during assignemnt
+    fp_h() = Foo();
+    Kokkos::parallel_for(
+        Kokkos::RangePolicy<ExecSpace>(0, 1),
+        KOKKOS_LAMBDA(int) { fp_d() = f1; });
+    Kokkos::fence();
+    Kokkos::deep_copy(fp_h, fp_d);
+
+    if (is_gpu)
+      ASSERT_EQ(1, f1.use_count());
+    else
+      ASSERT_EQ(2, f1.use_count());
+    ASSERT_EQ(1, f2.use_count());
+
+    // Assign non-tracked ptr
+    //   if  if_gpu will not change use_count
+    //   if !is_gpu will decrease use_count of f1
+    fp_h() = Foo();
+    ASSERT_EQ(1, f1.use_count());
+    ASSERT_EQ(1, f2.use_count());
+    fp_h() = f2;
+    ASSERT_EQ(1, f1.use_count());
+    ASSERT_EQ(2, f2.use_count());
+
+    // before deleting host version make sure its not tracked
+    fp_h() = Foo();
+    if (fp_h_ptr != fp_d_ptr) Kokkos::kokkos_free<HostMemSpace>(fp_h_ptr);
+    Kokkos::kokkos_free<DevMemSpace>(fp_d_ptr);
+  }
+
+  ASSERT_EQ(1, f1.use_count());
+  ASSERT_EQ(1, f2.use_count());
+}
+}  // namespace
+
+TEST(TEST_CATEGORY, host_shared_ptr_tracking) {
+  host_shared_ptr_test_reference_counting<typename TEST_EXECSPACE::memory_space,
+                                          Kokkos::HostSpace>();
+#ifdef KOKKOS_ENABLE_CUDA
+  if (std::is_same<TEST_EXECSPACE, Kokkos::Cuda>::value)
+    host_shared_ptr_test_reference_counting<Kokkos::CudaUVMSpace,
+                                            Kokkos::CudaUVMSpace>();
+#endif
+#ifdef KOKKOS_ENABLE_SYCL
+  if (std::is_same<TEST_EXECSPACE, Kokkos::Experimental::SYCL>::value)
+    host_shared_ptr_test_reference_counting<
+        Kokkos::Experimental::SYCLSharedUSMSpace,
+        Kokkos::Experimental::SYCLSharedUSMSpace>();
+#endif
+#ifdef KOKKOS_ENABLE_HIP
+  if (std::is_same<TEST_EXECSPACE, Kokkos::Experimental::HIP>::value)
+    host_shared_ptr_test_reference_counting<
+        Kokkos::Experimental::HIPHostPinnedSpace,
+        Kokkos::Experimental::HIPHostPinnedSpace>();
+#endif
+}
+
+#endif  // KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA
diff --git a/packages/kokkos/core/unit_test/TestInterOp.cpp b/packages/kokkos/core/unit_test/TestInterOp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7f08afada9d7c74e6d949949a6f52c2b7e3b7ada
--- /dev/null
+++ b/packages/kokkos/core/unit_test/TestInterOp.cpp
@@ -0,0 +1,162 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#include <Kokkos_Core.hpp>
+#include <Kokkos_DynRankView.hpp>
+#include <KokkosExp_InterOp.hpp>
+
+// View
+static_assert(
+    std::is_same<
+        Kokkos::Experimental::python_view_type_t<Kokkos::View<double*>>,
+        Kokkos::View<
+            double*, typename Kokkos::DefaultExecutionSpace::array_layout,
+            typename Kokkos::DefaultExecutionSpace::memory_space>>::value,
+    "Error! Unexpected python_view_type for: View");
+
+// DynRankView
+static_assert(
+    std::is_same<
+        Kokkos::Experimental::python_view_type_t<Kokkos::DynRankView<double>>,
+        Kokkos::DynRankView<
+            double, typename Kokkos::DefaultExecutionSpace::array_layout,
+            typename Kokkos::DefaultExecutionSpace::memory_space>>::value,
+    "Error! Unexpected python_view_type for: DynRankView");
+
+// View + Execution Space
+static_assert(
+    std::is_same<
+        Kokkos::Experimental::python_view_type_t<
+            Kokkos::View<double*, Kokkos::DefaultExecutionSpace>>,
+        Kokkos::View<
+            double*, typename Kokkos::DefaultExecutionSpace::array_layout,
+            typename Kokkos::DefaultExecutionSpace::memory_space>>::value,
+    "Error! Unexpected python_view_type for: View + Execution Space");
+
+// DynRankView + Execution Space
+static_assert(
+    std::is_same<
+        Kokkos::Experimental::python_view_type_t<
+            Kokkos::DynRankView<double, Kokkos::DefaultExecutionSpace>>,
+        Kokkos::DynRankView<
+            double, typename Kokkos::DefaultExecutionSpace::array_layout,
+            typename Kokkos::DefaultExecutionSpace::memory_space>>::value,
+    "Error! Unexpected python_view_type for: DynRankView + Execution Space");
+
+// View + Memory space
+static_assert(std::is_same<Kokkos::Experimental::python_view_type_t<
+                               Kokkos::View<int64_t*, Kokkos::HostSpace>>,
+                           Kokkos::View<int64_t*, Kokkos::LayoutRight,
+                                        Kokkos::HostSpace>>::value,
+              "Error! Unexpected python_view_type for: View + Memory space");
+
+// DynRankView + Memory space
+static_assert(
+    std::is_same<Kokkos::Experimental::python_view_type_t<
+                     Kokkos::DynRankView<int16_t, Kokkos::HostSpace>>,
+                 Kokkos::DynRankView<int16_t, Kokkos::LayoutRight,
+                                     Kokkos::HostSpace>>::value,
+    "Error! Unexpected python_view_type for: DynRankView + Memory space");
+
+// View + Layout + Execution space
+static_assert(
+    std::is_same<
+        Kokkos::Experimental::python_view_type_t<Kokkos::View<
+            int**, Kokkos::LayoutLeft, Kokkos::DefaultExecutionSpace>>,
+        Kokkos::View<int**, Kokkos::LayoutLeft,
+                     typename Kokkos::DefaultExecutionSpace::memory_space>>::
+        value,
+    "Error! Unexpected python_view_type for: View + Layout + Execution space");
+
+// DynRankView + Layout + Execution space
+static_assert(
+    std::is_same<Kokkos::Experimental::python_view_type_t<Kokkos::DynRankView<
+                     int, Kokkos::LayoutLeft, Kokkos::DefaultExecutionSpace>>,
+                 Kokkos::DynRankView<int, Kokkos::LayoutLeft,
+                                     typename Kokkos::DefaultExecutionSpace::
+                                         memory_space>>::value,
+    "Error! Unexpected python_view_type for: DynRankView + Layout + Execution "
+    "space");
+
+// View + Layout + Memory Space
+static_assert(
+    std::is_same<
+        Kokkos::Experimental::python_view_type_t<
+            Kokkos::View<uint32_t**, Kokkos::LayoutLeft, Kokkos::HostSpace>>,
+        Kokkos::View<uint32_t**, Kokkos::LayoutLeft, Kokkos::HostSpace>>::value,
+    "Error! Unexpected python_view_type for: View + Layout + Memory Space");
+
+// DynRankView + Layout + Memory Space
+static_assert(
+    std::is_same<Kokkos::Experimental::python_view_type_t<Kokkos::DynRankView<
+                     uint64_t, Kokkos::LayoutLeft, Kokkos::HostSpace>>,
+                 Kokkos::DynRankView<uint64_t, Kokkos::LayoutLeft,
+                                     Kokkos::HostSpace>>::value,
+    "Error! Unexpected python_view_type for: DynRankView + Layout + Memory "
+    "Space");
+
+// View + Layout + Execution space + Memory Trait
+static_assert(
+    std::is_same<
+        Kokkos::Experimental::python_view_type_t<Kokkos::View<
+            float***, Kokkos::LayoutLeft, Kokkos::DefaultHostExecutionSpace,
+            Kokkos::MemoryTraits<Kokkos::RandomAccess>>>,
+        Kokkos::View<float***, Kokkos::LayoutLeft,
+                     typename Kokkos::DefaultHostExecutionSpace::memory_space,
+                     Kokkos::MemoryTraits<Kokkos::RandomAccess>>>::value,
+    "Error! Unexpected python_view_type for: View + Layout + Execution space + "
+    "Memory Trait");
+
+// DynRankView + Layout + Execution space  + Memory trait
+static_assert(
+    std::is_same<
+        Kokkos::Experimental::python_view_type_t<Kokkos::DynRankView<
+            float, Kokkos::LayoutLeft, Kokkos::DefaultHostExecutionSpace,
+            Kokkos::MemoryTraits<Kokkos::Atomic>>>,
+        Kokkos::DynRankView<
+            float, Kokkos::LayoutLeft,
+            typename Kokkos::DefaultHostExecutionSpace::memory_space,
+            Kokkos::MemoryTraits<Kokkos::Atomic>>>::value,
+    "Error! Unexpected python_view_type for: DynRankView + Layout + Execution "
+    "space  + Memory trait");
diff --git a/packages/kokkos/core/unit_test/TestMDRange.hpp b/packages/kokkos/core/unit_test/TestMDRange.hpp
index 5618e40989b185a0233de2b20d6dec6636c9fe51..57461be714cde62bdd9b370c834759b91a13da92 100644
--- a/packages/kokkos/core/unit_test/TestMDRange.hpp
+++ b/packages/kokkos/core/unit_test/TestMDRange.hpp
@@ -2751,9 +2751,18 @@ struct TestMDRange_6D {
                            const int N3, const int N4, const int N5) {
 #if defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA)
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<128, 1>, Kokkos::Rank<6>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -2772,9 +2781,18 @@ struct TestMDRange_6D {
 #endif
 
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>, Kokkos::Rank<6>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -2807,9 +2825,18 @@ struct TestMDRange_6D {
 
     // Test with reducers - scalar
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>, Kokkos::Rank<6>,
+          Kokkos::IndexType<int>>;
+#endif
 #ifdef KOKKOS_ENABLE_SYCL
       range_type range({{0, 0, 0, 0, 0, 0}}, {{N0, N1, N2, N3, N4, N5}},
                        {{3, 3, 3, 2, 2, 2}});
@@ -2832,9 +2859,18 @@ struct TestMDRange_6D {
 
     // Test with reducers - scalar + label
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>, Kokkos::Rank<6>,
+          Kokkos::IndexType<int>>;
+#endif
 
 #ifdef KOKKOS_ENABLE_SYCL
       range_type range({{0, 0, 0, 0, 0, 0}}, {{N0, N1, N2, N3, N4, N5}},
@@ -2858,9 +2894,19 @@ struct TestMDRange_6D {
 
     // Test with reducers - scalar view
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type =
+          typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
+                                         Kokkos::IndexType<int>,
+                                         Kokkos::LaunchBounds<512, 1>>;
+#endif
 #ifdef KOKKOS_ENABLE_SYCL
       range_type range({{0, 0, 0, 0, 0, 0}}, {{N0, N1, N2, N3, N4, N5}},
                        {{3, 3, 3, 2, 2, 2}});
@@ -2888,9 +2934,18 @@ struct TestMDRange_6D {
     // Test Min reducer with lambda
 #if defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA)
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<128, 1>, Kokkos::Rank<6>,
+          Kokkos::IndexType<int>>;
+#endif
       range_type range({{1, 1, 1, 1, 1, 1}}, {{N0, N1, N2, N3, N4, N5}},
                        {{3, 3, 3, 2, 2, 1}});
 
@@ -2923,9 +2978,19 @@ struct TestMDRange_6D {
 
     // Tagged operator test
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type = typename Kokkos::MDRangePolicy<
           ExecSpace, Kokkos::Rank<6, Iterate::Default, Iterate::Default>,
           Kokkos::IndexType<int>, InitTag>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>,
+          Kokkos::Rank<6, Iterate::Default, Iterate::Default>,
+          Kokkos::IndexType<int>, InitTag>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -2977,9 +3042,18 @@ struct TestMDRange_6D {
                         const int N4, const int N5) {
 #if defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA)
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<128, 1>, Kokkos::Rank<6>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -3028,8 +3102,16 @@ struct TestMDRange_6D {
 #endif
 
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>, Kokkos::Rank<6>>;
+#endif
       using point_type = typename range_type::point_type;
 
       range_type range(point_type{{0, 0, 0, 0, 0, 0}},
@@ -3062,9 +3144,18 @@ struct TestMDRange_6D {
     }
 
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>, InitTag>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>, Kokkos::Rank<6>,
+          Kokkos::IndexType<int>, InitTag>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -3115,9 +3206,18 @@ struct TestMDRange_6D {
     }
 
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>, Kokkos::Rank<6>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -3158,9 +3258,19 @@ struct TestMDRange_6D {
     }
 
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type = typename Kokkos::MDRangePolicy<
           ExecSpace, Kokkos::Rank<6, Iterate::Default, Iterate::Default>,
           Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>,
+          Kokkos::Rank<6, Iterate::Default, Iterate::Default>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -3201,9 +3311,19 @@ struct TestMDRange_6D {
     }
 
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type = typename Kokkos::MDRangePolicy<
           ExecSpace, Kokkos::Rank<6, Iterate::Left, Iterate::Left>,
           Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>,
+          Kokkos::Rank<6, Iterate::Left, Iterate::Left>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -3244,9 +3364,19 @@ struct TestMDRange_6D {
     }
 
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type = typename Kokkos::MDRangePolicy<
           ExecSpace, Kokkos::Rank<6, Iterate::Left, Iterate::Right>,
           Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>,
+          Kokkos::Rank<6, Iterate::Left, Iterate::Right>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -3287,9 +3417,19 @@ struct TestMDRange_6D {
     }
 
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type = typename Kokkos::MDRangePolicy<
           ExecSpace, Kokkos::Rank<6, Iterate::Right, Iterate::Left>,
           Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>,
+          Kokkos::Rank<6, Iterate::Right, Iterate::Left>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -3330,9 +3470,19 @@ struct TestMDRange_6D {
     }
 
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type = typename Kokkos::MDRangePolicy<
           ExecSpace, Kokkos::Rank<6, Iterate::Right, Iterate::Right>,
           Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<512, 1>,
+          Kokkos::Rank<6, Iterate::Right, Iterate::Right>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
@@ -3683,9 +3833,18 @@ struct TestMDRange_6D_NegIdx {
   static void test_6D_negidx(const int N0, const int N1, const int N2,
                              const int N3, const int N4, const int N5) {
     {
+#if defined(KOKKOS_COMPILER_INTEL)
+      // Launchbounds causes hang with intel compilers
       using range_type =
           typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<6>,
                                          Kokkos::IndexType<int>>;
+#else
+      // Launchbounds to ensure the tile fits into a CUDA block under register
+      // constraints
+      using range_type = typename Kokkos::MDRangePolicy<
+          ExecSpace, Kokkos::LaunchBounds<256, 1>, Kokkos::Rank<6>,
+          Kokkos::IndexType<int>>;
+#endif
       using tile_type  = typename range_type::tile_type;
       using point_type = typename range_type::point_type;
 
diff --git a/packages/kokkos/core/unit_test/TestMathematicalFunctions.hpp b/packages/kokkos/core/unit_test/TestMathematicalFunctions.hpp
index 777f91aea3e560981d5dde05767f1726d8a1542f..b38871afaaf6a277f6080e34f1a81aac31f6fb93 100644
--- a/packages/kokkos/core/unit_test/TestMathematicalFunctions.hpp
+++ b/packages/kokkos/core/unit_test/TestMathematicalFunctions.hpp
@@ -601,7 +601,8 @@ TEST(TEST_CATEGORY, mathematical_functions_power_functions) {
   do_test_math_binary_function<TEST_EXECSPACE, kk_hypot>(2.f, 3.f);
   do_test_math_binary_function<TEST_EXECSPACE, kk_hypot>(2., 3.);
 #ifdef MATHEMATICAL_FUNCTIONS_HAVE_LONG_DOUBLE_OVERLOADS
-#if !(defined(KOKKOS_ARCH_POWER8) || defined(KOKKOS_ARCH_POWER9))  // FIXME
+// FIXME: fails with gcc on Power platforms
+#if !(defined(KOKKOS_ARCH_POWER8) || defined(KOKKOS_ARCH_POWER9))
   do_test_math_binary_function<TEST_EXECSPACE, kk_hypot>(2.l, 3.l);
 #endif
 #endif
@@ -668,7 +669,13 @@ TEST(TEST_CATEGORY, mathematical_functions_exponential_functions) {
   TEST_MATH_FUNCTION(log10)({1234.l, 567.l, 89.l, .003l});
 #endif
 
+// FIXME_OPENMPTARGET FIXME_AMD
+#if defined(KOKKOS_ENABLE_OPENMPTARGET) &&                           \
+    (defined(KOKKOS_ARCH_VEGA906) || defined(KOKKOS_ARCH_VEGA908) || \
+     defined(KOKKOS_ARCH_VEGA90A))
+
   TEST_MATH_FUNCTION(log2)({1, 23, 456, 7890});
+#endif
   TEST_MATH_FUNCTION(log2)({1l, 23l, 456l, 7890l});
   TEST_MATH_FUNCTION(log2)({1ll, 23ll, 456ll, 7890ll});
   TEST_MATH_FUNCTION(log2)({1u, 23u, 456u, 7890u});
@@ -869,3 +876,69 @@ TEST(TEST_CATEGORY,
 #endif
 #endif
 }
+
+template <class Space>
+struct TestAbsoluteValueFunction {
+  TestAbsoluteValueFunction() { run(); }
+  void run() const {
+    int errors = 0;
+    Kokkos::parallel_reduce(Kokkos::RangePolicy<Space>(0, 1), *this, errors);
+    ASSERT_EQ(errors, 0);
+  }
+  KOKKOS_FUNCTION void operator()(int, int& e) const {
+    using Kokkos::Experimental::abs;
+    if (abs(1) != 1 || abs(-1) != 1) {
+      ++e;
+      KOKKOS_IMPL_DO_NOT_USE_PRINTF("failed abs(int)\n");
+    }
+    if (abs(2l) != 2l || abs(-2l) != 2l) {
+      ++e;
+      KOKKOS_IMPL_DO_NOT_USE_PRINTF("failed abs(long int)\n");
+    }
+    if (abs(3ll) != 3ll || abs(-3ll) != 3ll) {
+      ++e;
+      KOKKOS_IMPL_DO_NOT_USE_PRINTF("failed abs(long long int)\n");
+    }
+    if (abs(4.f) != 4.f || abs(-4.f) != 4.f) {
+      ++e;
+      KOKKOS_IMPL_DO_NOT_USE_PRINTF("failed abs(float)\n");
+    }
+    if (abs(5.) != 5. || abs(-5.) != 5.) {
+      ++e;
+      KOKKOS_IMPL_DO_NOT_USE_PRINTF("failed abs(double)\n");
+    }
+#ifdef MATHEMATICAL_FUNCTIONS_HAVE_LONG_DOUBLE_OVERLOADS
+    if (abs(6.l) != 6.l || abs(-6.l) != 6.l) {
+      ++e;
+      KOKKOS_IMPL_DO_NOT_USE_PRINTF("failed abs(long double)\n");
+    }
+#endif
+    // special values
+    using Kokkos::Experimental::isinf;
+    using Kokkos::Experimental::isnan;
+    if (abs(-0.) != 0.
+    // WORKAROUND icpx changing default FP model when optimization level is >= 1
+    // using -fp-model=precise works too
+#ifndef __INTEL_LLVM_COMPILER
+        || !isinf(abs(-INFINITY)) || !isnan(abs(-NAN))
+#endif
+    ) {
+      ++e;
+      KOKKOS_IMPL_DO_NOT_USE_PRINTF(
+          "failed abs(floating_point) special values\n");
+    }
+
+    static_assert(std::is_same<decltype(abs(1)), int>::value, "");
+    static_assert(std::is_same<decltype(abs(2l)), long>::value, "");
+    static_assert(std::is_same<decltype(abs(3ll)), long long>::value, "");
+    static_assert(std::is_same<decltype(abs(4.f)), float>::value, "");
+    static_assert(std::is_same<decltype(abs(5.)), double>::value, "");
+#ifdef MATHEMATICAL_FUNCTIONS_HAVE_LONG_DOUBLE_OVERLOADS
+    static_assert(std::is_same<decltype(abs(6.l)), long double>::value, "");
+#endif
+  }
+};
+
+TEST(TEST_CATEGORY, mathematical_functions_absolute_value) {
+  TestAbsoluteValueFunction<TEST_EXECSPACE>();
+}
diff --git a/packages/kokkos/core/unit_test/TestMathematicalSpecialFunctions.hpp b/packages/kokkos/core/unit_test/TestMathematicalSpecialFunctions.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2d9b4db6bdef50c48a7010d907fb9abf02e05c35
--- /dev/null
+++ b/packages/kokkos/core/unit_test/TestMathematicalSpecialFunctions.hpp
@@ -0,0 +1,1895 @@
+#include <fstream>
+#include <gtest/gtest.h>
+#include "Kokkos_Core.hpp"
+
+namespace Test {
+
+struct TestLargeArgTag {};
+struct TestRealErfcxTag {};
+
+template <class ExecSpace>
+struct TestExponentialIntergral1Function {
+  using ViewType     = Kokkos::View<double*, ExecSpace>;
+  using HostViewType = Kokkos::View<double*, Kokkos::HostSpace>;
+
+  ViewType d_x, d_expint;
+  typename ViewType::HostMirror h_x, h_expint;
+  HostViewType h_ref;
+
+  void testit() {
+    using Kokkos::Experimental::fabs;
+    using Kokkos::Experimental::infinity;
+
+    d_x      = ViewType("d_x", 15);
+    d_expint = ViewType("d_expint", 15);
+    h_x      = Kokkos::create_mirror_view(d_x);
+    h_expint = Kokkos::create_mirror_view(d_expint);
+    h_ref    = HostViewType("h_ref", 15);
+
+    // Generate test inputs
+    h_x(0)  = -0.2;
+    h_x(1)  = 0.0;
+    h_x(2)  = 0.2;
+    h_x(3)  = 0.8;
+    h_x(4)  = 1.6;
+    h_x(5)  = 5.1;
+    h_x(6)  = 0.01;
+    h_x(7)  = 0.001;
+    h_x(8)  = 1.0;
+    h_x(9)  = 1.001;
+    h_x(10) = 1.01;
+    h_x(11) = 1.1;
+    h_x(12) = 7.2;
+    h_x(13) = 10.3;
+    h_x(14) = 15.4;
+    Kokkos::deep_copy(d_x, h_x);
+
+    // Call exponential integral function
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace>(0, 15), *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_expint, d_expint);
+
+    // Reference values computed with Octave
+    h_ref(0)  = -infinity<double>::value;  // x(0)=-0.2
+    h_ref(1)  = infinity<double>::value;   // x(1)= 0.0
+    h_ref(2)  = 1.222650544183893e+00;     // x(2) =0.2
+    h_ref(3)  = 3.105965785455429e-01;     // x(3) =0.8
+    h_ref(4)  = 8.630833369753976e-02;     // x(4) =1.6
+    h_ref(5)  = 1.021300107861738e-03;     // x(5) =5.1
+    h_ref(6)  = 4.037929576538113e+00;     // x(6) =0.01
+    h_ref(7)  = 6.331539364136149e+00;     // x(7) =0.001
+    h_ref(8)  = 2.193839343955205e-01;     // x(8) =1.0
+    h_ref(9)  = 2.190164225274689e-01;     // x(9) =1.001
+    h_ref(10) = 2.157416237944899e-01;     // x(10)=1.01
+    h_ref(11) = 1.859909045360401e-01;     // x(11)=1.1
+    h_ref(12) = 9.218811688716196e-05;     // x(12)=7.2
+    h_ref(13) = 2.996734771597901e-06;     // x(13)=10.3
+    h_ref(14) = 1.254522935050609e-08;     // x(14)=15.4
+
+    EXPECT_EQ(h_ref(0), h_expint(0));
+    EXPECT_EQ(h_ref(1), h_expint(1));
+    for (int i = 2; i < 15; i++) {
+      EXPECT_LE(std::abs(h_expint(i) - h_ref(i)), std::abs(h_ref(i)) * 1e-15);
+    }
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int& i) const {
+    d_expint(i) = Kokkos::Experimental::expint1(d_x(i));
+  }
+};
+
+template <class ExecSpace>
+struct TestComplexErrorFunction {
+  using ViewType = Kokkos::View<Kokkos::complex<double>*, ExecSpace>;
+  using HostViewType =
+      Kokkos::View<Kokkos::complex<double>*, Kokkos::HostSpace>;
+  using DblViewType     = Kokkos::View<double*, ExecSpace>;
+  using DblHostViewType = Kokkos::View<double*, Kokkos::HostSpace>;
+
+  ViewType d_z, d_erf, d_erfcx;
+  typename ViewType::HostMirror h_z, h_erf, h_erfcx;
+  HostViewType h_ref_erf, h_ref_erfcx;
+
+  DblViewType d_x, d_erfcx_dbl;
+  typename DblViewType::HostMirror h_x, h_erfcx_dbl;
+  DblHostViewType h_ref_erfcx_dbl;
+
+  void testit() {
+    using Kokkos::Experimental::infinity;
+
+    d_z         = ViewType("d_z", 52);
+    d_erf       = ViewType("d_erf", 52);
+    d_erfcx     = ViewType("d_erfcx", 52);
+    h_z         = Kokkos::create_mirror_view(d_z);
+    h_erf       = Kokkos::create_mirror_view(d_erf);
+    h_erfcx     = Kokkos::create_mirror_view(d_erfcx);
+    h_ref_erf   = HostViewType("h_ref_erf", 52);
+    h_ref_erfcx = HostViewType("h_ref_erfcx", 52);
+
+    d_x             = DblViewType("d_x", 6);
+    d_erfcx_dbl     = DblViewType("d_erfcx_dbl", 6);
+    h_x             = Kokkos::create_mirror_view(d_x);
+    h_erfcx_dbl     = Kokkos::create_mirror_view(d_erfcx_dbl);
+    h_ref_erfcx_dbl = DblHostViewType("h_ref_erfcx_dbl", 6);
+
+    // Generate test inputs
+    // abs(z)<=2
+    h_z(0)  = Kokkos::complex<double>(0.0011, 0);
+    h_z(1)  = Kokkos::complex<double>(-0.0011, 0);
+    h_z(2)  = Kokkos::complex<double>(1.4567, 0);
+    h_z(3)  = Kokkos::complex<double>(-1.4567, 0);
+    h_z(4)  = Kokkos::complex<double>(0, 0.0011);
+    h_z(5)  = Kokkos::complex<double>(0, -0.0011);
+    h_z(6)  = Kokkos::complex<double>(0, 1.4567);
+    h_z(7)  = Kokkos::complex<double>(0, -1.4567);
+    h_z(8)  = Kokkos::complex<double>(1.4567, 0.0011);
+    h_z(9)  = Kokkos::complex<double>(1.4567, -0.0011);
+    h_z(10) = Kokkos::complex<double>(-1.4567, 0.0011);
+    h_z(11) = Kokkos::complex<double>(-1.4567, -0.0011);
+    h_z(12) = Kokkos::complex<double>(1.4567, 0.5942);
+    h_z(13) = Kokkos::complex<double>(1.4567, -0.5942);
+    h_z(14) = Kokkos::complex<double>(-1.4567, 0.5942);
+    h_z(15) = Kokkos::complex<double>(-1.4567, -0.5942);
+    h_z(16) = Kokkos::complex<double>(0.0011, 0.5942);
+    h_z(17) = Kokkos::complex<double>(0.0011, -0.5942);
+    h_z(18) = Kokkos::complex<double>(-0.0011, 0.5942);
+    h_z(19) = Kokkos::complex<double>(-0.0011, -0.5942);
+    h_z(20) = Kokkos::complex<double>(0.0011, 0.0051);
+    h_z(21) = Kokkos::complex<double>(0.0011, -0.0051);
+    h_z(22) = Kokkos::complex<double>(-0.0011, 0.0051);
+    h_z(23) = Kokkos::complex<double>(-0.0011, -0.0051);
+    // abs(z)>2.0 and x>1
+    h_z(24) = Kokkos::complex<double>(3.5, 0.0011);
+    h_z(25) = Kokkos::complex<double>(3.5, -0.0011);
+    h_z(26) = Kokkos::complex<double>(-3.5, 0.0011);
+    h_z(27) = Kokkos::complex<double>(-3.5, -0.0011);
+    h_z(28) = Kokkos::complex<double>(3.5, 9.7);
+    h_z(29) = Kokkos::complex<double>(3.5, -9.7);
+    h_z(30) = Kokkos::complex<double>(-3.5, 9.7);
+    h_z(31) = Kokkos::complex<double>(-3.5, -9.7);
+    h_z(32) = Kokkos::complex<double>(18.9, 9.7);
+    h_z(33) = Kokkos::complex<double>(18.9, -9.7);
+    h_z(34) = Kokkos::complex<double>(-18.9, 9.7);
+    h_z(35) = Kokkos::complex<double>(-18.9, -9.7);
+    // abs(z)>2.0 and 0<=x<=1 and abs(y)<6
+    h_z(36) = Kokkos::complex<double>(0.85, 3.5);
+    h_z(37) = Kokkos::complex<double>(0.85, -3.5);
+    h_z(38) = Kokkos::complex<double>(-0.85, 3.5);
+    h_z(39) = Kokkos::complex<double>(-0.85, -3.5);
+    h_z(40) = Kokkos::complex<double>(0.0011, 3.5);
+    h_z(41) = Kokkos::complex<double>(0.0011, -3.5);
+    h_z(42) = Kokkos::complex<double>(-0.0011, 3.5);
+    h_z(43) = Kokkos::complex<double>(-0.0011, -3.5);
+    // abs(z)>2.0 and 0<=x<=1 and abs(y)>=6
+    h_z(44) = Kokkos::complex<double>(0.85, 7.5);
+    h_z(45) = Kokkos::complex<double>(0.85, -7.5);
+    h_z(46) = Kokkos::complex<double>(-0.85, 7.5);
+    h_z(47) = Kokkos::complex<double>(-0.85, -7.5);
+    h_z(48) = Kokkos::complex<double>(0.85, 19.7);
+    h_z(49) = Kokkos::complex<double>(0.85, -19.7);
+    h_z(50) = Kokkos::complex<double>(-0.85, 19.7);
+    h_z(51) = Kokkos::complex<double>(-0.85, -19.7);
+
+    h_x(0) = -infinity<double>::value;
+    h_x(1) = -1.2;
+    h_x(2) = 0.0;
+    h_x(3) = 1.2;
+    h_x(4) = 10.5;
+    h_x(5) = infinity<double>::value;
+
+    Kokkos::deep_copy(d_z, h_z);
+    Kokkos::deep_copy(d_x, h_x);
+
+    // Call erf and erfcx functions
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace>(0, 52), *this);
+    Kokkos::fence();
+
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace, TestRealErfcxTag>(0, 1),
+                         *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_erf, d_erf);
+    Kokkos::deep_copy(h_erfcx, d_erfcx);
+    Kokkos::deep_copy(h_erfcx_dbl, d_erfcx_dbl);
+
+    // Reference values computed with Octave
+    h_ref_erf(0) = Kokkos::complex<double>(0.001241216583181022, 0);
+    h_ref_erf(1) = Kokkos::complex<double>(-0.001241216583181022, 0);
+    h_ref_erf(2) = Kokkos::complex<double>(0.9606095744865353, 0);
+    h_ref_erf(3) = Kokkos::complex<double>(-0.9606095744865353, 0);
+    h_ref_erf(4) = Kokkos::complex<double>(0, 0.001241217584429469);
+    h_ref_erf(5) = Kokkos::complex<double>(0, -0.001241217584429469);
+    h_ref_erf(6) = Kokkos::complex<double>(0, 4.149756424218223);
+    h_ref_erf(7) = Kokkos::complex<double>(0, -4.149756424218223);
+    h_ref_erf(8) =
+        Kokkos::complex<double>(0.960609812745064, 0.0001486911741082233);
+    h_ref_erf(9) =
+        Kokkos::complex<double>(0.960609812745064, -0.0001486911741082233);
+    h_ref_erf(10) =
+        Kokkos::complex<double>(-0.960609812745064, 0.0001486911741082233);
+    h_ref_erf(11) =
+        Kokkos::complex<double>(-0.960609812745064, -0.0001486911741082233);
+    h_ref_erf(12) =
+        Kokkos::complex<double>(1.02408827958197, 0.04828570635603527);
+    h_ref_erf(13) =
+        Kokkos::complex<double>(1.02408827958197, -0.04828570635603527);
+    h_ref_erf(14) =
+        Kokkos::complex<double>(-1.02408827958197, 0.04828570635603527);
+    h_ref_erf(15) =
+        Kokkos::complex<double>(-1.02408827958197, -0.04828570635603527);
+    h_ref_erf(16) =
+        Kokkos::complex<double>(0.001766791817179109, 0.7585038120712589);
+    h_ref_erf(17) =
+        Kokkos::complex<double>(0.001766791817179109, -0.7585038120712589);
+    h_ref_erf(18) =
+        Kokkos::complex<double>(-0.001766791817179109, 0.7585038120712589);
+    h_ref_erf(19) =
+        Kokkos::complex<double>(-0.001766791817179109, -0.7585038120712589);
+    h_ref_erf(20) =
+        Kokkos::complex<double>(0.001241248867618165, 0.005754776682713324);
+    h_ref_erf(21) =
+        Kokkos::complex<double>(0.001241248867618165, -0.005754776682713324);
+    h_ref_erf(22) =
+        Kokkos::complex<double>(-0.001241248867618165, 0.005754776682713324);
+    h_ref_erf(23) =
+        Kokkos::complex<double>(-0.001241248867618165, -0.005754776682713324);
+    h_ref_erf(24) =
+        Kokkos::complex<double>(0.9999992569244941, 5.939313159932013e-09);
+    h_ref_erf(25) =
+        Kokkos::complex<double>(0.9999992569244941, -5.939313159932013e-09);
+    h_ref_erf(26) =
+        Kokkos::complex<double>(-0.9999992569244941, 5.939313159932013e-09);
+    h_ref_erf(27) =
+        Kokkos::complex<double>(-0.9999992569244941, -5.939313159932013e-09);
+    h_ref_erf(28) =
+        Kokkos::complex<double>(-1.915595842013002e+34, 1.228821279117683e+32);
+    h_ref_erf(29) =
+        Kokkos::complex<double>(-1.915595842013002e+34, -1.228821279117683e+32);
+    h_ref_erf(30) =
+        Kokkos::complex<double>(1.915595842013002e+34, 1.228821279117683e+32);
+    h_ref_erf(31) =
+        Kokkos::complex<double>(1.915595842013002e+34, -1.228821279117683e+32);
+    h_ref_erf(32) = Kokkos::complex<double>(1, 5.959897539826596e-117);
+    h_ref_erf(33) = Kokkos::complex<double>(1, -5.959897539826596e-117);
+    h_ref_erf(34) = Kokkos::complex<double>(-1, 5.959897539826596e-117);
+    h_ref_erf(35) = Kokkos::complex<double>(-1, -5.959897539826596e-117);
+    h_ref_erf(36) =
+        Kokkos::complex<double>(-9211.077162784413, 13667.93825589455);
+    h_ref_erf(37) =
+        Kokkos::complex<double>(-9211.077162784413, -13667.93825589455);
+    h_ref_erf(38) =
+        Kokkos::complex<double>(9211.077162784413, 13667.93825589455);
+    h_ref_erf(39) =
+        Kokkos::complex<double>(9211.077162784413, -13667.93825589455);
+    h_ref_erf(40) = Kokkos::complex<double>(259.38847811225, 35281.28906479814);
+    h_ref_erf(41) =
+        Kokkos::complex<double>(259.38847811225, -35281.28906479814);
+    h_ref_erf(42) =
+        Kokkos::complex<double>(-259.38847811225, 35281.28906479814);
+    h_ref_erf(43) =
+        Kokkos::complex<double>(-259.38847811225, -35281.28906479814);
+    h_ref_erf(44) =
+        Kokkos::complex<double>(6.752085728270252e+21, 9.809477366939276e+22);
+    h_ref_erf(45) =
+        Kokkos::complex<double>(6.752085728270252e+21, -9.809477366939276e+22);
+    h_ref_erf(46) =
+        Kokkos::complex<double>(-6.752085728270252e+21, 9.809477366939276e+22);
+    h_ref_erf(47) =
+        Kokkos::complex<double>(-6.752085728270252e+21, -9.809477366939276e+22);
+    h_ref_erf(48) =
+        Kokkos::complex<double>(4.37526734926942e+166, -2.16796709605852e+166);
+    h_ref_erf(49) =
+        Kokkos::complex<double>(4.37526734926942e+166, 2.16796709605852e+166);
+    h_ref_erf(50) =
+        Kokkos::complex<double>(-4.37526734926942e+166, -2.16796709605852e+166);
+    h_ref_erf(51) =
+        Kokkos::complex<double>(-4.37526734926942e+166, 2.16796709605852e+166);
+
+    h_ref_erfcx(0) = Kokkos::complex<double>(0.9987599919156778, 0);
+    h_ref_erfcx(1) = Kokkos::complex<double>(1.001242428085786, 0);
+    h_ref_erfcx(2) = Kokkos::complex<double>(0.3288157848563544, 0);
+    h_ref_erfcx(3) = Kokkos::complex<double>(16.36639786516915, 0);
+    h_ref_erfcx(4) =
+        Kokkos::complex<double>(0.999998790000732, -0.001241216082557101);
+    h_ref_erfcx(5) =
+        Kokkos::complex<double>(0.999998790000732, 0.001241216082557101);
+    h_ref_erfcx(6) =
+        Kokkos::complex<double>(0.1197948131677216, -0.4971192955307743);
+    h_ref_erfcx(7) =
+        Kokkos::complex<double>(0.1197948131677216, 0.4971192955307743);
+    h_ref_erfcx(8) =
+        Kokkos::complex<double>(0.3288156873503045, -0.0001874479383970247);
+    h_ref_erfcx(9) =
+        Kokkos::complex<double>(0.3288156873503045, 0.0001874479383970247);
+    h_ref_erfcx(10) =
+        Kokkos::complex<double>(16.36629202874158, -0.05369111060785572);
+    h_ref_erfcx(11) =
+        Kokkos::complex<double>(16.36629202874158, 0.05369111060785572);
+    h_ref_erfcx(12) =
+        Kokkos::complex<double>(0.3020886508118801, -0.09424097887578842);
+    h_ref_erfcx(13) =
+        Kokkos::complex<double>(0.3020886508118801, 0.09424097887578842);
+    h_ref_erfcx(14) =
+        Kokkos::complex<double>(-2.174707722732267, -11.67259764091796);
+    h_ref_erfcx(15) =
+        Kokkos::complex<double>(-2.174707722732267, 11.67259764091796);
+    h_ref_erfcx(16) =
+        Kokkos::complex<double>(0.7019810779371267, -0.5319516793968513);
+    h_ref_erfcx(17) =
+        Kokkos::complex<double>(0.7019810779371267, 0.5319516793968513);
+    h_ref_erfcx(18) =
+        Kokkos::complex<double>(0.7030703366403597, -0.5337884198542978);
+    h_ref_erfcx(19) =
+        Kokkos::complex<double>(0.7030703366403597, 0.5337884198542978);
+    h_ref_erfcx(20) =
+        Kokkos::complex<double>(0.9987340467266177, -0.005743428170378673);
+    h_ref_erfcx(21) =
+        Kokkos::complex<double>(0.9987340467266177, 0.005743428170378673);
+    h_ref_erfcx(22) =
+        Kokkos::complex<double>(1.001216353762532, -0.005765867613873103);
+    h_ref_erfcx(23) =
+        Kokkos::complex<double>(1.001216353762532, 0.005765867613873103);
+    h_ref_erfcx(24) =
+        Kokkos::complex<double>(0.1552936427089241, -4.545593205871305e-05);
+    h_ref_erfcx(25) =
+        Kokkos::complex<double>(0.1552936427089241, 4.545593205871305e-05);
+    h_ref_erfcx(26) =
+        Kokkos::complex<double>(417949.5262869648, -3218.276197742372);
+    h_ref_erfcx(27) =
+        Kokkos::complex<double>(417949.5262869648, 3218.276197742372);
+    h_ref_erfcx(28) =
+        Kokkos::complex<double>(0.01879467905925653, -0.0515934271478583);
+    h_ref_erfcx(29) =
+        Kokkos::complex<double>(0.01879467905925653, 0.0515934271478583);
+    h_ref_erfcx(30) =
+        Kokkos::complex<double>(-0.01879467905925653, -0.0515934271478583);
+    h_ref_erfcx(31) =
+        Kokkos::complex<double>(-0.01879467905925653, 0.0515934271478583);
+    h_ref_erfcx(32) =
+        Kokkos::complex<double>(0.02362328821805, -0.01209735551897239);
+    h_ref_erfcx(33) =
+        Kokkos::complex<double>(0.02362328821805, 0.01209735551897239);
+    h_ref_erfcx(34) = Kokkos::complex<double>(-2.304726099084567e+114,
+                                              -2.942443198107089e+114);
+    h_ref_erfcx(35) = Kokkos::complex<double>(-2.304726099084567e+114,
+                                              2.942443198107089e+114);
+    h_ref_erfcx(36) =
+        Kokkos::complex<double>(0.04174017523145063, -0.1569865319886248);
+    h_ref_erfcx(37) =
+        Kokkos::complex<double>(0.04174017523145063, 0.1569865319886248);
+    h_ref_erfcx(38) =
+        Kokkos::complex<double>(-0.04172154858670504, -0.156980085534407);
+    h_ref_erfcx(39) =
+        Kokkos::complex<double>(-0.04172154858670504, 0.156980085534407);
+    h_ref_erfcx(40) =
+        Kokkos::complex<double>(6.355803055239174e-05, -0.1688298297427782);
+    h_ref_erfcx(41) =
+        Kokkos::complex<double>(6.355803055239174e-05, 0.1688298297427782);
+    h_ref_erfcx(42) =
+        Kokkos::complex<double>(-5.398806789669434e-05, -0.168829903432947);
+    h_ref_erfcx(43) =
+        Kokkos::complex<double>(-5.398806789669434e-05, 0.168829903432947);
+    h_ref_erfcx(44) =
+        Kokkos::complex<double>(0.008645103282302355, -0.07490521021566741);
+    h_ref_erfcx(45) =
+        Kokkos::complex<double>(0.008645103282302355, 0.07490521021566741);
+    h_ref_erfcx(46) =
+        Kokkos::complex<double>(-0.008645103282302355, -0.07490521021566741);
+    h_ref_erfcx(47) =
+        Kokkos::complex<double>(-0.008645103282302355, 0.07490521021566741);
+    h_ref_erfcx(48) =
+        Kokkos::complex<double>(0.001238176693606428, -0.02862247416909219);
+    h_ref_erfcx(49) =
+        Kokkos::complex<double>(0.001238176693606428, 0.02862247416909219);
+    h_ref_erfcx(50) =
+        Kokkos::complex<double>(-0.001238176693606428, -0.02862247416909219);
+    h_ref_erfcx(51) =
+        Kokkos::complex<double>(-0.001238176693606428, 0.02862247416909219);
+
+    h_ref_erfcx_dbl(0) = infinity<double>::value;
+    h_ref_erfcx_dbl(1) = 8.062854217063865e+00;
+    h_ref_erfcx_dbl(2) = 1.0;
+    h_ref_erfcx_dbl(3) = 3.785374169292397e-01;
+    h_ref_erfcx_dbl(4) = 5.349189974656411e-02;
+    h_ref_erfcx_dbl(5) = 0.0;
+
+    for (int i = 0; i < 52; i++) {
+      EXPECT_LE(Kokkos::abs(h_erf(i) - h_ref_erf(i)),
+                Kokkos::abs(h_ref_erf(i)) * 1e-13);
+    }
+
+    for (int i = 0; i < 52; i++) {
+      EXPECT_LE(Kokkos::abs(h_erfcx(i) - h_ref_erfcx(i)),
+                Kokkos::abs(h_ref_erfcx(i)) * 1e-13);
+    }
+
+    EXPECT_EQ(h_erfcx_dbl(0), h_ref_erfcx_dbl(0));
+    EXPECT_EQ(h_erfcx_dbl(5), h_ref_erfcx_dbl(5));
+    for (int i = 1; i < 5; i++) {
+      EXPECT_LE(std::abs(h_erfcx_dbl(i) - h_ref_erfcx_dbl(i)),
+                std::abs(h_ref_erfcx_dbl(i)) * 1e-13);
+    }
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int& i) const {
+    d_erf(i)   = Kokkos::Experimental::erf(d_z(i));
+    d_erfcx(i) = Kokkos::Experimental::erfcx(d_z(i));
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const TestRealErfcxTag&, const int& /*i*/) const {
+    d_erfcx_dbl(0) = Kokkos::Experimental::erfcx(d_x(0));
+    d_erfcx_dbl(1) = Kokkos::Experimental::erfcx(d_x(1));
+    d_erfcx_dbl(2) = Kokkos::Experimental::erfcx(d_x(2));
+    d_erfcx_dbl(3) = Kokkos::Experimental::erfcx(d_x(3));
+    d_erfcx_dbl(4) = Kokkos::Experimental::erfcx(d_x(4));
+    d_erfcx_dbl(5) = Kokkos::Experimental::erfcx(d_x(5));
+  }
+};
+
+template <class ExecSpace>
+struct TestComplexBesselJ0Y0Function {
+  using ViewType = Kokkos::View<Kokkos::complex<double>*, ExecSpace>;
+  using HostViewType =
+      Kokkos::View<Kokkos::complex<double>*, Kokkos::HostSpace>;
+
+  ViewType d_z, d_cbj0, d_cby0;
+  typename ViewType::HostMirror h_z, h_cbj0, h_cby0;
+  HostViewType h_ref_cbj0, h_ref_cby0;
+
+  ViewType d_z_large, d_cbj0_large, d_cby0_large;
+  typename ViewType::HostMirror h_z_large, h_cbj0_large, h_cby0_large;
+  HostViewType h_ref_cbj0_large, h_ref_cby0_large;
+
+  void testit() {
+    using Kokkos::Experimental::infinity;
+
+    int N      = 25;
+    d_z        = ViewType("d_z", N);
+    d_cbj0     = ViewType("d_cbj0", N);
+    d_cby0     = ViewType("d_cby0", N);
+    h_z        = Kokkos::create_mirror_view(d_z);
+    h_cbj0     = Kokkos::create_mirror_view(d_cbj0);
+    h_cby0     = Kokkos::create_mirror_view(d_cby0);
+    h_ref_cbj0 = HostViewType("h_ref_cbj0", N);
+    h_ref_cby0 = HostViewType("h_ref_cby0", N);
+
+    // Generate test inputs
+    h_z(0) = Kokkos::complex<double>(0.0, 0.0);
+    // abs(z)<=25
+    h_z(1)  = Kokkos::complex<double>(3.0, 2.0);
+    h_z(2)  = Kokkos::complex<double>(3.0, -2.0);
+    h_z(3)  = Kokkos::complex<double>(-3.0, 2.0);
+    h_z(4)  = Kokkos::complex<double>(-3.0, -2.0);
+    h_z(5)  = Kokkos::complex<double>(23.0, 10.0);
+    h_z(6)  = Kokkos::complex<double>(23.0, -10.0);
+    h_z(7)  = Kokkos::complex<double>(-23.0, 10.0);
+    h_z(8)  = Kokkos::complex<double>(-23.0, -10.0);
+    h_z(9)  = Kokkos::complex<double>(3.0, 0.0);
+    h_z(10) = Kokkos::complex<double>(-3.0, 0.0);
+    h_z(11) = Kokkos::complex<double>(23.0, 0.0);
+    h_z(12) = Kokkos::complex<double>(-23.0, 0.0);
+    // abs(z)>25
+    h_z(13) = Kokkos::complex<double>(28.0, 10.0);
+    h_z(14) = Kokkos::complex<double>(28.0, -10.0);
+    h_z(15) = Kokkos::complex<double>(-28.0, 10.0);
+    h_z(16) = Kokkos::complex<double>(-28.0, -10.0);
+    h_z(17) = Kokkos::complex<double>(60.0, 10.0);
+    h_z(18) = Kokkos::complex<double>(60.0, -10.0);
+    h_z(19) = Kokkos::complex<double>(-60.0, 10.0);
+    h_z(20) = Kokkos::complex<double>(-60.0, -10.0);
+    h_z(21) = Kokkos::complex<double>(28.0, 0.0);
+    h_z(22) = Kokkos::complex<double>(-28.0, 0.0);
+    h_z(23) = Kokkos::complex<double>(60.0, 0.0);
+    h_z(24) = Kokkos::complex<double>(-60.0, 0.0);
+
+    Kokkos::deep_copy(d_z, h_z);
+
+    // Call Bessel functions
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace>(0, N), *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_cbj0, d_cbj0);
+    Kokkos::deep_copy(h_cby0, d_cby0);
+
+    // Reference values computed with Octave
+    h_ref_cbj0(0) = Kokkos::complex<double>(1.000000000000000e+00, 0);
+    h_ref_cbj0(1) =
+        Kokkos::complex<double>(-1.249234879607422e+00, -9.479837920577351e-01);
+    h_ref_cbj0(2) =
+        Kokkos::complex<double>(-1.249234879607422e+00, +9.479837920577351e-01);
+    h_ref_cbj0(3) =
+        Kokkos::complex<double>(-1.249234879607422e+00, +9.479837920577351e-01);
+    h_ref_cbj0(4) =
+        Kokkos::complex<double>(-1.249234879607422e+00, -9.479837920577351e-01);
+    h_ref_cbj0(5) =
+        Kokkos::complex<double>(-1.602439981218195e+03, +7.230667451989807e+02);
+    h_ref_cbj0(6) =
+        Kokkos::complex<double>(-1.602439981218195e+03, -7.230667451989807e+02);
+    h_ref_cbj0(7) =
+        Kokkos::complex<double>(-1.602439981218195e+03, -7.230667451989807e+02);
+    h_ref_cbj0(8) =
+        Kokkos::complex<double>(-1.602439981218195e+03, +7.230667451989807e+02);
+    h_ref_cbj0(9) = Kokkos::complex<double>(-2.600519549019335e-01, 0);
+    h_ref_cbj0(10) =
+        Kokkos::complex<double>(-2.600519549019335e-01, +9.951051106466461e-18);
+    h_ref_cbj0(11) = Kokkos::complex<double>(-1.624127813134866e-01, 0);
+    h_ref_cbj0(12) =
+        Kokkos::complex<double>(-1.624127813134866e-01, -1.387778780781446e-17);
+    h_ref_cbj0(13) =
+        Kokkos::complex<double>(-1.012912188513958e+03, -1.256239636146142e+03);
+    h_ref_cbj0(14) =
+        Kokkos::complex<double>(-1.012912188513958e+03, +1.256239636146142e+03);
+    h_ref_cbj0(15) =
+        Kokkos::complex<double>(-1.012912188513958e+03, +1.256239636146142e+03);
+    h_ref_cbj0(16) =
+        Kokkos::complex<double>(-1.012912188513958e+03, -1.256239636146142e+03);
+    h_ref_cbj0(17) =
+        Kokkos::complex<double>(-1.040215134669324e+03, -4.338202386810095e+02);
+    h_ref_cbj0(18) =
+        Kokkos::complex<double>(-1.040215134669324e+03, +4.338202386810095e+02);
+    h_ref_cbj0(19) =
+        Kokkos::complex<double>(-1.040215134669324e+03, +4.338202386810095e+02);
+    h_ref_cbj0(20) =
+        Kokkos::complex<double>(-1.040215134669324e+03, -4.338202386810095e+02);
+    h_ref_cbj0(21) = Kokkos::complex<double>(-7.315701054899962e-02, 0);
+    h_ref_cbj0(22) =
+        Kokkos::complex<double>(-7.315701054899962e-02, -6.938893903907228e-18);
+    h_ref_cbj0(23) = Kokkos::complex<double>(-9.147180408906189e-02, 0);
+    h_ref_cbj0(24) =
+        Kokkos::complex<double>(-9.147180408906189e-02, +1.387778780781446e-17);
+
+    h_ref_cby0(0) = Kokkos::complex<double>(-infinity<double>::value, 0);
+    h_ref_cby0(1) =
+        Kokkos::complex<double>(1.000803196554890e+00, -1.231441609303427e+00);
+    h_ref_cby0(2) =
+        Kokkos::complex<double>(1.000803196554890e+00, +1.231441609303427e+00);
+    h_ref_cby0(3) =
+        Kokkos::complex<double>(-8.951643875605797e-01, -1.267028149911417e+00);
+    h_ref_cby0(4) =
+        Kokkos::complex<double>(-8.951643875605797e-01, +1.267028149911417e+00);
+    h_ref_cby0(5) =
+        Kokkos::complex<double>(-7.230667452992603e+02, -1.602439974000479e+03);
+    h_ref_cby0(6) =
+        Kokkos::complex<double>(-7.230667452992603e+02, +1.602439974000479e+03);
+    h_ref_cby0(7) =
+        Kokkos::complex<double>(7.230667450987011e+02, -1.602439988435912e+03);
+    h_ref_cby0(8) =
+        Kokkos::complex<double>(7.230667450987011e+02, +1.602439988435912e+03);
+    h_ref_cby0(9) = Kokkos::complex<double>(3.768500100127903e-01, 0);
+    h_ref_cby0(10) =
+        Kokkos::complex<double>(3.768500100127903e-01, -5.201039098038670e-01);
+    h_ref_cby0(11) = Kokkos::complex<double>(-3.598179027370283e-02, 0);
+    h_ref_cby0(12) =
+        Kokkos::complex<double>(-3.598179027370282e-02, -3.248255626269732e-01);
+    h_ref_cby0(13) =
+        Kokkos::complex<double>(1.256239642409530e+03, -1.012912186329053e+03);
+    h_ref_cby0(14) =
+        Kokkos::complex<double>(1.256239642409530e+03, +1.012912186329053e+03);
+    h_ref_cby0(15) =
+        Kokkos::complex<double>(-1.256239629882755e+03, -1.012912190698863e+03);
+    h_ref_cby0(16) =
+        Kokkos::complex<double>(-1.256239629882755e+03, +1.012912190698863e+03);
+    h_ref_cby0(17) =
+        Kokkos::complex<double>(4.338202411482646e+02, -1.040215130736213e+03);
+    h_ref_cby0(18) =
+        Kokkos::complex<double>(4.338202411482646e+02, +1.040215130736213e+03);
+    h_ref_cby0(19) =
+        Kokkos::complex<double>(-4.338202362137545e+02, -1.040215138602435e+03);
+    h_ref_cby0(20) =
+        Kokkos::complex<double>(-4.338202362137545e+02, +1.040215138602435e+03);
+    h_ref_cby0(21) = Kokkos::complex<double>(1.318364704235323e-01, 0);
+    h_ref_cby0(22) =
+        Kokkos::complex<double>(1.318364704235323e-01, -1.463140210979992e-01);
+    h_ref_cby0(23) = Kokkos::complex<double>(4.735895220944939e-02, 0);
+    h_ref_cby0(24) =
+        Kokkos::complex<double>(4.735895220944938e-02, -1.829436081781237e-01);
+
+    for (int i = 0; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_cbj0(i) - h_ref_cbj0(i)),
+                Kokkos::abs(h_ref_cbj0(i)) * 1e-13);
+    }
+
+    EXPECT_EQ(h_ref_cby0(0), h_cby0(0));
+    for (int i = 1; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_cby0(i) - h_ref_cby0(i)),
+                Kokkos::abs(h_ref_cby0(i)) * 1e-13);
+    }
+
+    ////Test large arguments
+    d_z_large        = ViewType("d_z_large", 6);
+    d_cbj0_large     = ViewType("d_cbj0_large", 6);
+    d_cby0_large     = ViewType("d_cby0_large", 6);
+    h_z_large        = Kokkos::create_mirror_view(d_z_large);
+    h_cbj0_large     = Kokkos::create_mirror_view(d_cbj0_large);
+    h_cby0_large     = Kokkos::create_mirror_view(d_cby0_large);
+    h_ref_cbj0_large = HostViewType("h_ref_cbj0_large", 2);
+    h_ref_cby0_large = HostViewType("h_ref_cby0_large", 2);
+
+    h_z_large(0) = Kokkos::complex<double>(10000.0, 100.0);
+    h_z_large(1) = Kokkos::complex<double>(10000.0, 100.0);
+    h_z_large(2) = Kokkos::complex<double>(10000.0, 100.0);
+    h_z_large(3) = Kokkos::complex<double>(-10000.0, 100.0);
+    h_z_large(4) = Kokkos::complex<double>(-10000.0, 100.0);
+    h_z_large(5) = Kokkos::complex<double>(-10000.0, 100.0);
+
+    Kokkos::deep_copy(d_z_large, h_z_large);
+
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace, TestLargeArgTag>(0, 1),
+                         *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_cbj0_large, d_cbj0_large);
+    Kokkos::deep_copy(h_cby0_large, d_cby0_large);
+
+    h_ref_cbj0_large(0) =
+        Kokkos::complex<double>(-9.561811498244175e+40, -4.854995782103029e+40);
+    h_ref_cbj0_large(1) =
+        Kokkos::complex<double>(-9.561811498244175e+40, +4.854995782103029e+40);
+
+    h_ref_cby0_large(0) =
+        Kokkos::complex<double>(4.854995782103029e+40, -9.561811498244175e+40);
+    h_ref_cby0_large(1) =
+        Kokkos::complex<double>(-4.854995782103029e+40, -9.561811498244175e+40);
+
+    EXPECT_TRUE((Kokkos::abs(h_cbj0_large(0) - h_ref_cbj0_large(0)) <
+                 Kokkos::abs(h_ref_cbj0_large(0)) * 1e-12) &&
+                (Kokkos::abs(h_cbj0_large(0) - h_ref_cbj0_large(0)) >
+                 Kokkos::abs(h_ref_cbj0_large(0)) * 1e-13));
+    EXPECT_TRUE(Kokkos::abs(h_cbj0_large(1) - h_ref_cbj0_large(0)) >
+                Kokkos::abs(h_ref_cbj0_large(0)) * 1e-6);
+    EXPECT_TRUE(Kokkos::abs(h_cbj0_large(2) - h_ref_cbj0_large(0)) <
+                Kokkos::abs(h_ref_cbj0_large(0)) * 1e-13);
+    EXPECT_TRUE((Kokkos::abs(h_cbj0_large(3) - h_ref_cbj0_large(1)) <
+                 Kokkos::abs(h_ref_cbj0_large(1)) * 1e-12) &&
+                (Kokkos::abs(h_cbj0_large(3) - h_ref_cbj0_large(1)) >
+                 Kokkos::abs(h_ref_cbj0_large(1)) * 1e-13));
+    EXPECT_TRUE(Kokkos::abs(h_cbj0_large(4) - h_ref_cbj0_large(1)) >
+                Kokkos::abs(h_ref_cbj0_large(1)) * 1e-6);
+    EXPECT_TRUE(Kokkos::abs(h_cbj0_large(5) - h_ref_cbj0_large(1)) <
+                Kokkos::abs(h_ref_cbj0_large(1)) * 1e-13);
+
+    EXPECT_TRUE((Kokkos::abs(h_cby0_large(0) - h_ref_cby0_large(0)) <
+                 Kokkos::abs(h_ref_cby0_large(0)) * 1e-12) &&
+                (Kokkos::abs(h_cby0_large(0) - h_ref_cby0_large(0)) >
+                 Kokkos::abs(h_ref_cby0_large(0)) * 1e-13));
+    EXPECT_TRUE(Kokkos::abs(h_cby0_large(1) - h_ref_cby0_large(0)) >
+                Kokkos::abs(h_ref_cby0_large(0)) * 1e-6);
+    EXPECT_TRUE(Kokkos::abs(h_cby0_large(2) - h_ref_cby0_large(0)) <
+                Kokkos::abs(h_ref_cby0_large(0)) * 1e-13);
+    EXPECT_TRUE((Kokkos::abs(h_cby0_large(3) - h_ref_cby0_large(1)) <
+                 Kokkos::abs(h_ref_cby0_large(1)) * 1e-12) &&
+                (Kokkos::abs(h_cby0_large(3) - h_ref_cby0_large(1)) >
+                 Kokkos::abs(h_ref_cby0_large(1)) * 1e-13));
+    EXPECT_TRUE(Kokkos::abs(h_cby0_large(4) - h_ref_cby0_large(1)) >
+                Kokkos::abs(h_ref_cby0_large(1)) * 1e-6);
+    EXPECT_TRUE(Kokkos::abs(h_cby0_large(5) - h_ref_cby0_large(1)) <
+                Kokkos::abs(h_ref_cby0_large(1)) * 1e-13);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int& i) const {
+    d_cbj0(i) = Kokkos::Experimental::cyl_bessel_j0<Kokkos::complex<double>,
+                                                    double, int>(d_z(i));
+    d_cby0(i) = Kokkos::Experimental::cyl_bessel_y0<Kokkos::complex<double>,
+                                                    double, int>(d_z(i));
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const TestLargeArgTag&, const int& /*i*/) const {
+    d_cbj0_large(0) =
+        Kokkos::Experimental::cyl_bessel_j0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(0));
+    d_cbj0_large(1) =
+        Kokkos::Experimental::cyl_bessel_j0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(1), 11000, 3000);
+    d_cbj0_large(2) =
+        Kokkos::Experimental::cyl_bessel_j0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(2), 11000, 7500);
+    d_cbj0_large(3) =
+        Kokkos::Experimental::cyl_bessel_j0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(3));
+    d_cbj0_large(4) =
+        Kokkos::Experimental::cyl_bessel_j0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(4), 11000, 3000);
+    d_cbj0_large(5) =
+        Kokkos::Experimental::cyl_bessel_j0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(5), 11000, 7500);
+
+    d_cby0_large(0) =
+        Kokkos::Experimental::cyl_bessel_y0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(0));
+    d_cby0_large(1) =
+        Kokkos::Experimental::cyl_bessel_y0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(1), 11000, 3000);
+    d_cby0_large(2) =
+        Kokkos::Experimental::cyl_bessel_y0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(2), 11000, 7500);
+    d_cby0_large(3) =
+        Kokkos::Experimental::cyl_bessel_y0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(3));
+    d_cby0_large(4) =
+        Kokkos::Experimental::cyl_bessel_y0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(4), 11000, 3000);
+    d_cby0_large(5) =
+        Kokkos::Experimental::cyl_bessel_y0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(5), 11000, 7500);
+  }
+};
+
+template <class ExecSpace>
+struct TestComplexBesselJ1Y1Function {
+  using ViewType = Kokkos::View<Kokkos::complex<double>*, ExecSpace>;
+  using HostViewType =
+      Kokkos::View<Kokkos::complex<double>*, Kokkos::HostSpace>;
+
+  ViewType d_z, d_cbj1, d_cby1;
+  typename ViewType::HostMirror h_z, h_cbj1, h_cby1;
+  HostViewType h_ref_cbj1, h_ref_cby1;
+
+  ViewType d_z_large, d_cbj1_large, d_cby1_large;
+  typename ViewType::HostMirror h_z_large, h_cbj1_large, h_cby1_large;
+  HostViewType h_ref_cbj1_large, h_ref_cby1_large;
+
+  void testit() {
+    using Kokkos::Experimental::infinity;
+
+    int N      = 25;
+    d_z        = ViewType("d_z", N);
+    d_cbj1     = ViewType("d_cbj1", N);
+    d_cby1     = ViewType("d_cby1", N);
+    h_z        = Kokkos::create_mirror_view(d_z);
+    h_cbj1     = Kokkos::create_mirror_view(d_cbj1);
+    h_cby1     = Kokkos::create_mirror_view(d_cby1);
+    h_ref_cbj1 = HostViewType("h_ref_cbj1", N);
+    h_ref_cby1 = HostViewType("h_ref_cby1", N);
+
+    // Generate test inputs
+    h_z(0) = Kokkos::complex<double>(0.0, 0.0);
+    // abs(z)<=25
+    h_z(1)  = Kokkos::complex<double>(3.0, 2.0);
+    h_z(2)  = Kokkos::complex<double>(3.0, -2.0);
+    h_z(3)  = Kokkos::complex<double>(-3.0, 2.0);
+    h_z(4)  = Kokkos::complex<double>(-3.0, -2.0);
+    h_z(5)  = Kokkos::complex<double>(23.0, 10.0);
+    h_z(6)  = Kokkos::complex<double>(23.0, -10.0);
+    h_z(7)  = Kokkos::complex<double>(-23.0, 10.0);
+    h_z(8)  = Kokkos::complex<double>(-23.0, -10.0);
+    h_z(9)  = Kokkos::complex<double>(3.0, 0.0);
+    h_z(10) = Kokkos::complex<double>(-3.0, 0.0);
+    h_z(11) = Kokkos::complex<double>(23.0, 0.0);
+    h_z(12) = Kokkos::complex<double>(-23.0, 0.0);
+    // abs(z)>25
+    h_z(13) = Kokkos::complex<double>(28.0, 10.0);
+    h_z(14) = Kokkos::complex<double>(28.0, -10.0);
+    h_z(15) = Kokkos::complex<double>(-28.0, 10.0);
+    h_z(16) = Kokkos::complex<double>(-28.0, -10.0);
+    h_z(17) = Kokkos::complex<double>(60.0, 10.0);
+    h_z(18) = Kokkos::complex<double>(60.0, -10.0);
+    h_z(19) = Kokkos::complex<double>(-60.0, 10.0);
+    h_z(20) = Kokkos::complex<double>(-60.0, -10.0);
+    h_z(21) = Kokkos::complex<double>(28.0, 0.0);
+    h_z(22) = Kokkos::complex<double>(-28.0, 0.0);
+    h_z(23) = Kokkos::complex<double>(60.0, 0.0);
+    h_z(24) = Kokkos::complex<double>(-60.0, 0.0);
+
+    Kokkos::deep_copy(d_z, h_z);
+
+    // Call Bessel functions
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace>(0, N), *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_cbj1, d_cbj1);
+    Kokkos::deep_copy(h_cby1, d_cby1);
+
+    // Reference values computed with Octave
+    h_ref_cbj1(0) = Kokkos::complex<double>(0, 0);
+    h_ref_cbj1(1) =
+        Kokkos::complex<double>(7.801488485792540e-01, -1.260982060238848e+00);
+    h_ref_cbj1(2) =
+        Kokkos::complex<double>(7.801488485792540e-01, +1.260982060238848e+00);
+    h_ref_cbj1(3) =
+        Kokkos::complex<double>(-7.801488485792543e-01, -1.260982060238848e+00);
+    h_ref_cbj1(4) =
+        Kokkos::complex<double>(-7.801488485792543e-01, +1.260982060238848e+00);
+    h_ref_cbj1(5) =
+        Kokkos::complex<double>(-7.469476253429664e+02, -1.576608505254311e+03);
+    h_ref_cbj1(6) =
+        Kokkos::complex<double>(-7.469476253429664e+02, +1.576608505254311e+03);
+    h_ref_cbj1(7) =
+        Kokkos::complex<double>(7.469476253429661e+02, -1.576608505254311e+03);
+    h_ref_cbj1(8) =
+        Kokkos::complex<double>(7.469476253429661e+02, +1.576608505254311e+03);
+    h_ref_cbj1(9) = Kokkos::complex<double>(3.390589585259365e-01, 0);
+    h_ref_cbj1(10) =
+        Kokkos::complex<double>(-3.390589585259365e-01, +3.373499138396203e-17);
+    h_ref_cbj1(11) = Kokkos::complex<double>(-3.951932188370151e-02, 0);
+    h_ref_cbj1(12) =
+        Kokkos::complex<double>(3.951932188370151e-02, +7.988560221984213e-18);
+    h_ref_cbj1(13) =
+        Kokkos::complex<double>(1.233147100257312e+03, -1.027302265904111e+03);
+    h_ref_cbj1(14) =
+        Kokkos::complex<double>(1.233147100257312e+03, +1.027302265904111e+03);
+    h_ref_cbj1(15) =
+        Kokkos::complex<double>(-1.233147100257312e+03, -1.027302265904111e+03);
+    h_ref_cbj1(16) =
+        Kokkos::complex<double>(-1.233147100257312e+03, +1.027302265904111e+03);
+    h_ref_cbj1(17) =
+        Kokkos::complex<double>(4.248029136732908e+02, -1.042364939115052e+03);
+    h_ref_cbj1(18) =
+        Kokkos::complex<double>(4.248029136732908e+02, +1.042364939115052e+03);
+    h_ref_cbj1(19) =
+        Kokkos::complex<double>(-4.248029136732909e+02, -1.042364939115052e+03);
+    h_ref_cbj1(20) =
+        Kokkos::complex<double>(-4.248029136732909e+02, +1.042364939115052e+03);
+    h_ref_cbj1(21) = Kokkos::complex<double>(1.305514883350938e-01, 0);
+    h_ref_cbj1(22) =
+        Kokkos::complex<double>(-1.305514883350938e-01, +7.993709105806192e-18);
+    h_ref_cbj1(23) = Kokkos::complex<double>(4.659838375816632e-02, 0);
+    h_ref_cbj1(24) =
+        Kokkos::complex<double>(-4.659838375816632e-02, +6.322680793358811e-18);
+
+    h_ref_cby1(0) = Kokkos::complex<double>(-infinity<double>::value, 0);
+    h_ref_cby1(1) =
+        Kokkos::complex<double>(1.285849341463599e+00, +7.250812532419394e-01);
+    h_ref_cby1(2) =
+        Kokkos::complex<double>(1.285849341463599e+00, -7.250812532419394e-01);
+    h_ref_cby1(3) =
+        Kokkos::complex<double>(1.236114779014097e+00, -8.352164439165690e-01);
+    h_ref_cby1(4) =
+        Kokkos::complex<double>(1.236114779014097e+00, +8.352164439165690e-01);
+    h_ref_cby1(5) =
+        Kokkos::complex<double>(1.576608512528508e+03, -7.469476251109801e+02);
+    h_ref_cby1(6) =
+        Kokkos::complex<double>(1.576608512528508e+03, +7.469476251109801e+02);
+    h_ref_cby1(7) =
+        Kokkos::complex<double>(1.576608497980113e+03, +7.469476255749524e+02);
+    h_ref_cby1(8) =
+        Kokkos::complex<double>(1.576608497980113e+03, -7.469476255749524e+02);
+    h_ref_cby1(9) = Kokkos::complex<double>(3.246744247918000e-01, 0);
+    h_ref_cby1(10) =
+        Kokkos::complex<double>(-3.246744247918000e-01, -6.781179170518730e-01);
+    h_ref_cby1(11) = Kokkos::complex<double>(1.616692009926331e-01, 0);
+    h_ref_cby1(12) =
+        Kokkos::complex<double>(-1.616692009926332e-01, +7.903864376740302e-02);
+    h_ref_cby1(13) =
+        Kokkos::complex<double>(1.027302268200224e+03, +1.233147093992241e+03);
+    h_ref_cby1(14) =
+        Kokkos::complex<double>(1.027302268200224e+03, -1.233147093992241e+03);
+    h_ref_cby1(15) =
+        Kokkos::complex<double>(1.027302263607999e+03, -1.233147106522383e+03);
+    h_ref_cby1(16) =
+        Kokkos::complex<double>(1.027302263607999e+03, +1.233147106522383e+03);
+    h_ref_cby1(17) =
+        Kokkos::complex<double>(1.042364943073579e+03, +4.248029112344685e+02);
+    h_ref_cby1(18) =
+        Kokkos::complex<double>(1.042364943073579e+03, -4.248029112344685e+02);
+    h_ref_cby1(19) =
+        Kokkos::complex<double>(1.042364935156525e+03, -4.248029161121132e+02);
+    h_ref_cby1(20) =
+        Kokkos::complex<double>(1.042364935156525e+03, +4.248029161121132e+02);
+    h_ref_cby1(21) = Kokkos::complex<double>(7.552212658226459e-02, 0);
+    h_ref_cby1(22) =
+        Kokkos::complex<double>(-7.552212658226459e-02, -2.611029766701876e-01);
+    h_ref_cby1(23) = Kokkos::complex<double>(9.186960936986688e-02, 0);
+    h_ref_cby1(24) =
+        Kokkos::complex<double>(-9.186960936986688e-02, -9.319676751633262e-02);
+
+    for (int i = 0; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_cbj1(i) - h_ref_cbj1(i)),
+                Kokkos::abs(h_ref_cbj1(i)) * 1e-13);
+    }
+
+    EXPECT_EQ(h_ref_cby1(0), h_cby1(0));
+    for (int i = 1; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_cby1(i) - h_ref_cby1(i)),
+                Kokkos::abs(h_ref_cby1(i)) * 1e-13);
+    }
+
+    ////Test large arguments
+    d_z_large        = ViewType("d_z_large", 6);
+    d_cbj1_large     = ViewType("d_cbj1_large", 6);
+    d_cby1_large     = ViewType("d_cby1_large", 6);
+    h_z_large        = Kokkos::create_mirror_view(d_z_large);
+    h_cbj1_large     = Kokkos::create_mirror_view(d_cbj1_large);
+    h_cby1_large     = Kokkos::create_mirror_view(d_cby1_large);
+    h_ref_cbj1_large = HostViewType("h_ref_cbj1_large", 2);
+    h_ref_cby1_large = HostViewType("h_ref_cby1_large", 2);
+
+    h_z_large(0) = Kokkos::complex<double>(10000.0, 100.0);
+    h_z_large(1) = Kokkos::complex<double>(10000.0, 100.0);
+    h_z_large(2) = Kokkos::complex<double>(10000.0, 100.0);
+    h_z_large(3) = Kokkos::complex<double>(-10000.0, 100.0);
+    h_z_large(4) = Kokkos::complex<double>(-10000.0, 100.0);
+    h_z_large(5) = Kokkos::complex<double>(-10000.0, 100.0);
+
+    Kokkos::deep_copy(d_z_large, h_z_large);
+
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace, TestLargeArgTag>(0, 1),
+                         *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_cbj1_large, d_cbj1_large);
+    Kokkos::deep_copy(h_cby1_large, d_cby1_large);
+
+    h_ref_cbj1_large(0) =
+        Kokkos::complex<double>(4.854515317906369e+40, -9.562049455402486e+40);
+    h_ref_cbj1_large(1) =
+        Kokkos::complex<double>(-4.854515317906371e+40, -9.562049455402486e+40);
+
+    h_ref_cby1_large(0) =
+        Kokkos::complex<double>(9.562049455402486e+40, 4.854515317906369e+40);
+    h_ref_cby1_large(1) =
+        Kokkos::complex<double>(9.562049455402486e+40, -4.854515317906369e+40);
+
+    EXPECT_TRUE((Kokkos::abs(h_cbj1_large(0) - h_ref_cbj1_large(0)) <
+                 Kokkos::abs(h_ref_cbj1_large(0)) * 1e-12) &&
+                (Kokkos::abs(h_cbj1_large(0) - h_ref_cbj1_large(0)) >
+                 Kokkos::abs(h_ref_cbj1_large(0)) * 1e-13));
+    EXPECT_TRUE(Kokkos::abs(h_cbj1_large(1) - h_ref_cbj1_large(0)) >
+                Kokkos::abs(h_ref_cbj1_large(0)) * 1e-6);
+    EXPECT_TRUE(Kokkos::abs(h_cbj1_large(2) - h_ref_cbj1_large(0)) <
+                Kokkos::abs(h_ref_cbj1_large(0)) * 1e-13);
+    EXPECT_TRUE((Kokkos::abs(h_cbj1_large(3) - h_ref_cbj1_large(1)) <
+                 Kokkos::abs(h_ref_cbj1_large(1)) * 1e-12) &&
+                (Kokkos::abs(h_cbj1_large(3) - h_ref_cbj1_large(1)) >
+                 Kokkos::abs(h_ref_cbj1_large(1)) * 1e-13));
+    EXPECT_TRUE(Kokkos::abs(h_cbj1_large(4) - h_ref_cbj1_large(1)) >
+                Kokkos::abs(h_ref_cbj1_large(1)) * 1e-6);
+    EXPECT_TRUE(Kokkos::abs(h_cbj1_large(5) - h_ref_cbj1_large(1)) <
+                Kokkos::abs(h_ref_cbj1_large(1)) * 1e-13);
+
+    EXPECT_TRUE((Kokkos::abs(h_cby1_large(0) - h_ref_cby1_large(0)) <
+                 Kokkos::abs(h_ref_cby1_large(0)) * 1e-12) &&
+                (Kokkos::abs(h_cby1_large(0) - h_ref_cby1_large(0)) >
+                 Kokkos::abs(h_ref_cby1_large(0)) * 1e-13));
+    EXPECT_TRUE(Kokkos::abs(h_cby1_large(1) - h_ref_cby1_large(0)) >
+                Kokkos::abs(h_ref_cby1_large(0)) * 1e-6);
+    EXPECT_TRUE(Kokkos::abs(h_cby1_large(2) - h_ref_cby1_large(0)) <
+                Kokkos::abs(h_ref_cby1_large(0)) * 1e-13);
+    EXPECT_TRUE((Kokkos::abs(h_cby1_large(3) - h_ref_cby1_large(1)) <
+                 Kokkos::abs(h_ref_cby1_large(1)) * 1e-12) &&
+                (Kokkos::abs(h_cby1_large(3) - h_ref_cby1_large(1)) >
+                 Kokkos::abs(h_ref_cby1_large(1)) * 1e-13));
+    EXPECT_TRUE(Kokkos::abs(h_cby1_large(4) - h_ref_cby1_large(1)) >
+                Kokkos::abs(h_ref_cby1_large(1)) * 1e-6);
+    EXPECT_TRUE(Kokkos::abs(h_cby1_large(5) - h_ref_cby1_large(1)) <
+                Kokkos::abs(h_ref_cby1_large(1)) * 1e-13);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int& i) const {
+    d_cbj1(i) = Kokkos::Experimental::cyl_bessel_j1<Kokkos::complex<double>,
+                                                    double, int>(d_z(i));
+    d_cby1(i) = Kokkos::Experimental::cyl_bessel_y1<Kokkos::complex<double>,
+                                                    double, int>(d_z(i));
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const TestLargeArgTag&, const int& /*i*/) const {
+    d_cbj1_large(0) =
+        Kokkos::Experimental::cyl_bessel_j1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(0));
+    d_cbj1_large(1) =
+        Kokkos::Experimental::cyl_bessel_j1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(1), 11000, 3000);
+    d_cbj1_large(2) =
+        Kokkos::Experimental::cyl_bessel_j1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(2), 11000, 7500);
+    d_cbj1_large(3) =
+        Kokkos::Experimental::cyl_bessel_j1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(3));
+    d_cbj1_large(4) =
+        Kokkos::Experimental::cyl_bessel_j1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(4), 11000, 3000);
+    d_cbj1_large(5) =
+        Kokkos::Experimental::cyl_bessel_j1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(5), 11000, 7500);
+
+    d_cby1_large(0) =
+        Kokkos::Experimental::cyl_bessel_y1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(0));
+    d_cby1_large(1) =
+        Kokkos::Experimental::cyl_bessel_y1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(1), 11000, 3000);
+    d_cby1_large(2) =
+        Kokkos::Experimental::cyl_bessel_y1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(2), 11000, 7500);
+    d_cby1_large(3) =
+        Kokkos::Experimental::cyl_bessel_y1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(3));
+    d_cby1_large(4) =
+        Kokkos::Experimental::cyl_bessel_y1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(4), 11000, 3000);
+    d_cby1_large(5) =
+        Kokkos::Experimental::cyl_bessel_y1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(5), 11000, 7500);
+  }
+};
+
+template <class ExecSpace>
+struct TestComplexBesselI0K0Function {
+  using ViewType = Kokkos::View<Kokkos::complex<double>*, ExecSpace>;
+  using HostViewType =
+      Kokkos::View<Kokkos::complex<double>*, Kokkos::HostSpace>;
+
+  ViewType d_z, d_cbi0, d_cbk0;
+  typename ViewType::HostMirror h_z, h_cbi0, h_cbk0;
+  HostViewType h_ref_cbi0, h_ref_cbk0;
+
+  ViewType d_z_large, d_cbi0_large, d_cbk0_large;
+  typename ViewType::HostMirror h_z_large, h_cbi0_large, h_cbk0_large;
+  HostViewType h_ref_cbi0_large, h_ref_cbk0_large;
+
+  void testit() {
+    using Kokkos::Experimental::infinity;
+
+    int N      = 25;
+    d_z        = ViewType("d_z", N);
+    d_cbi0     = ViewType("d_cbi0", N);
+    d_cbk0     = ViewType("d_cbk0", N);
+    h_z        = Kokkos::create_mirror_view(d_z);
+    h_cbi0     = Kokkos::create_mirror_view(d_cbi0);
+    h_cbk0     = Kokkos::create_mirror_view(d_cbk0);
+    h_ref_cbi0 = HostViewType("h_ref_cbi0", N);
+    h_ref_cbk0 = HostViewType("h_ref_cbk0", N);
+
+    // Generate test inputs
+    h_z(0)  = Kokkos::complex<double>(0.0, 0.0);
+    h_z(1)  = Kokkos::complex<double>(3.0, 2.0);
+    h_z(2)  = Kokkos::complex<double>(3.0, -2.0);
+    h_z(3)  = Kokkos::complex<double>(-3.0, 2.0);
+    h_z(4)  = Kokkos::complex<double>(-3.0, -2.0);
+    h_z(5)  = Kokkos::complex<double>(23.0, 10.0);
+    h_z(6)  = Kokkos::complex<double>(23.0, -10.0);
+    h_z(7)  = Kokkos::complex<double>(-23.0, 10.0);
+    h_z(8)  = Kokkos::complex<double>(-23.0, -10.0);
+    h_z(9)  = Kokkos::complex<double>(3.0, 0.0);
+    h_z(10) = Kokkos::complex<double>(-3.0, 0.0);
+    h_z(11) = Kokkos::complex<double>(23.0, 0.0);
+    h_z(12) = Kokkos::complex<double>(-23.0, 0.0);
+    h_z(13) = Kokkos::complex<double>(28.0, 10.0);
+    h_z(14) = Kokkos::complex<double>(28.0, -10.0);
+    h_z(15) = Kokkos::complex<double>(-28.0, 10.0);
+    h_z(16) = Kokkos::complex<double>(-28.0, -10.0);
+    h_z(17) = Kokkos::complex<double>(60.0, 10.0);
+    h_z(18) = Kokkos::complex<double>(60.0, -10.0);
+    h_z(19) = Kokkos::complex<double>(-60.0, 10.0);
+    h_z(20) = Kokkos::complex<double>(-60.0, -10.0);
+    h_z(21) = Kokkos::complex<double>(28.0, 0.0);
+    h_z(22) = Kokkos::complex<double>(-28.0, 0.0);
+    h_z(23) = Kokkos::complex<double>(60.0, 0.0);
+    h_z(24) = Kokkos::complex<double>(-60.0, 0.0);
+
+    Kokkos::deep_copy(d_z, h_z);
+
+    // Call Bessel functions
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace>(0, N), *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_cbi0, d_cbi0);
+    Kokkos::deep_copy(h_cbk0, d_cbk0);
+
+    // Reference values computed with Octave
+    h_ref_cbi0(0) = Kokkos::complex<double>(1.000000000000000e+00, 0);
+    h_ref_cbi0(1) =
+        Kokkos::complex<double>(-4.695171920440706e-01, +4.313788409468920e+00);
+    h_ref_cbi0(2) =
+        Kokkos::complex<double>(-4.695171920440706e-01, -4.313788409468920e+00);
+    h_ref_cbi0(3) =
+        Kokkos::complex<double>(-4.695171920440706e-01, -4.313788409468920e+00);
+    h_ref_cbi0(4) =
+        Kokkos::complex<double>(-4.695171920440706e-01, +4.313788409468920e+00);
+    h_ref_cbi0(5) =
+        Kokkos::complex<double>(-7.276526052028507e+08, -2.806354803468570e+08);
+    h_ref_cbi0(6) =
+        Kokkos::complex<double>(-7.276526052028507e+08, +2.806354803468570e+08);
+    h_ref_cbi0(7) =
+        Kokkos::complex<double>(-7.276526052028507e+08, +2.806354803468570e+08);
+    h_ref_cbi0(8) =
+        Kokkos::complex<double>(-7.276526052028507e+08, -2.806354803468570e+08);
+    h_ref_cbi0(9)  = Kokkos::complex<double>(4.880792585865025e+00, 0);
+    h_ref_cbi0(10) = Kokkos::complex<double>(4.880792585865025e+00, 0);
+    h_ref_cbi0(11) = Kokkos::complex<double>(8.151421225128924e+08, 0);
+    h_ref_cbi0(12) = Kokkos::complex<double>(8.151421225128924e+08, 0);
+    h_ref_cbi0(13) =
+        Kokkos::complex<double>(-9.775983282455373e+10, -4.159160389327644e+10);
+    h_ref_cbi0(14) =
+        Kokkos::complex<double>(-9.775983282455373e+10, +4.159160389327644e+10);
+    h_ref_cbi0(15) =
+        Kokkos::complex<double>(-9.775983282455373e+10, +4.159160389327644e+10);
+    h_ref_cbi0(16) =
+        Kokkos::complex<double>(-9.775983282455373e+10, -4.159160389327644e+10);
+    h_ref_cbi0(17) =
+        Kokkos::complex<double>(-5.158377566681892e+24, -2.766704059464302e+24);
+    h_ref_cbi0(18) =
+        Kokkos::complex<double>(-5.158377566681892e+24, +2.766704059464302e+24);
+    h_ref_cbi0(19) =
+        Kokkos::complex<double>(-5.158377566681892e+24, +2.766704059464302e+24);
+    h_ref_cbi0(20) =
+        Kokkos::complex<double>(-5.158377566681892e+24, -2.766704059464302e+24);
+    h_ref_cbi0(21) = Kokkos::complex<double>(1.095346047317573e+11, 0);
+    h_ref_cbi0(22) = Kokkos::complex<double>(1.095346047317573e+11, 0);
+    h_ref_cbi0(23) = Kokkos::complex<double>(5.894077055609803e+24, 0);
+    h_ref_cbi0(24) = Kokkos::complex<double>(5.894077055609803e+24, 0);
+
+    h_ref_cbk0(0) = Kokkos::complex<double>(infinity<double>::value, 0);
+    h_ref_cbk0(1) =
+        Kokkos::complex<double>(-2.078722558742977e-02, -2.431266356716766e-02);
+    h_ref_cbk0(2) =
+        Kokkos::complex<double>(-2.078722558742977e-02, +2.431266356716766e-02);
+    h_ref_cbk0(3) =
+        Kokkos::complex<double>(-1.357295320191579e+01, +1.499344424826928e+00);
+    h_ref_cbk0(4) =
+        Kokkos::complex<double>(-1.357295320191579e+01, -1.499344424826928e+00);
+    h_ref_cbk0(5) =
+        Kokkos::complex<double>(-1.820476218131465e-11, +1.795056004780177e-11);
+    h_ref_cbk0(6) =
+        Kokkos::complex<double>(-1.820476218131465e-11, -1.795056004780177e-11);
+    h_ref_cbk0(7) =
+        Kokkos::complex<double>(8.816423633943287e+08, +2.285988078870750e+09);
+    h_ref_cbk0(8) =
+        Kokkos::complex<double>(8.816423633943287e+08, -2.285988078870750e+09);
+    h_ref_cbk0(9) = Kokkos::complex<double>(3.473950438627926e-02, 0);
+    h_ref_cbk0(10) =
+        Kokkos::complex<double>(3.473950438627926e-02, -1.533346213144909e+01);
+    h_ref_cbk0(11) = Kokkos::complex<double>(2.667545110351910e-11, 0);
+    h_ref_cbk0(12) =
+        Kokkos::complex<double>(2.667545110351910e-11, -2.560844503718094e+09);
+    h_ref_cbk0(13) =
+        Kokkos::complex<double>(-1.163319528590747e-13, +1.073711234918388e-13);
+    h_ref_cbk0(14) =
+        Kokkos::complex<double>(-1.163319528590747e-13, -1.073711234918388e-13);
+    h_ref_cbk0(15) =
+        Kokkos::complex<double>(1.306638772421339e+11, +3.071215726177843e+11);
+    h_ref_cbk0(16) =
+        Kokkos::complex<double>(1.306638772421339e+11, -3.071215726177843e+11);
+    h_ref_cbk0(17) =
+        Kokkos::complex<double>(-1.111584549467388e-27, +8.581979311477652e-28);
+    h_ref_cbk0(18) =
+        Kokkos::complex<double>(-1.111584549467388e-27, -8.581979311477652e-28);
+    h_ref_cbk0(19) =
+        Kokkos::complex<double>(8.691857147870108e+24, +1.620552106793022e+25);
+    h_ref_cbk0(20) =
+        Kokkos::complex<double>(8.691857147870108e+24, -1.620552106793022e+25);
+    h_ref_cbk0(21) = Kokkos::complex<double>(1.630534586888181e-13, 0);
+    h_ref_cbk0(22) =
+        Kokkos::complex<double>(1.630534586888181e-13, -3.441131095391506e+11);
+    h_ref_cbk0(23) = Kokkos::complex<double>(1.413897840559108e-27, 0);
+    h_ref_cbk0(24) =
+        Kokkos::complex<double>(1.413897840559108e-27, -1.851678917759592e+25);
+
+    for (int i = 0; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_cbi0(i) - h_ref_cbi0(i)),
+                Kokkos::abs(h_ref_cbi0(i)) * 1e-13);
+    }
+
+    EXPECT_EQ(h_ref_cbk0(0), h_cbk0(0));
+    for (int i = 1; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_cbk0(i) - h_ref_cbk0(i)),
+                Kokkos::abs(h_ref_cbk0(i)) * 1e-13);
+    }
+
+    ////Test large arguments
+    d_z_large        = ViewType("d_z_large", 6);
+    d_cbi0_large     = ViewType("d_cbi0_large", 6);
+    h_z_large        = Kokkos::create_mirror_view(d_z_large);
+    h_cbi0_large     = Kokkos::create_mirror_view(d_cbi0_large);
+    h_ref_cbi0_large = HostViewType("h_ref_cbi0_large", 2);
+
+    h_z_large(0) = Kokkos::complex<double>(100.0, 10.0);
+    h_z_large(1) = Kokkos::complex<double>(100.0, 10.0);
+    h_z_large(2) = Kokkos::complex<double>(100.0, 10.0);
+    h_z_large(3) = Kokkos::complex<double>(-100.0, 10.0);
+    h_z_large(4) = Kokkos::complex<double>(-100.0, 10.0);
+    h_z_large(5) = Kokkos::complex<double>(-100.0, 10.0);
+
+    Kokkos::deep_copy(d_z_large, h_z_large);
+
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace, TestLargeArgTag>(0, 1),
+                         *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_cbi0_large, d_cbi0_large);
+
+    h_ref_cbi0_large(0) =
+        Kokkos::complex<double>(-9.266819049505678e+41, -5.370779383266049e+41);
+    h_ref_cbi0_large(1) =
+        Kokkos::complex<double>(-9.266819049505678e+41, +5.370779383266049e+41);
+
+    EXPECT_TRUE(Kokkos::abs(h_cbi0_large(0) - h_ref_cbi0_large(0)) <
+                Kokkos::abs(h_ref_cbi0_large(0)) * 1e-15);
+    EXPECT_TRUE(Kokkos::abs(h_cbi0_large(1) - h_ref_cbi0_large(0)) >
+                Kokkos::abs(h_ref_cbi0_large(0)) * 1e-4);
+    EXPECT_TRUE(Kokkos::abs(h_cbi0_large(2) - h_ref_cbi0_large(0)) <
+                Kokkos::abs(h_ref_cbi0_large(0)) * 1e-15);
+    EXPECT_TRUE(Kokkos::abs(h_cbi0_large(3) - h_ref_cbi0_large(1)) <
+                Kokkos::abs(h_ref_cbi0_large(1)) * 1e-15);
+    EXPECT_TRUE(Kokkos::abs(h_cbi0_large(4) - h_ref_cbi0_large(1)) >
+                Kokkos::abs(h_ref_cbi0_large(1)) * 1e-4);
+    EXPECT_TRUE(Kokkos::abs(h_cbi0_large(5) - h_ref_cbi0_large(1)) <
+                Kokkos::abs(h_ref_cbi0_large(1)) * 1e-15);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int& i) const {
+    d_cbi0(i) = Kokkos::Experimental::cyl_bessel_i0<Kokkos::complex<double>,
+                                                    double, int>(d_z(i));
+    d_cbk0(i) = Kokkos::Experimental::cyl_bessel_k0<Kokkos::complex<double>,
+                                                    double, int>(d_z(i));
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const TestLargeArgTag&, const int& /*i*/) const {
+    d_cbi0_large(0) =
+        Kokkos::Experimental::cyl_bessel_i0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(0));
+    d_cbi0_large(1) =
+        Kokkos::Experimental::cyl_bessel_i0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(1), 110, 35);
+    d_cbi0_large(2) =
+        Kokkos::Experimental::cyl_bessel_i0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(2), 110, 190);
+    d_cbi0_large(3) =
+        Kokkos::Experimental::cyl_bessel_i0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(3));
+    d_cbi0_large(4) =
+        Kokkos::Experimental::cyl_bessel_i0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(4), 110, 35);
+    d_cbi0_large(5) =
+        Kokkos::Experimental::cyl_bessel_i0<Kokkos::complex<double>, double,
+                                            int>(d_z_large(5), 110, 190);
+  }
+};
+
+template <class ExecSpace>
+struct TestComplexBesselI1K1Function {
+  using ViewType = Kokkos::View<Kokkos::complex<double>*, ExecSpace>;
+  using HostViewType =
+      Kokkos::View<Kokkos::complex<double>*, Kokkos::HostSpace>;
+
+  ViewType d_z, d_cbi1, d_cbk1;
+  typename ViewType::HostMirror h_z, h_cbi1, h_cbk1;
+  HostViewType h_ref_cbi1, h_ref_cbk1;
+
+  ViewType d_z_large, d_cbi1_large, d_cbk1_large;
+  typename ViewType::HostMirror h_z_large, h_cbi1_large, h_cbk1_large;
+  HostViewType h_ref_cbi1_large, h_ref_cbk1_large;
+
+  void testit() {
+    using Kokkos::Experimental::infinity;
+
+    int N      = 25;
+    d_z        = ViewType("d_z", N);
+    d_cbi1     = ViewType("d_cbi1", N);
+    d_cbk1     = ViewType("d_cbk1", N);
+    h_z        = Kokkos::create_mirror_view(d_z);
+    h_cbi1     = Kokkos::create_mirror_view(d_cbi1);
+    h_cbk1     = Kokkos::create_mirror_view(d_cbk1);
+    h_ref_cbi1 = HostViewType("h_ref_cbi1", N);
+    h_ref_cbk1 = HostViewType("h_ref_cbk1", N);
+
+    // Generate test inputs
+    h_z(0)  = Kokkos::complex<double>(0.0, 0.0);
+    h_z(1)  = Kokkos::complex<double>(3.0, 2.0);
+    h_z(2)  = Kokkos::complex<double>(3.0, -2.0);
+    h_z(3)  = Kokkos::complex<double>(-3.0, 2.0);
+    h_z(4)  = Kokkos::complex<double>(-3.0, -2.0);
+    h_z(5)  = Kokkos::complex<double>(23.0, 10.0);
+    h_z(6)  = Kokkos::complex<double>(23.0, -10.0);
+    h_z(7)  = Kokkos::complex<double>(-23.0, 10.0);
+    h_z(8)  = Kokkos::complex<double>(-23.0, -10.0);
+    h_z(9)  = Kokkos::complex<double>(3.0, 0.0);
+    h_z(10) = Kokkos::complex<double>(-3.0, 0.0);
+    h_z(11) = Kokkos::complex<double>(23.0, 0.0);
+    h_z(12) = Kokkos::complex<double>(-23.0, 0.0);
+    h_z(13) = Kokkos::complex<double>(28.0, 10.0);
+    h_z(14) = Kokkos::complex<double>(28.0, -10.0);
+    h_z(15) = Kokkos::complex<double>(-28.0, 10.0);
+    h_z(16) = Kokkos::complex<double>(-28.0, -10.0);
+    h_z(17) = Kokkos::complex<double>(60.0, 10.0);
+    h_z(18) = Kokkos::complex<double>(60.0, -10.0);
+    h_z(19) = Kokkos::complex<double>(-60.0, 10.0);
+    h_z(20) = Kokkos::complex<double>(-60.0, -10.0);
+    h_z(21) = Kokkos::complex<double>(28.0, 0.0);
+    h_z(22) = Kokkos::complex<double>(-28.0, 0.0);
+    h_z(23) = Kokkos::complex<double>(60.0, 0.0);
+    h_z(24) = Kokkos::complex<double>(-60.0, 0.0);
+
+    Kokkos::deep_copy(d_z, h_z);
+
+    // Call Bessel functions
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace>(0, N), *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_cbi1, d_cbi1);
+    Kokkos::deep_copy(h_cbk1, d_cbk1);
+
+    // Reference values computed with Octave
+    h_ref_cbi1(0) = Kokkos::complex<double>(0, 0);
+    h_ref_cbi1(1) =
+        Kokkos::complex<double>(-8.127809410735776e-01, +3.780682961371298e+00);
+    h_ref_cbi1(2) =
+        Kokkos::complex<double>(-8.127809410735776e-01, -3.780682961371298e+00);
+    h_ref_cbi1(3) =
+        Kokkos::complex<double>(8.127809410735776e-01, +3.780682961371298e+00);
+    h_ref_cbi1(4) =
+        Kokkos::complex<double>(8.127809410735776e-01, -3.780682961371298e+00);
+    h_ref_cbi1(5) =
+        Kokkos::complex<double>(-7.119745937677552e+08, -2.813616375214342e+08);
+    h_ref_cbi1(6) =
+        Kokkos::complex<double>(-7.119745937677552e+08, +2.813616375214342e+08);
+    h_ref_cbi1(7) =
+        Kokkos::complex<double>(7.119745937677552e+08, -2.813616375214342e+08);
+    h_ref_cbi1(8) =
+        Kokkos::complex<double>(7.119745937677552e+08, +2.813616375214342e+08);
+    h_ref_cbi1(9)  = Kokkos::complex<double>(3.953370217402609e+00, 0);
+    h_ref_cbi1(10) = Kokkos::complex<double>(-3.953370217402609e+00, 0);
+    h_ref_cbi1(11) = Kokkos::complex<double>(7.972200260896506e+08, 0);
+    h_ref_cbi1(12) = Kokkos::complex<double>(-7.972200260896506e+08, 0);
+    h_ref_cbi1(13) =
+        Kokkos::complex<double>(-9.596150723281404e+10, -4.149038020045121e+10);
+    h_ref_cbi1(14) =
+        Kokkos::complex<double>(-9.596150723281404e+10, +4.149038020045121e+10);
+    h_ref_cbi1(15) =
+        Kokkos::complex<double>(9.596150723281404e+10, -4.149038020045121e+10);
+    h_ref_cbi1(16) =
+        Kokkos::complex<double>(9.596150723281404e+10, +4.149038020045121e+10);
+    h_ref_cbi1(17) =
+        Kokkos::complex<double>(-5.112615594220387e+24, -2.751210232069100e+24);
+    h_ref_cbi1(18) =
+        Kokkos::complex<double>(-5.112615594220387e+24, +2.751210232069100e+24);
+    h_ref_cbi1(19) =
+        Kokkos::complex<double>(5.112615594220387e+24, -2.751210232069100e+24);
+    h_ref_cbi1(20) =
+        Kokkos::complex<double>(5.112615594220387e+24, +2.751210232069100e+24);
+    h_ref_cbi1(21) = Kokkos::complex<double>(1.075605042080823e+11, 0);
+    h_ref_cbi1(22) = Kokkos::complex<double>(-1.075605042080823e+11, 0);
+    h_ref_cbi1(23) = Kokkos::complex<double>(5.844751588390470e+24, 0);
+    h_ref_cbi1(24) = Kokkos::complex<double>(-5.844751588390470e+24, 0);
+
+    h_ref_cbk1(0) = Kokkos::complex<double>(infinity<double>::value, 0);
+    h_ref_cbk1(1) =
+        Kokkos::complex<double>(-2.480952007015153e-02, -2.557074905635180e-02);
+    h_ref_cbk1(2) =
+        Kokkos::complex<double>(-2.480952007015153e-02, +2.557074905635180e-02);
+    h_ref_cbk1(3) =
+        Kokkos::complex<double>(-1.185255629692602e+01, +2.527855884398198e+00);
+    h_ref_cbk1(4) =
+        Kokkos::complex<double>(-1.185255629692602e+01, -2.527855884398198e+00);
+    h_ref_cbk1(5) =
+        Kokkos::complex<double>(-1.839497240093994e-11, +1.841855854336314e-11);
+    h_ref_cbk1(6) =
+        Kokkos::complex<double>(-1.839497240093994e-11, -1.841855854336314e-11);
+    h_ref_cbk1(7) =
+        Kokkos::complex<double>(8.839236534393319e+08, +2.236734153323357e+09);
+    h_ref_cbk1(8) =
+        Kokkos::complex<double>(8.839236534393319e+08, -2.236734153323357e+09);
+    h_ref_cbk1(9) = Kokkos::complex<double>(4.015643112819419e-02, 0);
+    h_ref_cbk1(10) =
+        Kokkos::complex<double>(-4.015643112819419e-02, -1.241987883191272e+01);
+    h_ref_cbk1(11) = Kokkos::complex<double>(2.724930589574976e-11, 0);
+    h_ref_cbk1(12) =
+        Kokkos::complex<double>(-2.724930589574976e-11, -2.504540577257910e+09);
+    h_ref_cbk1(13) =
+        Kokkos::complex<double>(-1.175637676331817e-13, +1.097080943197297e-13);
+    h_ref_cbk1(14) =
+        Kokkos::complex<double>(-1.175637676331817e-13, -1.097080943197297e-13);
+    h_ref_cbk1(15) =
+        Kokkos::complex<double>(1.303458736323849e+11, +3.014719661500124e+11);
+    h_ref_cbk1(16) =
+        Kokkos::complex<double>(1.303458736323849e+11, -3.014719661500124e+11);
+    h_ref_cbk1(17) =
+        Kokkos::complex<double>(-1.119411861396158e-27, +8.666195226392352e-28);
+    h_ref_cbk1(18) =
+        Kokkos::complex<double>(-1.119411861396158e-27, -8.666195226392352e-28);
+    h_ref_cbk1(19) =
+        Kokkos::complex<double>(8.643181853549355e+24, +1.606175559143138e+25);
+    h_ref_cbk1(20) =
+        Kokkos::complex<double>(8.643181853549355e+24, -1.606175559143138e+25);
+    h_ref_cbk1(21) = Kokkos::complex<double>(1.659400107332009e-13, 0);
+    h_ref_cbk1(22) =
+        Kokkos::complex<double>(-1.659400107332009e-13, -3.379112898365253e+11);
+    h_ref_cbk1(23) = Kokkos::complex<double>(1.425632026517104e-27, 0);
+    h_ref_cbk1(24) =
+        Kokkos::complex<double>(-1.425632026517104e-27, -1.836182865214478e+25);
+
+    for (int i = 0; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_cbi1(i) - h_ref_cbi1(i)),
+                Kokkos::abs(h_ref_cbi1(i)) * 1e-13);
+    }
+
+    EXPECT_EQ(h_ref_cbk1(0), h_cbk1(0));
+    for (int i = 1; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_cbk1(i) - h_ref_cbk1(i)),
+                Kokkos::abs(h_ref_cbk1(i)) * 1e-13);
+    }
+
+    ////Test large arguments
+    d_z_large        = ViewType("d_z_large", 6);
+    d_cbi1_large     = ViewType("d_cbi1_large", 6);
+    h_z_large        = Kokkos::create_mirror_view(d_z_large);
+    h_cbi1_large     = Kokkos::create_mirror_view(d_cbi1_large);
+    h_ref_cbi1_large = HostViewType("h_ref_cbi1_large", 2);
+
+    h_z_large(0) = Kokkos::complex<double>(100.0, 10.0);
+    h_z_large(1) = Kokkos::complex<double>(100.0, 10.0);
+    h_z_large(2) = Kokkos::complex<double>(100.0, 10.0);
+    h_z_large(3) = Kokkos::complex<double>(-100.0, 10.0);
+    h_z_large(4) = Kokkos::complex<double>(-100.0, 10.0);
+    h_z_large(5) = Kokkos::complex<double>(-100.0, 10.0);
+
+    Kokkos::deep_copy(d_z_large, h_z_large);
+
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace, TestLargeArgTag>(0, 1),
+                         *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_cbi1_large, d_cbi1_large);
+
+    h_ref_cbi1_large(0) =
+        Kokkos::complex<double>(-9.218158020154234e+41, -5.348736158968607e+41);
+    h_ref_cbi1_large(1) =
+        Kokkos::complex<double>(9.218158020154234e+41, -5.348736158968607e+41);
+
+    EXPECT_TRUE(Kokkos::abs(h_cbi1_large(0) - h_ref_cbi1_large(0)) <
+                Kokkos::abs(h_ref_cbi1_large(0)) * 1e-15);
+    EXPECT_TRUE(Kokkos::abs(h_cbi1_large(1) - h_ref_cbi1_large(0)) >
+                Kokkos::abs(h_ref_cbi1_large(0)) * 1e-4);
+    EXPECT_TRUE(Kokkos::abs(h_cbi1_large(2) - h_ref_cbi1_large(0)) <
+                Kokkos::abs(h_ref_cbi1_large(0)) * 1e-15);
+    EXPECT_TRUE(Kokkos::abs(h_cbi1_large(3) - h_ref_cbi1_large(1)) <
+                Kokkos::abs(h_ref_cbi1_large(1)) * 1e-15);
+    EXPECT_TRUE(Kokkos::abs(h_cbi1_large(4) - h_ref_cbi1_large(1)) >
+                Kokkos::abs(h_ref_cbi1_large(1)) * 1e-4);
+    EXPECT_TRUE(Kokkos::abs(h_cbi1_large(5) - h_ref_cbi1_large(1)) <
+                Kokkos::abs(h_ref_cbi1_large(1)) * 1e-15);
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int& i) const {
+    d_cbi1(i) = Kokkos::Experimental::cyl_bessel_i1<Kokkos::complex<double>,
+                                                    double, int>(d_z(i));
+    d_cbk1(i) = Kokkos::Experimental::cyl_bessel_k1<Kokkos::complex<double>,
+                                                    double, int>(d_z(i));
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const TestLargeArgTag&, const int& /*i*/) const {
+    d_cbi1_large(0) =
+        Kokkos::Experimental::cyl_bessel_i1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(0));
+    d_cbi1_large(1) =
+        Kokkos::Experimental::cyl_bessel_i1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(1), 110, 35);
+    d_cbi1_large(2) =
+        Kokkos::Experimental::cyl_bessel_i1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(2), 110, 190);
+    d_cbi1_large(3) =
+        Kokkos::Experimental::cyl_bessel_i1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(3));
+    d_cbi1_large(4) =
+        Kokkos::Experimental::cyl_bessel_i1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(4), 110, 35);
+    d_cbi1_large(5) =
+        Kokkos::Experimental::cyl_bessel_i1<Kokkos::complex<double>, double,
+                                            int>(d_z_large(5), 110, 190);
+  }
+};
+
+template <class ExecSpace>
+struct TestComplexBesselH1Function {
+  using ViewType = Kokkos::View<Kokkos::complex<double>*, ExecSpace>;
+  using HostViewType =
+      Kokkos::View<Kokkos::complex<double>*, Kokkos::HostSpace>;
+
+  ViewType d_z, d_ch10, d_ch11;
+  typename ViewType::HostMirror h_z, h_ch10, h_ch11;
+  HostViewType h_ref_ch10, h_ref_ch11;
+
+  void testit() {
+    using Kokkos::Experimental::infinity;
+
+    int N      = 25;
+    d_z        = ViewType("d_z", N);
+    d_ch10     = ViewType("d_ch10", N);
+    d_ch11     = ViewType("d_ch11", N);
+    h_z        = Kokkos::create_mirror_view(d_z);
+    h_ch10     = Kokkos::create_mirror_view(d_ch10);
+    h_ch11     = Kokkos::create_mirror_view(d_ch11);
+    h_ref_ch10 = HostViewType("h_ref_ch10", N);
+    h_ref_ch11 = HostViewType("h_ref_ch11", N);
+
+    // Generate test inputs
+    h_z(0)  = Kokkos::complex<double>(0.0, 0.0);
+    h_z(1)  = Kokkos::complex<double>(3.0, 2.0);
+    h_z(2)  = Kokkos::complex<double>(3.0, -2.0);
+    h_z(3)  = Kokkos::complex<double>(-3.0, 2.0);
+    h_z(4)  = Kokkos::complex<double>(-3.0, -2.0);
+    h_z(5)  = Kokkos::complex<double>(23.0, 10.0);
+    h_z(6)  = Kokkos::complex<double>(23.0, -10.0);
+    h_z(7)  = Kokkos::complex<double>(-23.0, 10.0);
+    h_z(8)  = Kokkos::complex<double>(-23.0, -10.0);
+    h_z(9)  = Kokkos::complex<double>(3.0, 0.0);
+    h_z(10) = Kokkos::complex<double>(-3.0, 0.0);
+    h_z(11) = Kokkos::complex<double>(23.0, 0.0);
+    h_z(12) = Kokkos::complex<double>(-23.0, 0.0);
+    h_z(13) = Kokkos::complex<double>(28.0, 10.0);
+    h_z(14) = Kokkos::complex<double>(28.0, -10.0);
+    h_z(15) = Kokkos::complex<double>(-28.0, 10.0);
+    h_z(16) = Kokkos::complex<double>(-28.0, -10.0);
+    h_z(17) = Kokkos::complex<double>(200.0, 60.0);
+    h_z(18) = Kokkos::complex<double>(200.0, -60.0);
+    h_z(19) = Kokkos::complex<double>(-200.0, 60.0);
+    h_z(20) = Kokkos::complex<double>(-200.0, -60.0);
+    h_z(21) = Kokkos::complex<double>(28.0, 0.0);
+    h_z(22) = Kokkos::complex<double>(-28.0, 0.0);
+    h_z(23) = Kokkos::complex<double>(200.0, 0.0);
+    h_z(24) = Kokkos::complex<double>(-200.0, 0.0);
+
+    Kokkos::deep_copy(d_z, h_z);
+
+    // Call Hankel functions
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace>(0, N), *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_ch10, d_ch10);
+    Kokkos::deep_copy(h_ch11, d_ch11);
+
+    // Reference values computed with Octave
+    h_ref_ch10(0) = Kokkos::complex<double>(1.0, -infinity<double>::value);
+    h_ref_ch10(1) =
+        Kokkos::complex<double>(-1.779327030399459e-02, +5.281940449715537e-02);
+    h_ref_ch10(2) =
+        Kokkos::complex<double>(-2.480676488910849e+00, +1.948786988612626e+00);
+    h_ref_ch10(3) =
+        Kokkos::complex<double>(1.779327030399459e-02, +5.281940449715537e-02);
+    h_ref_ch10(4) =
+        Kokkos::complex<double>(-2.516263029518839e+00, -1.843148179618315e+00);
+    h_ref_ch10(5) =
+        Kokkos::complex<double>(-7.217716938222564e-06, -1.002796203581228e-07);
+    h_ref_ch10(6) =
+        Kokkos::complex<double>(-3.204879955218674e+03, -1.446133490498241e+03);
+    h_ref_ch10(7) =
+        Kokkos::complex<double>(7.217716938222564e-06, -1.002796203581228e-07);
+    h_ref_ch10(8) =
+        Kokkos::complex<double>(-3.204879969654108e+03, +1.446133490297682e+03);
+    h_ref_ch10(9) =
+        Kokkos::complex<double>(-2.600519549019334e-01, +3.768500100127903e-01);
+    h_ref_ch10(10) =
+        Kokkos::complex<double>(2.600519549019334e-01, +3.768500100127903e-01);
+    h_ref_ch10(11) =
+        Kokkos::complex<double>(-1.624127813134865e-01, -3.598179027370283e-02);
+    h_ref_ch10(12) =
+        Kokkos::complex<double>(1.624127813134865e-01, -3.598179027370283e-02);
+    h_ref_ch10(13) =
+        Kokkos::complex<double>(-2.184905481759440e-06, +6.263387166445335e-06);
+    h_ref_ch10(14) =
+        Kokkos::complex<double>(-2.025824374843011e+03, +2.512479278555672e+03);
+    h_ref_ch10(15) =
+        Kokkos::complex<double>(2.184905481759440e-06, +6.263387166445335e-06);
+    h_ref_ch10(16) =
+        Kokkos::complex<double>(-2.025824379212821e+03, -2.512479266028897e+03);
+    h_ref_ch10(17) =
+        Kokkos::complex<double>(-1.983689762743337e-28, -4.408449940359881e-28);
+    h_ref_ch10(18) =
+        Kokkos::complex<double>(-8.261945332108929e+23, -6.252486138159269e+24);
+    h_ref_ch10(19) =
+        Kokkos::complex<double>(1.983689762743337e-28, -4.408449940359881e-28);
+    h_ref_ch10(20) =
+        Kokkos::complex<double>(-8.261945332108929e+23, +6.252486138159269e+24);
+    h_ref_ch10(21) =
+        Kokkos::complex<double>(-7.315701054899959e-02, +1.318364704235323e-01);
+    h_ref_ch10(22) =
+        Kokkos::complex<double>(7.315701054899959e-02, +1.318364704235323e-01);
+    h_ref_ch10(23) =
+        Kokkos::complex<double>(-1.543743993056510e-02, -5.426577524981793e-02);
+    h_ref_ch10(24) =
+        Kokkos::complex<double>(1.543743993056510e-02, -5.426577524981793e-02);
+
+    h_ref_ch11(0) = Kokkos::complex<double>(0.0, -infinity<double>::value);
+    h_ref_ch11(1) =
+        Kokkos::complex<double>(5.506759533731469e-02, +2.486728122475093e-02);
+    h_ref_ch11(2) =
+        Kokkos::complex<double>(1.505230101821194e+00, +2.546831401702448e+00);
+    h_ref_ch11(3) =
+        Kokkos::complex<double>(5.506759533731469e-02, -2.486728122475093e-02);
+    h_ref_ch11(4) =
+        Kokkos::complex<double>(-1.615365292495823e+00, +2.497096839252946e+00);
+    h_ref_ch11(5) =
+        Kokkos::complex<double>(-2.319863729607219e-07, +7.274197719836158e-06);
+    h_ref_ch11(6) =
+        Kokkos::complex<double>(-1.493895250453947e+03, +3.153217017782819e+03);
+    h_ref_ch11(7) =
+        Kokkos::complex<double>(-2.319863729607210e-07, -7.274197719836158e-06);
+    h_ref_ch11(8) =
+        Kokkos::complex<double>(1.493895250917918e+03, +3.153217003234423e+03);
+    h_ref_ch11(9) =
+        Kokkos::complex<double>(3.390589585259364e-01, +3.246744247918000e-01);
+    h_ref_ch11(10) =
+        Kokkos::complex<double>(3.390589585259364e-01, -3.246744247918000e-01);
+    h_ref_ch11(11) =
+        Kokkos::complex<double>(-3.951932188370152e-02, +1.616692009926331e-01);
+    h_ref_ch11(12) =
+        Kokkos::complex<double>(-3.951932188370151e-02, -1.616692009926331e-01);
+    h_ref_ch11(13) =
+        Kokkos::complex<double>(6.265071091331731e-06, +2.296112637347948e-06);
+    h_ref_ch11(14) =
+        Kokkos::complex<double>(2.466294194249553e+03, +2.054604534104335e+03);
+    h_ref_ch11(15) =
+        Kokkos::complex<double>(6.265071091331731e-06, -2.296112637347947e-06);
+    h_ref_ch11(16) =
+        Kokkos::complex<double>(-2.466294206779695e+03, +2.054604529512110e+03);
+    h_ref_ch11(17) =
+        Kokkos::complex<double>(-4.416040381930448e-28, +1.974955285825768e-28);
+    h_ref_ch11(18) =
+        Kokkos::complex<double>(-6.250095237987940e+24, +8.112776606830997e+23);
+    h_ref_ch11(19) =
+        Kokkos::complex<double>(-4.416040381930448e-28, -1.974955285825769e-28);
+    h_ref_ch11(20) =
+        Kokkos::complex<double>(6.250095237987940e+24, +8.112776606831005e+23);
+    h_ref_ch11(21) =
+        Kokkos::complex<double>(1.305514883350938e-01, +7.552212658226459e-02);
+    h_ref_ch11(22) =
+        Kokkos::complex<double>(1.305514883350938e-01, -7.552212658226456e-02);
+    h_ref_ch11(23) =
+        Kokkos::complex<double>(-5.430453818237824e-02, +1.530182458038999e-02);
+    h_ref_ch11(24) =
+        Kokkos::complex<double>(-5.430453818237824e-02, -1.530182458039000e-02);
+
+    EXPECT_EQ(h_ref_ch10(0), h_ch10(0));
+    std::cout << "h_ch10(0): " << h_ch10(0)
+              << ", h_ref_ch10(0): " << h_ref_ch10(0) << std::endl;
+    for (int i = 1; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_ch10(i) - h_ref_ch10(i)),
+                Kokkos::abs(h_ref_ch10(i)) * 1e-13);
+      std::cout << i
+                << ", actual diff: " << Kokkos::abs(h_ch10(i) - h_ref_ch10(i))
+                << ", expected diff: " << Kokkos::abs(h_ref_ch10(i)) * 1e-13
+                << std::endl;
+    }
+
+    EXPECT_EQ(h_ref_ch11(0), h_ch11(0));
+    std::cout << "h_ch11(0): " << h_ch11(0)
+              << ", h_ref_ch11(0): " << h_ref_ch11(0) << std::endl;
+    for (int i = 1; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_ch11(i) - h_ref_ch11(i)),
+                Kokkos::abs(h_ref_ch11(i)) * 1e-13);
+      std::cout << i
+                << ", actual diff: " << Kokkos::abs(h_ch11(i) - h_ref_ch11(i))
+                << ", expected diff: " << Kokkos::abs(h_ref_ch11(i)) * 1e-13
+                << std::endl;
+    }
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int& i) const {
+    d_ch10(i) = Kokkos::Experimental::cyl_bessel_h10(d_z(i));
+    d_ch11(i) = Kokkos::Experimental::cyl_bessel_h11(d_z(i));
+  }
+};
+
+template <class ExecSpace>
+struct TestComplexBesselH2Function {
+  using ViewType = Kokkos::View<Kokkos::complex<double>*, ExecSpace>;
+  using HostViewType =
+      Kokkos::View<Kokkos::complex<double>*, Kokkos::HostSpace>;
+
+  ViewType d_z, d_ch20, d_ch21;
+  typename ViewType::HostMirror h_z, h_ch20, h_ch21;
+  HostViewType h_ref_ch20, h_ref_ch21;
+
+  void testit() {
+    using Kokkos::Experimental::infinity;
+
+    int N      = 25;
+    d_z        = ViewType("d_z", N);
+    d_ch20     = ViewType("d_ch20", N);
+    d_ch21     = ViewType("d_ch21", N);
+    h_z        = Kokkos::create_mirror_view(d_z);
+    h_ch20     = Kokkos::create_mirror_view(d_ch20);
+    h_ch21     = Kokkos::create_mirror_view(d_ch21);
+    h_ref_ch20 = HostViewType("h_ref_ch20", N);
+    h_ref_ch21 = HostViewType("h_ref_ch21", N);
+
+    // Generate test inputs
+    h_z(0)  = Kokkos::complex<double>(0.0, 0.0);
+    h_z(1)  = Kokkos::complex<double>(3.0, 2.0);
+    h_z(2)  = Kokkos::complex<double>(3.0, -2.0);
+    h_z(3)  = Kokkos::complex<double>(-3.0, 2.0);
+    h_z(4)  = Kokkos::complex<double>(-3.0, -2.0);
+    h_z(5)  = Kokkos::complex<double>(23.0, 10.0);
+    h_z(6)  = Kokkos::complex<double>(23.0, -10.0);
+    h_z(7)  = Kokkos::complex<double>(-23.0, 10.0);
+    h_z(8)  = Kokkos::complex<double>(-23.0, -10.0);
+    h_z(9)  = Kokkos::complex<double>(3.0, 0.0);
+    h_z(10) = Kokkos::complex<double>(-3.0, 0.0);
+    h_z(11) = Kokkos::complex<double>(23.0, 0.0);
+    h_z(12) = Kokkos::complex<double>(-23.0, 0.0);
+    h_z(13) = Kokkos::complex<double>(28.0, 10.0);
+    h_z(14) = Kokkos::complex<double>(28.0, -10.0);
+    h_z(15) = Kokkos::complex<double>(-28.0, 10.0);
+    h_z(16) = Kokkos::complex<double>(-28.0, -10.0);
+    h_z(17) = Kokkos::complex<double>(200.0, 60.0);
+    h_z(18) = Kokkos::complex<double>(200.0, -60.0);
+    h_z(19) = Kokkos::complex<double>(-200.0, 60.0);
+    h_z(20) = Kokkos::complex<double>(-200.0, -60.0);
+    h_z(21) = Kokkos::complex<double>(28.0, 0.0);
+    h_z(22) = Kokkos::complex<double>(-28.0, 0.0);
+    h_z(23) = Kokkos::complex<double>(200.0, 0.0);
+    h_z(24) = Kokkos::complex<double>(-200.0, 0.0);
+
+    Kokkos::deep_copy(d_z, h_z);
+
+    // Call Hankel functions
+    Kokkos::parallel_for(Kokkos::RangePolicy<ExecSpace>(0, N), *this);
+    Kokkos::fence();
+
+    Kokkos::deep_copy(h_ch20, d_ch20);
+    Kokkos::deep_copy(h_ch21, d_ch21);
+
+    // Reference values computed with Octave
+    h_ref_ch20(0) = Kokkos::complex<double>(1.0, infinity<double>::value);
+    h_ref_ch20(1) =
+        Kokkos::complex<double>(-2.480676488910849e+00, -1.948786988612626e+00);
+    h_ref_ch20(2) =
+        Kokkos::complex<double>(-1.779327030399459e-02, -5.281940449715537e-02);
+    h_ref_ch20(3) =
+        Kokkos::complex<double>(-2.516263029518839e+00, +1.843148179618315e+00);
+    h_ref_ch20(4) =
+        Kokkos::complex<double>(1.779327030399459e-02, -5.281940449715537e-02);
+    h_ref_ch20(5) =
+        Kokkos::complex<double>(-3.204879955218674e+03, +1.446133490498241e+03);
+    h_ref_ch20(6) =
+        Kokkos::complex<double>(-7.217716938222564e-06, +1.002796203581228e-07);
+    h_ref_ch20(7) =
+        Kokkos::complex<double>(-3.204879969654108e+03, -1.446133490297682e+03);
+    h_ref_ch20(8) =
+        Kokkos::complex<double>(7.217716938222564e-06, +1.002796203581228e-07);
+    h_ref_ch20(9) =
+        Kokkos::complex<double>(-2.600519549019334e-01, -3.768500100127903e-01);
+    h_ref_ch20(10) =
+        Kokkos::complex<double>(-7.801558647058006e-01, -3.768500100127903e-01);
+    h_ref_ch20(11) =
+        Kokkos::complex<double>(-1.624127813134865e-01, +3.598179027370283e-02);
+    h_ref_ch20(12) =
+        Kokkos::complex<double>(-4.872383439404597e-01, +3.598179027370281e-02);
+    h_ref_ch20(13) =
+        Kokkos::complex<double>(-2.025824374843011e+03, -2.512479278555672e+03);
+    h_ref_ch20(14) =
+        Kokkos::complex<double>(-2.184905481759440e-06, -6.263387166445335e-06);
+    h_ref_ch20(15) =
+        Kokkos::complex<double>(-2.025824379212821e+03, +2.512479266028897e+03);
+    h_ref_ch20(16) =
+        Kokkos::complex<double>(2.184905481759440e-06, -6.263387166445335e-06);
+    h_ref_ch20(17) =
+        Kokkos::complex<double>(-8.261945332108929e+23, +6.252486138159269e+24);
+    h_ref_ch20(18) =
+        Kokkos::complex<double>(-1.983689762743337e-28, +4.408449940359881e-28);
+    h_ref_ch20(19) =
+        Kokkos::complex<double>(-8.261945332108929e+23, -6.252486138159269e+24);
+    h_ref_ch20(20) =
+        Kokkos::complex<double>(1.983689762743337e-28, +4.408449940359881e-28);
+    h_ref_ch20(21) =
+        Kokkos::complex<double>(-7.315701054899959e-02, -1.318364704235323e-01);
+    h_ref_ch20(22) =
+        Kokkos::complex<double>(-2.194710316469988e-01, -1.318364704235323e-01);
+    h_ref_ch20(23) =
+        Kokkos::complex<double>(-1.543743993056510e-02, +5.426577524981793e-02);
+    h_ref_ch20(24) =
+        Kokkos::complex<double>(-4.631231979169528e-02, +5.426577524981793e-02);
+
+    h_ref_ch21(0) = Kokkos::complex<double>(0.0, infinity<double>::value);
+    h_ref_ch21(1) =
+        Kokkos::complex<double>(1.505230101821194e+00, -2.546831401702448e+00);
+    h_ref_ch21(2) =
+        Kokkos::complex<double>(5.506759533731469e-02, -2.486728122475093e-02);
+    h_ref_ch21(3) =
+        Kokkos::complex<double>(-1.615365292495823e+00, -2.497096839252946e+00);
+    h_ref_ch21(4) =
+        Kokkos::complex<double>(5.506759533731469e-02, +2.486728122475093e-02);
+    h_ref_ch21(5) =
+        Kokkos::complex<double>(-1.493895250453947e+03, -3.153217017782819e+03);
+    h_ref_ch21(6) =
+        Kokkos::complex<double>(-2.319863729607219e-07, -7.274197719836158e-06);
+    h_ref_ch21(7) =
+        Kokkos::complex<double>(1.493895250917918e+03, -3.153217003234423e+03);
+    h_ref_ch21(8) =
+        Kokkos::complex<double>(-2.319863729607210e-07, +7.274197719836158e-06);
+    h_ref_ch21(9) =
+        Kokkos::complex<double>(3.390589585259364e-01, -3.246744247918000e-01);
+    h_ref_ch21(10) =
+        Kokkos::complex<double>(-1.017176875577809e+00, +3.246744247918000e-01);
+    h_ref_ch21(11) =
+        Kokkos::complex<double>(-3.951932188370152e-02, -1.616692009926331e-01);
+    h_ref_ch21(12) =
+        Kokkos::complex<double>(1.185579656511045e-01, +1.616692009926332e-01);
+    h_ref_ch21(13) =
+        Kokkos::complex<double>(2.466294194249553e+03, -2.054604534104335e+03);
+    h_ref_ch21(14) =
+        Kokkos::complex<double>(6.265071091331731e-06, -2.296112637347948e-06);
+    h_ref_ch21(15) =
+        Kokkos::complex<double>(-2.466294206779695e+03, -2.054604529512110e+03);
+    h_ref_ch21(16) =
+        Kokkos::complex<double>(6.265071091331731e-06, +2.296112637347947e-06);
+    h_ref_ch21(17) =
+        Kokkos::complex<double>(-6.250095237987940e+24, -8.112776606830997e+23);
+    h_ref_ch21(18) =
+        Kokkos::complex<double>(-4.416040381930448e-28, -1.974955285825768e-28);
+    h_ref_ch21(19) =
+        Kokkos::complex<double>(6.250095237987940e+24, -8.112776606831005e+23);
+    h_ref_ch21(20) =
+        Kokkos::complex<double>(-4.416040381930448e-28, +1.974955285825769e-28);
+    h_ref_ch21(21) =
+        Kokkos::complex<double>(1.305514883350938e-01, -7.552212658226459e-02);
+    h_ref_ch21(22) =
+        Kokkos::complex<double>(-3.916544650052814e-01, +7.552212658226461e-02);
+    h_ref_ch21(23) =
+        Kokkos::complex<double>(-5.430453818237824e-02, -1.530182458038999e-02);
+    h_ref_ch21(24) =
+        Kokkos::complex<double>(1.629136145471347e-01, +1.530182458039000e-02);
+
+    EXPECT_EQ(h_ref_ch20(0), h_ch20(0));
+    for (int i = 1; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_ch20(i) - h_ref_ch20(i)),
+                Kokkos::abs(h_ref_ch20(i)) * 1e-13);
+    }
+
+    EXPECT_EQ(h_ref_ch21(0), h_ch21(0));
+    for (int i = 1; i < N; i++) {
+      EXPECT_LE(Kokkos::abs(h_ch21(i) - h_ref_ch21(i)),
+                Kokkos::abs(h_ref_ch21(i)) * 1e-13);
+    }
+  }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int& i) const {
+    d_ch20(i) = Kokkos::Experimental::cyl_bessel_h20(d_z(i));
+    d_ch21(i) = Kokkos::Experimental::cyl_bessel_h21(d_z(i));
+  }
+};
+
+TEST(TEST_CATEGORY, mathspecialfunc_expint1) {
+  TestExponentialIntergral1Function<TEST_EXECSPACE> test;
+  test.testit();
+}
+
+TEST(TEST_CATEGORY, mathspecialfunc_errorfunc) {
+  TestComplexErrorFunction<TEST_EXECSPACE> test;
+  test.testit();
+}
+
+TEST(TEST_CATEGORY, mathspecialfunc_cbesselj0y0) {
+  TestComplexBesselJ0Y0Function<TEST_EXECSPACE> test;
+  test.testit();
+}
+
+TEST(TEST_CATEGORY, mathspecialfunc_cbesselj1y1) {
+  TestComplexBesselJ1Y1Function<TEST_EXECSPACE> test;
+  test.testit();
+}
+
+TEST(TEST_CATEGORY, mathspecialfunc_cbesseli0k0) {
+  TestComplexBesselI0K0Function<TEST_EXECSPACE> test;
+  test.testit();
+}
+
+TEST(TEST_CATEGORY, mathspecialfunc_cbesseli1k1) {
+  TestComplexBesselI1K1Function<TEST_EXECSPACE> test;
+  test.testit();
+}
+
+TEST(TEST_CATEGORY, mathspecialfunc_cbesselh1stkind) {
+  TestComplexBesselH1Function<TEST_EXECSPACE> test;
+  test.testit();
+}
+
+TEST(TEST_CATEGORY, mathspecialfunc_cbesselh2ndkind) {
+  TestComplexBesselH2Function<TEST_EXECSPACE> test;
+  test.testit();
+}
+
+}  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestMemoryPool.hpp b/packages/kokkos/core/unit_test/TestMemoryPool.hpp
index 63895ad47dc435c98201a2b46d8b439d2a50ad51..829e8d641a5b00a0be67200bdf30495951e95457 100644
--- a/packages/kokkos/core/unit_test/TestMemoryPool.hpp
+++ b/packages/kokkos/core/unit_test/TestMemoryPool.hpp
@@ -50,7 +50,7 @@
 #include <cmath>
 #include <algorithm>
 
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 namespace TestMemoryPool {
 
diff --git a/packages/kokkos/core/unit_test/TestNonTrivialScalarTypes.hpp b/packages/kokkos/core/unit_test/TestNonTrivialScalarTypes.hpp
index 6c8a47a5861dd361364a94551abcfd50d0e85153..d7607c4f71b0df4f32af77f4441c8c909992b14a 100644
--- a/packages/kokkos/core/unit_test/TestNonTrivialScalarTypes.hpp
+++ b/packages/kokkos/core/unit_test/TestNonTrivialScalarTypes.hpp
@@ -48,7 +48,7 @@
 
 #include <Kokkos_Core.hpp>
 
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <iostream>
 #include <cstdlib>
 #include <cstdint>
@@ -310,6 +310,46 @@ struct array_reduce {
     return lsum;
   }
 };
+
+struct point_t {
+  uint8_t x, y, z;
+
+  KOKKOS_FUNCTION
+  point_t() : x(1), y(1), z(1){};
+
+  KOKKOS_FUNCTION
+  point_t(const point_t &val) : x(val.x), y(val.y), z(val.z){};
+
+  KOKKOS_FUNCTION
+  point_t(const volatile point_t &val) : x(val.x), y(val.y), z(val.z){};
+
+  KOKKOS_FUNCTION
+  point_t(const int rhs) { x = y = z = static_cast<uint8_t>(rhs); }
+
+  KOKKOS_FUNCTION
+  explicit operator int() const { return static_cast<int>(x + y + z); }
+
+  KOKKOS_FUNCTION
+  bool operator==(const volatile point_t rhs) const volatile {
+    return (x == rhs.x && y == rhs.y && z == rhs.z);
+  }
+
+  KOKKOS_FUNCTION
+  void operator=(point_t rhs) volatile {
+    x = rhs.x;
+    y = rhs.y;
+    z = rhs.z;
+  }
+
+  KOKKOS_FUNCTION
+  volatile point_t operator+=(const volatile point_t rhs) volatile {
+    x += rhs.x;
+    y += rhs.y;
+    z += rhs.z;
+    return *this;
+  }
+};
+
 }  // namespace Test
 
 namespace Kokkos {
@@ -334,5 +374,21 @@ struct reduction_identity<Test::array_reduce<scalar_t, N>> {
     return Test::array_reduce<scalar_t, N>(t_red_ident::prod());
   }
 };
+
+template <>
+struct reduction_identity<Test::point_t> {
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static uint8_t sum() noexcept {
+    return 0;
+  }
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static uint8_t prod() noexcept {
+    return 1;
+  }
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static uint8_t max() noexcept {
+    return 0xff;
+  }
+  KOKKOS_FORCEINLINE_FUNCTION constexpr static uint8_t min() noexcept {
+    return 0x0;
+  }
+};
 }  // namespace Kokkos
 #endif  // TESTNONTRIVIALSCALARTYPES_HPP_
diff --git a/packages/kokkos/core/unit_test/TestNumericTraits.hpp b/packages/kokkos/core/unit_test/TestNumericTraits.hpp
index fe01b83834f26eddc15e71360d77e85452ef0238..cb69cb83211e7b82f941e544b0498da6df737cf4 100644
--- a/packages/kokkos/core/unit_test/TestNumericTraits.hpp
+++ b/packages/kokkos/core/unit_test/TestNumericTraits.hpp
@@ -46,6 +46,7 @@
 
 #include <Kokkos_Core.hpp>
 #include <type_traits>
+#include <limits>
 #include "Kokkos_NumericTraits.hpp"
 #include "Kokkos_ExecPolicy.hpp"
 
@@ -198,7 +199,9 @@ struct TestNumericTraits<
 TEST(TEST_CATEGORY, numeric_traits_infinity) {
   TestNumericTraits<TEST_EXECSPACE, float, Infinity>();
   TestNumericTraits<TEST_EXECSPACE, double, Infinity>();
+#ifndef KOKKOS_COMPILER_IBM  // fails with XL 16.1.1 see issue #4100
   TestNumericTraits<TEST_EXECSPACE, long double, Infinity>();
+#endif
 }
 
 TEST(TEST_CATEGORY, numeric_traits_epsilon) {
@@ -334,3 +337,182 @@ TEST(TEST_CATEGORY, numeric_traits_min_max_exponent10) {
   TestNumericTraits<TEST_EXECSPACE, long double, MinExponent10>();
   TestNumericTraits<TEST_EXECSPACE, long double, MaxExponent10>();
 }
+
+namespace NumericTraitsSFINAE {
+
+struct HasNoSpecialization {};
+
+#define CHECK_TRAIT_IS_SFINAE_FRIENDLY(TRAIT)                              \
+  template <class T>                                                       \
+  using TRAIT##_value_t = decltype(Kokkos::Experimental::TRAIT<T>::value); \
+  template <class T>                                                       \
+  using has_##TRAIT = Kokkos::is_detected<TRAIT##_value_t, T>;             \
+  static_assert(!has_##TRAIT<HasNoSpecialization>::value, "");
+
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(infinity)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(finite_min)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(finite_max)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(epsilon)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(round_error)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(norm_min)
+
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(digits)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(digits10)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(max_digits10)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(radix)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(min_exponent)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(min_exponent10)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(max_exponent)
+CHECK_TRAIT_IS_SFINAE_FRIENDLY(max_exponent10)
+
+}  // namespace NumericTraitsSFINAE
+
+// Example detecting presence or absence of values
+template <class T>
+using infinity_value_t = decltype(Kokkos::Experimental::infinity<T>::value);
+
+template <class T>
+using has_infinity = Kokkos::is_detected<infinity_value_t, T>;
+
+template <class T, std::enable_if_t<has_infinity<T>::value>* = nullptr>
+constexpr T legacy_std_numeric_limits_infinity() {
+  return Kokkos::Experimental::infinity<T>::value;
+}
+
+template <class T, std::enable_if_t<!has_infinity<T>::value>* = nullptr>
+constexpr T legacy_std_numeric_limits_infinity() {
+  return T();
+}
+
+TEST(TEST_CATEGORY, numeric_traits_sfinae_friendly) {
+  ASSERT_EQ(legacy_std_numeric_limits_infinity<int>(), 0);
+}
+
+// Compare to std::numeric_limits
+template <int V1, int V2>
+struct AssertIntEquality {
+  static constexpr bool value = false;
+};
+template <int V>
+struct AssertIntEquality<V, V> {
+  static constexpr bool value = true;
+};
+#define CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(T, TRAIT)           \
+  static_assert(AssertIntEquality<Kokkos::Experimental::TRAIT<T>::value, \
+                                  std::numeric_limits<T>::TRAIT>::value, \
+                "")
+#define CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(T, TRAIT) \
+  static_assert(Kokkos::Experimental::TRAIT<T>::value ==       \
+                    std::numeric_limits<T>::TRAIT(),           \
+                "")
+
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(float, infinity);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(double, infinity);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(long double, infinity);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(float, epsilon);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(double, epsilon);
+#ifndef KOKKOS_COMPILER_IBM  // fails with XL 16.1.1
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(long double, epsilon);
+#endif
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(float, round_error);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(double, round_error);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION(long double, round_error);
+// clang-format off
+static_assert(Kokkos::Experimental::norm_min<float      >::value == std::numeric_limits<      float>::min(), "");
+static_assert(Kokkos::Experimental::norm_min<double     >::value == std::numeric_limits<     double>::min(), "");
+static_assert(Kokkos::Experimental::norm_min<long double>::value == std::numeric_limits<long double>::min(), "");
+// integer types
+static_assert(Kokkos::Experimental::finite_min<char                  >::value == std::numeric_limits<                  char>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<signed char           >::value == std::numeric_limits<           signed char>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<unsigned char         >::value == std::numeric_limits<         unsigned char>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<short                 >::value == std::numeric_limits<                 short>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<unsigned short        >::value == std::numeric_limits<        unsigned short>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<int                   >::value == std::numeric_limits<                   int>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<unsigned int          >::value == std::numeric_limits<          unsigned int>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<long int              >::value == std::numeric_limits<              long int>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<unsigned long int     >::value == std::numeric_limits<     unsigned long int>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<long long int         >::value == std::numeric_limits<         long long int>::min(), "");
+static_assert(Kokkos::Experimental::finite_min<unsigned long long int>::value == std::numeric_limits<unsigned long long int>::min(), "");
+static_assert(Kokkos::Experimental::finite_max<char                  >::value == std::numeric_limits<                  char>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<signed char           >::value == std::numeric_limits<           signed char>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<unsigned char         >::value == std::numeric_limits<         unsigned char>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<short                 >::value == std::numeric_limits<                 short>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<unsigned short        >::value == std::numeric_limits<        unsigned short>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<int                   >::value == std::numeric_limits<                   int>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<unsigned int          >::value == std::numeric_limits<          unsigned int>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<long int              >::value == std::numeric_limits<              long int>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<unsigned long int     >::value == std::numeric_limits<     unsigned long int>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<long long int         >::value == std::numeric_limits<         long long int>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<unsigned long long int>::value == std::numeric_limits<unsigned long long int>::max(), "");
+// floating point types
+static_assert(Kokkos::Experimental::finite_min<float      >::value == -std::numeric_limits<      float>::max(), "");
+static_assert(Kokkos::Experimental::finite_min<double     >::value == -std::numeric_limits<     double>::max(), "");
+static_assert(Kokkos::Experimental::finite_min<long double>::value == -std::numeric_limits<long double>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<float      >::value ==  std::numeric_limits<      float>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<double     >::value ==  std::numeric_limits<     double>::max(), "");
+static_assert(Kokkos::Experimental::finite_max<long double>::value ==  std::numeric_limits<long double>::max(), "");
+// clang-format on
+
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(bool, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(char, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(signed char, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned char, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(short, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned short, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(int, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned int, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long int, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned long int, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long long int, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned long long int, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(float, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(double, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long double, digits);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(bool, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(char, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(signed char, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned char, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(short, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned short, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(int, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned int, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long int, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned long int, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long long int, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned long long int, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(float, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(double, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long double, digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(float, max_digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(double, max_digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long double, max_digits10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(bool, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(char, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(signed char, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned char, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(short, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned short, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(int, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned int, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long int, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned long int, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long long int, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(unsigned long long int, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(float, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(double, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long double, radix);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(float, min_exponent);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(float, max_exponent);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(double, min_exponent);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(double, max_exponent);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long double, min_exponent);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long double, max_exponent);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(float, min_exponent10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(float, max_exponent10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(double, min_exponent10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(double, max_exponent10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long double, min_exponent10);
+CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT(long double, max_exponent10);
+
+#undef CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_FUNCTION
+#undef CHECK_SAME_AS_NUMERIC_LIMITS_MEMBER_CONSTANT
diff --git a/packages/kokkos/core/unit_test/TestPolicyConstruction.hpp b/packages/kokkos/core/unit_test/TestPolicyConstruction.hpp
index 0017c690e75c6e1bde1808e87203d8dbbea754cc..d75d78b31f08f3d6234a174053630abace0d781a 100644
--- a/packages/kokkos/core/unit_test/TestPolicyConstruction.hpp
+++ b/packages/kokkos/core/unit_test/TestPolicyConstruction.hpp
@@ -291,34 +291,34 @@ class TestRangePolicyConstruction {
     using policy_t = Kokkos::RangePolicy<>;
     {
       policy_t p(5, 15);
-      ASSERT_TRUE((p.begin() == 5));
-      ASSERT_TRUE((p.end() == 15));
+      ASSERT_EQ(p.begin(), 5);
+      ASSERT_EQ(p.end(), 15);
     }
     {
       policy_t p(Kokkos::DefaultExecutionSpace(), 5, 15);
-      ASSERT_TRUE((p.begin() == 5));
-      ASSERT_TRUE((p.end() == 15));
+      ASSERT_EQ(p.begin(), 5);
+      ASSERT_EQ(p.end(), 15);
     }
     {
       policy_t p(5, 15, Kokkos::ChunkSize(10));
-      ASSERT_TRUE((p.begin() == 5));
-      ASSERT_TRUE((p.end() == 15));
-      ASSERT_TRUE((p.chunk_size() == 10));
+      ASSERT_EQ(p.begin(), 5);
+      ASSERT_EQ(p.end(), 15);
+      ASSERT_EQ(p.chunk_size(), 10);
     }
     {
       policy_t p(Kokkos::DefaultExecutionSpace(), 5, 15, Kokkos::ChunkSize(10));
-      ASSERT_TRUE((p.begin() == 5));
-      ASSERT_TRUE((p.end() == 15));
-      ASSERT_TRUE((p.chunk_size() == 10));
+      ASSERT_EQ(p.begin(), 5);
+      ASSERT_EQ(p.end(), 15);
+      ASSERT_EQ(p.chunk_size(), 10);
     }
     {
       policy_t p;
-      ASSERT_TRUE((p.begin() == 0));
-      ASSERT_TRUE((p.end() == 0));
+      ASSERT_EQ(p.begin(), 0);
+      ASSERT_EQ(p.end(), 0);
       p = policy_t(5, 15, Kokkos::ChunkSize(10));
-      ASSERT_TRUE((p.begin() == 5));
-      ASSERT_TRUE((p.end() == 15));
-      ASSERT_TRUE((p.chunk_size() == 10));
+      ASSERT_EQ(p.begin(), 5);
+      ASSERT_EQ(p.end(), 15);
+      ASSERT_EQ(p.chunk_size(), 10);
     }
   }
 };
@@ -582,7 +582,7 @@ class TestTeamPolicyConstruction {
     ASSERT_EQ(p1.team_size(), team_size);
 // FIXME_SYCL implement chunk_size
 #ifndef KOKKOS_ENABLE_SYCL
-    ASSERT_TRUE(p1.chunk_size() > 0);
+    ASSERT_GT(p1.chunk_size(), 0);
 #endif
     ASSERT_EQ(p1.scratch_size(0), 0);
 
@@ -795,7 +795,7 @@ TEST(TEST_CATEGORY, desired_occupancy_empty_base_optimization) {
   static_assert(sizeof(decltype(policy)) == 1, "");
   static_assert_dummy_policy_must_be_size_one<sizeof(decltype(policy))>
       _assert1{};
-  (void)_assert1;  // avoid unused variable warning
+  (void)&_assert1;  // avoid unused variable warning
 
   using Kokkos::Experimental::DesiredOccupancy;
   auto policy_with_occ =
@@ -805,7 +805,7 @@ TEST(TEST_CATEGORY, desired_occupancy_empty_base_optimization) {
   static_assert_dummy_policy_must_be_size_of_desired_occupancy<
       sizeof(decltype(policy_with_occ)), sizeof(DesiredOccupancy)>
       _assert2{};
-  (void)_assert2;  // avoid unused variable warning
+  (void)&_assert2;  // avoid unused variable warning
 }
 
 template <typename Policy>
diff --git a/packages/kokkos/core/unit_test/TestQuadPrecisionMath.hpp b/packages/kokkos/core/unit_test/TestQuadPrecisionMath.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e45d84e7e05b5beaed658fff20201968fd0d1050
--- /dev/null
+++ b/packages/kokkos/core/unit_test/TestQuadPrecisionMath.hpp
@@ -0,0 +1,109 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#include <Kokkos_Macros.hpp>
+#ifdef KOKKOS_ENABLE_LIBQUADMATH
+
+#include <impl/Kokkos_QuadPrecisionMath.hpp>
+#include <Kokkos_Core.hpp>
+
+#include <gtest/gtest.h>
+
+// FIXME instantiate only once for default host execution space
+TEST(TEST_CATEGORY, quad_precision_reductions) {
+  int const n = 100;
+  __float128 r;
+
+  Kokkos::parallel_reduce(
+      Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(0, n),
+      KOKKOS_LAMBDA(int i, __float128 &v) { v += static_cast<__float128>(i); },
+      r);
+  EXPECT_EQ(r, n * (n - 1) / 2);
+
+  Kokkos::parallel_reduce(
+      Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(0, n),
+      KOKKOS_LAMBDA(int i, __float128 &v) { v += static_cast<__float128>(i); },
+      Kokkos::Sum<__float128>(r));
+  EXPECT_EQ(r, n * (n - 1) / 2);
+
+  Kokkos::parallel_reduce(
+      Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(0, n),
+      KOKKOS_LAMBDA(int i, __float128 &v) {
+        if (v > static_cast<__float128>(i)) {
+          v = static_cast<__float128>(i);
+        }
+      },
+      Kokkos::Min<__float128>(r));
+  EXPECT_EQ(r, 0);
+
+  Kokkos::parallel_reduce(
+      Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(0, n),
+      KOKKOS_LAMBDA(int i, __float128 &v) {
+        if (v < static_cast<__float128>(i)) {
+          v = static_cast<__float128>(i);
+        }
+      },
+      Kokkos::Max<__float128>(r));
+  EXPECT_EQ(r, n - 1);
+
+  Kokkos::parallel_reduce(
+      Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(1, n),
+      KOKKOS_LAMBDA(int i, __float128 &v) { v *= static_cast<__float128>(i); },
+      Kokkos::Prod<__float128>(r));
+  EXPECT_FLOAT_EQ(r, tgammaq(n + 1));  // factorial(n) = tgamma(n+1)
+}
+
+TEST(TEST_CATEGORY, quad_precision_common_math_functions) {
+  Kokkos::parallel_for(
+      Kokkos::RangePolicy<Kokkos::DefaultHostExecutionSpace>(0, 1),
+      KOKKOS_LAMBDA(int) {
+        (void)Kokkos::Experimental::fabs((__float128)0);
+        (void)Kokkos::Experimental::sqrt((__float128)1);
+        (void)Kokkos::Experimental::exp((__float128)2);
+        (void)Kokkos::Experimental::sin((__float128)3);
+        (void)Kokkos::Experimental::cosh((__float128)4);
+      });
+}
+
+#endif
diff --git a/packages/kokkos/core/unit_test/TestRange.hpp b/packages/kokkos/core/unit_test/TestRange.hpp
index a6a6220f2dceea470414fb0d712796689f6d151c..d6b5d8fecc86173d3fa438cbca5b8242b48ddb36 100644
--- a/packages/kokkos/core/unit_test/TestRange.hpp
+++ b/packages/kokkos/core/unit_test/TestRange.hpp
@@ -317,10 +317,10 @@ struct TestRange {
           if (count(t) < min) min = count(t);
           if (count(t) > max) max = count(t);
         }
-        ASSERT_TRUE(min < max);
+        ASSERT_LT(min, max);
 
         // if ( ExecSpace::concurrency() > 2 ) {
-        //  ASSERT_TRUE( 2 * min < max );
+        //  ASSERT_LT( 2 * min, max );
         //}
       }
     }
@@ -361,10 +361,10 @@ struct TestRange {
           if (count(t) < min) min = count(t);
           if (count(t) > max) max = count(t);
         }
-        ASSERT_TRUE(min < max);
+        ASSERT_LT(min, max);
 
         // if ( ExecSpace::concurrency() > 2 ) {
-        //  ASSERT_TRUE( 2 * min < max );
+        //  ASSERT_LT( 2 * min, max );
         //}
       }
     }
diff --git a/packages/kokkos/core/unit_test/TestRangePolicyRequire.hpp b/packages/kokkos/core/unit_test/TestRangePolicyRequire.hpp
index 693f19613db6beb8c1c2a551574808de26633726..508b7192cb29aa87d3d28930d0babe0be1024432 100644
--- a/packages/kokkos/core/unit_test/TestRangePolicyRequire.hpp
+++ b/packages/kokkos/core/unit_test/TestRangePolicyRequire.hpp
@@ -309,10 +309,10 @@ struct TestRangeRequire {
           if (count(t) < min) min = count(t);
           if (count(t) > max) max = count(t);
         }
-        ASSERT_TRUE(min < max);
+        ASSERT_LT(min, max);
 
         // if ( ExecSpace::concurrency() > 2 ) {
-        //  ASSERT_TRUE( 2 * min < max );
+        //  ASSERT_LT( 2 * min, max );
         //}
       }
     }
@@ -353,10 +353,10 @@ struct TestRangeRequire {
           if (count(t) < min) min = count(t);
           if (count(t) > max) max = count(t);
         }
-        ASSERT_TRUE(min < max);
+        ASSERT_LT(min, max);
 
         // if ( ExecSpace::concurrency() > 2 ) {
-        //  ASSERT_TRUE( 2 * min < max );
+        //  ASSERT_LT( 2 * min, max );
         //}
       }
     }
diff --git a/packages/kokkos/core/unit_test/TestReduce.hpp b/packages/kokkos/core/unit_test/TestReduce.hpp
index 5f7fbd5623d6e8e4c25c261a0f092d79c1573fba..81e063f83e3ae4fba46f525756c262cb851d2068 100644
--- a/packages/kokkos/core/unit_test/TestReduce.hpp
+++ b/packages/kokkos/core/unit_test/TestReduce.hpp
@@ -539,6 +539,10 @@ class TestReduceDynamicView {
 
 }  // namespace
 
+// FIXME_OPENMPTARGET : The feature works with LLVM/13 on NVIDIA
+// architectures. The jenkins currently tests with LLVM/12.
+#if defined(KOKKOS_ENABLE_OPENMPTARGET) && defined(KOKKOS_COMPILER_CLANG) && \
+    (KOKKOS_COMPILER_CLANG >= 1300)
 TEST(TEST_CATEGORY, int64_t_reduce) {
   TestReduce<int64_t, TEST_EXECSPACE>(0);
   TestReduce<int64_t, TEST_EXECSPACE>(1000000);
@@ -563,7 +567,10 @@ TEST(TEST_CATEGORY, int64_t_reduce_dynamic_view) {
   TestReduceDynamicView<int64_t, TEST_EXECSPACE>(0);
   TestReduceDynamicView<int64_t, TEST_EXECSPACE>(1000000);
 }
+#endif
 
+// FIXME_OPENMPTARGET: Not yet implemented.
+#ifndef KOKKOS_ENABLE_OPENMPTARGET
 TEST(TEST_CATEGORY, int_combined_reduce) {
   using functor_type = CombinedReduceFunctorSameType<int64_t, TEST_EXECSPACE>;
   constexpr uint64_t nw = 1000;
@@ -626,4 +633,5 @@ TEST(TEST_CATEGORY, int_combined_reduce_mixed) {
   ASSERT_EQ(nsum, result2);
   ASSERT_EQ(nsum, result3_v());
 }
+#endif
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestReduceCombinatorical.hpp b/packages/kokkos/core/unit_test/TestReduceCombinatorical.hpp
index 68e7d746dd91a68046c4d074884ef5aef7519427..4664f265594b858e8879e7d2faa3aca62d320a0d 100644
--- a/packages/kokkos/core/unit_test/TestReduceCombinatorical.hpp
+++ b/packages/kokkos/core/unit_test/TestReduceCombinatorical.hpp
@@ -439,11 +439,11 @@ struct TestReduceCombinatoricalInstantiation {
                        Test::ReduceCombinatorical::AddPlus<double>(value));
     if ((Kokkos::DefaultExecutionSpace::concurrency() > 1) &&
         (ExecSpace::concurrency() > 1) && (expected_result > 0)) {
-      ASSERT_TRUE(expected_result < value);
+      ASSERT_LT(expected_result, value);
     } else if (((Kokkos::DefaultExecutionSpace::concurrency() > 1) ||
                 (ExecSpace::concurrency() > 1)) &&
                (expected_result > 0)) {
-      ASSERT_TRUE(expected_result <= value);
+      ASSERT_LE(expected_result, value);
     } else {
       ASSERT_EQ(expected_result, value);
     }
@@ -453,11 +453,11 @@ struct TestReduceCombinatoricalInstantiation {
     CallParallelReduce(args..., add);
     if ((Kokkos::DefaultExecutionSpace::concurrency() > 1) &&
         (ExecSpace::concurrency() > 1) && (expected_result > 0)) {
-      ASSERT_TRUE(expected_result < value);
+      ASSERT_LT(expected_result, value);
     } else if (((Kokkos::DefaultExecutionSpace::concurrency() > 1) ||
                 (ExecSpace::concurrency() > 1)) &&
                (expected_result > 0)) {
-      ASSERT_TRUE(expected_result <= value);
+      ASSERT_LE(expected_result, value);
     } else {
       ASSERT_EQ(expected_result, value);
     }
diff --git a/packages/kokkos/core/unit_test/TestReducers.hpp b/packages/kokkos/core/unit_test/TestReducers.hpp
index 35f0e231fd2a7b1e88bbf4be568532aa5c219e3f..0d5f7fe7ba538524e0119c950f01469c7aa48a83 100644
--- a/packages/kokkos/core/unit_test/TestReducers.hpp
+++ b/packages/kokkos/core/unit_test/TestReducers.hpp
@@ -296,7 +296,8 @@ struct TestReducers {
     Scalar reference_sum = 0;
 
     for (int i = 0; i < N; i++) {
-      h_values(i) = (Scalar)(rand() % 100);
+      int denom   = sizeof(Scalar) <= 2 ? 10 : 100;
+      h_values(i) = (Scalar)(rand() % denom);
       reference_sum += h_values(i);
     }
     Kokkos::deep_copy(values, h_values);
diff --git a/packages/kokkos/core/unit_test/TestReducers_d.hpp b/packages/kokkos/core/unit_test/TestReducers_d.hpp
index e2254a1c1fe653b22c3e6b9a9ebad50d07a9eb89..2d5802cdd4fcde24e8ac1dfe0f8d42ba9eaf396b 100644
--- a/packages/kokkos/core/unit_test/TestReducers_d.hpp
+++ b/packages/kokkos/core/unit_test/TestReducers_d.hpp
@@ -64,4 +64,49 @@ TEST(TEST_CATEGORY, reducers_struct) {
   TestReducers<array_reduce<float, 7>, TEST_EXECSPACE>::test_sum(1031);
 #endif
 }
+
+TEST(TEST_CATEGORY, reducers_half_t) {
+  using ThisTestType = Kokkos::Experimental::half_t;
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(2);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(101);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(202);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(303);
+
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_prod(5);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_prod(10);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_prod(15);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_prod(20);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_prod(25);
+}
+
+TEST(TEST_CATEGORY, reducers_int8_t) {
+  using ThisTestType = int8_t;
+
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(1);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(2);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(3);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(4);
+
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_prod(1);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_prod(2);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_prod(3);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_prod(4);
+}
+
+#if !defined(KOKKOS_ENABLE_HIP) && !defined(KOKKOS_ENABLE_OPENMPTARGET)
+// TODO - resolve: "Kokkos_HIP_Vectorization.hpp:80:15: error: call to
+//                 implicitly-deleted default constructor of 'conv_type'
+//                   conv_type tmp_in;"
+//
+// TODO - resolve:  4: [  FAILED  ] openmptarget.reducers_point_t (1 ms)
+TEST(TEST_CATEGORY, reducers_point_t) {
+  using ThisTestType = point_t;
+
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(1);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(2);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(3);
+  TestReducers<ThisTestType, TEST_EXECSPACE>::test_sum(4);
+}
+#endif  // !KOKKOS_ENABLE_HIP && !KOKKOS_ENABLE_OPENMPTARGET
+
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestReductions.hpp b/packages/kokkos/core/unit_test/TestReductions.hpp
index 949ca7eaf30a4746a8fec355f1b62c035c83d041..1fa8a2e92e68f7c3bf34e6cc4cc96b29b73071f3 100644
--- a/packages/kokkos/core/unit_test/TestReductions.hpp
+++ b/packages/kokkos/core/unit_test/TestReductions.hpp
@@ -45,8 +45,6 @@
 #ifndef KOKKOS_TEST_REDUCTIONS_HPP
 #define KOKKOS_TEST_REDUCTIONS_HPP
 #include <Kokkos_Macros.hpp>
-#ifndef KOKKOS_ENABLE_OPENMPTARGET
 #include <TestReduce.hpp>
-#endif
 #include <TestCXX11Deduction.hpp>
 #endif
diff --git a/packages/kokkos/core/unit_test/TestReductions_DeviceView.hpp b/packages/kokkos/core/unit_test/TestReductions_DeviceView.hpp
index 17563de335e5b6a6170985e392ea8ae0de5ae8c1..6ffa11b11ca2d639bd9fd930a733d41ae7950482 100644
--- a/packages/kokkos/core/unit_test/TestReductions_DeviceView.hpp
+++ b/packages/kokkos/core/unit_test/TestReductions_DeviceView.hpp
@@ -32,11 +32,17 @@ void test_reduce_device_view(int64_t N, PolicyType policy,
   typename ExecSpace::execution_space().fence();
   double time_fence0 = timer.seconds();
   Kokkos::deep_copy(result, 0);
+
+  // We need a warm-up to get reasonable results
+  Kokkos::parallel_reduce("Test::ReduceDeviceView::TestReducer", policy,
+                          functor,
+                          Kokkos::Sum<int64_t, TEST_EXECSPACE>(result));
+  Kokkos::fence();
+
   timer.reset();
   bool is_async = time0 < time_fence0;
 
   // Test Reducer
-
   Kokkos::parallel_reduce("Test::ReduceDeviceView::TestReducer", policy,
                           functor,
                           Kokkos::Sum<int64_t, TEST_EXECSPACE>(result));
@@ -75,11 +81,11 @@ void test_reduce_device_view(int64_t N, PolicyType policy,
 
   ASSERT_EQ(N, scalar_result);
   if (is_async) {
-    ASSERT_TRUE(time1 < time_fence1);
+    ASSERT_LT(time1, time_fence1);
   }
   if (is_async) {
-    ASSERT_TRUE(time2 < time_fence2);
-    ASSERT_TRUE(time3 > time_fence3);
+    ASSERT_LT(time2, time_fence2);
+    ASSERT_GT(time3, time_fence3);
   }
 }
 
@@ -128,8 +134,6 @@ TEST(TEST_CATEGORY, reduce_device_view_mdrange_policy) {
       MDRangePolicyFunctor());
 }
 
-// FIXME_HIP
-#ifndef KOKKOS_ENABLE_HIP
 TEST(TEST_CATEGORY, reduce_device_view_team_policy) {
 // FIXME_SYCL The number of workgroups on CUDA devices can not be larger than
 // 65535
@@ -145,5 +149,4 @@ TEST(TEST_CATEGORY, reduce_device_view_team_policy) {
       TeamPolicyFunctor(1024));
 #endif
 }
-#endif
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/TestStackTrace.hpp b/packages/kokkos/core/unit_test/TestStackTrace.hpp
index 284332f3f85e87b9f8fc030084ecc78448da4e38..d34d0f92e959277e7f6a66c0718ce381cd794e61 100644
--- a/packages/kokkos/core/unit_test/TestStackTrace.hpp
+++ b/packages/kokkos/core/unit_test/TestStackTrace.hpp
@@ -73,10 +73,10 @@ void test_stacktrace(bool bTerminate, bool bCustom = true) {
 
     if (bDynamic) {
       printf("test_f1: %s \n", foutput.c_str());
-      ASSERT_TRUE(std::string::npos != foutput.find("stacktrace_test_f1"));
+      ASSERT_NE(std::string::npos, foutput.find("stacktrace_test_f1"));
       for (auto x : {"stacktrace_test_f0", "stacktrace_test_f2",
                      "stacktrace_test_f3", "stacktrace_test_f4"}) {
-        ASSERT_TRUE(std::string::npos == foutput.find(x));
+        ASSERT_EQ(std::string::npos, foutput.find(x));
       }
     }
   }
@@ -92,7 +92,7 @@ void test_stacktrace(bool bTerminate, bool bCustom = true) {
                   foutput.find("Test::stacktrace_test_f1"));
       for (auto x : {"stacktrace_test_f0", "stacktrace_test_f2",
                      "stacktrace_test_f3", "stacktrace_test_f4"}) {
-        ASSERT_TRUE(std::string::npos == foutput.find(x));
+        ASSERT_EQ(std::string::npos, foutput.find(x));
       }
     }
   }
@@ -114,7 +114,7 @@ void test_stacktrace(bool bTerminate, bool bCustom = true) {
       std::string foutput = sstream.str();
       printf("test_f3: %s \n", foutput.c_str());
       for (auto x : {"stacktrace_test_f1", "stacktrace_test_f3"}) {
-        ASSERT_TRUE(std::string::npos != foutput.find(x));
+        ASSERT_NE(std::string::npos, foutput.find(x));
       }
     }
     // TODO make sure stacktrace_test_f2/4 don't show up
@@ -129,7 +129,7 @@ void test_stacktrace(bool bTerminate, bool bCustom = true) {
       std::string foutput = sstream.str();
       printf("demangled test_f3: %s \n", foutput.c_str());
       for (auto x : {"stacktrace_test_f1", "stacktrace_test_f3"}) {
-        ASSERT_TRUE(std::string::npos != foutput.find(x));
+        ASSERT_NE(std::string::npos, foutput.find(x));
       }
     }
 
diff --git a/packages/kokkos/core/unit_test/TestTeam.hpp b/packages/kokkos/core/unit_test/TestTeam.hpp
index 97ddfd4cf58518bfa494eedf4445ba68fdb1132a..a5e3de85bbc49508a2fe3c456860da9aa0b8af57 100644
--- a/packages/kokkos/core/unit_test/TestTeam.hpp
+++ b/packages/kokkos/core/unit_test/TestTeam.hpp
@@ -137,8 +137,10 @@ struct TestTeamPolicy {
     Kokkos::TeamPolicy<ExecSpace, NoOpTag> none_auto(
         smallest_work, smallest_work, smallest_work);
 #endif
+    (void)none_auto;
     Kokkos::TeamPolicy<ExecSpace, NoOpTag> both_auto(
         smallest_work, Kokkos::AUTO(), Kokkos::AUTO());
+    (void)both_auto;
     // FIXME_OPENMPTARGET temporary restriction for team size to be at least 32
 #ifdef KOKKOS_ENABLE_OPENMPTARGET
     Kokkos::TeamPolicy<ExecSpace, NoOpTag> auto_vector(smallest_work, 32,
@@ -147,8 +149,10 @@ struct TestTeamPolicy {
     Kokkos::TeamPolicy<ExecSpace, NoOpTag> auto_vector(
         smallest_work, smallest_work, Kokkos::AUTO());
 #endif
+    (void)auto_vector;
     Kokkos::TeamPolicy<ExecSpace, NoOpTag> auto_team(
         smallest_work, Kokkos::AUTO(), smallest_work);
+    (void)auto_team;
   }
 
   static void test_for(const size_t league_size) {
@@ -970,7 +974,11 @@ struct ClassNoShmemSizeFunction {
                 double *, ExecSpace,
                 Kokkos::MemoryTraits<Kokkos::Unmanaged> >::shmem_size(1600);
 
-    int team_size = 8;
+#ifdef KOKKOS_ENABLE_SYCL
+    int team_size = 4;
+#else
+    int team_size      = 8;
+#endif
     if (team_size > ExecSpace::concurrency())
       team_size = ExecSpace::concurrency();
     {
@@ -1115,7 +1123,11 @@ void test_team_mulit_level_scratch_test_lambda() {
       Kokkos::View<double *, ExecSpace,
                    Kokkos::MemoryTraits<Kokkos::Unmanaged> >::shmem_size(1600);
 
+#ifdef KOKKOS_ENABLE_SYCL
+  int team_size = 4;
+#else
   int team_size = 8;
+#endif
   if (team_size > ExecSpace::concurrency())
     team_size = ExecSpace::concurrency();
 
@@ -1400,7 +1412,7 @@ struct TestTeamBroadcast<
     // above because the functor switches it back.
     bool setValue = ((lid % ts) != tid);
 
-    teamMember.team_broadcast([&](value_type &var) { var *= 2; }, value,
+    teamMember.team_broadcast([&](value_type &var) { var += var; }, value,
                               lid % ts);
     teamMember.team_broadcast([&](bool &bVar) { bVar = !bVar; }, setValue,
                               lid % ts);
@@ -1465,7 +1477,7 @@ struct TestTeamBroadcast<
     value_type expected_result = 0;
     for (unsigned int i = 0; i < league_size; i++) {
       value_type val =
-          (value_type((i % team_size) * 3) + off) * (value_type)team_size;
+          (value_type((i % team_size) * 3) + off) * value_type(team_size);
       expected_result += val;
     }
     // For comparison purposes treat the reduction as a random walk in the
diff --git a/packages/kokkos/core/unit_test/TestTeamBasic.hpp b/packages/kokkos/core/unit_test/TestTeamBasic.hpp
index 87c010ac2a0c5701916049532a715c6a5addce15..17899f63b1f7816cff75a34ccdce0b42d0ee1b3e 100644
--- a/packages/kokkos/core/unit_test/TestTeamBasic.hpp
+++ b/packages/kokkos/core/unit_test/TestTeamBasic.hpp
@@ -105,6 +105,75 @@ TEST(TEST_CATEGORY, team_broadcast_long) {
                     long>::test_teambroadcast(1000, 1);
 }
 
+// FIXME_OPENMPTARGET CI fails with
+// Libomptarget error: Copying data from device failed.
+// Possibly, because long_wrapper is not trivially-copyable.
+#ifndef KOKKOS_ENABLE_OPENMPTARGET
+struct long_wrapper {
+  long value;
+
+  KOKKOS_FUNCTION
+  long_wrapper() : value(0) {}
+
+  KOKKOS_FUNCTION
+  long_wrapper(long val) : value(val) {}
+
+  KOKKOS_FUNCTION
+  friend void operator+=(long_wrapper& lhs, const long_wrapper& rhs) {
+    lhs.value += rhs.value;
+  }
+
+  KOKKOS_FUNCTION
+  friend void operator+=(volatile long_wrapper& lhs,
+                         const volatile long_wrapper& rhs) {
+    lhs.value += rhs.value;
+  }
+
+  KOKKOS_FUNCTION
+  void operator=(const long_wrapper& other) { value = other.value; }
+
+  KOKKOS_FUNCTION
+  void operator=(const volatile long_wrapper& other) volatile {
+    value = other.value;
+  }
+  KOKKOS_FUNCTION
+  operator long() const { return value; }
+};
+}  // namespace Test
+
+namespace Kokkos {
+template <>
+struct reduction_identity<Test::long_wrapper>
+    : public reduction_identity<long> {};
+}  // namespace Kokkos
+
+namespace Test {
+
+// Test for non-arithmetic type
+TEST(TEST_CATEGORY, team_broadcast_long_wrapper) {
+  static_assert(!std::is_arithmetic<long_wrapper>::value, "");
+
+  TestTeamBroadcast<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static>,
+                    long_wrapper>::test_teambroadcast(0, 1);
+  TestTeamBroadcast<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic>,
+                    long_wrapper>::test_teambroadcast(0, 1);
+
+  TestTeamBroadcast<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static>,
+                    long_wrapper>::test_teambroadcast(2, 1);
+  TestTeamBroadcast<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic>,
+                    long_wrapper>::test_teambroadcast(2, 1);
+  TestTeamBroadcast<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static>,
+                    long_wrapper>::test_teambroadcast(16, 1);
+  TestTeamBroadcast<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic>,
+                    long_wrapper>::test_teambroadcast(16, 1);
+
+  TestTeamBroadcast<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static>,
+                    long_wrapper>::test_teambroadcast(1000, 1);
+  TestTeamBroadcast<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic>,
+                    long_wrapper>::test_teambroadcast(1000, 1);
+}
+#endif
+
 TEST(TEST_CATEGORY, team_broadcast_char) {
   {
     TestTeamBroadcast<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static>,
diff --git a/packages/kokkos/core/unit_test/TestTeamReductionScan.hpp b/packages/kokkos/core/unit_test/TestTeamReductionScan.hpp
index 3db0eafa339de221a8dad8feb3cf7b3fa62027f2..836134afe0cd4d537520b12c80dd4efaafc21f38 100644
--- a/packages/kokkos/core/unit_test/TestTeamReductionScan.hpp
+++ b/packages/kokkos/core/unit_test/TestTeamReductionScan.hpp
@@ -53,14 +53,8 @@ TEST(TEST_CATEGORY, team_reduction_scan) {
   TestScanTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic> >(0);
   TestScanTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static> >(10);
   TestScanTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic> >(10);
-// FIXME_HIP
-#ifdef KOKKOS_ENABLE_HIP
-  if (!std::is_same<TEST_EXECSPACE, Kokkos::Experimental::HIP>::value)
-#endif
-  {
-    TestScanTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static> >(10000);
-    TestScanTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic> >(10000);
-  }
+  TestScanTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static> >(10000);
+  TestScanTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic> >(10000);
 }
 
 TEST(TEST_CATEGORY, team_long_reduce) {
diff --git a/packages/kokkos/core/unit_test/TestTeamScratch.hpp b/packages/kokkos/core/unit_test/TestTeamScratch.hpp
index 75ca3587629ded5f5cc2dd2f3b8ef6623e8a07f7..bab937273ddee06bb55b17b4fefc567c98bac30b 100644
--- a/packages/kokkos/core/unit_test/TestTeamScratch.hpp
+++ b/packages/kokkos/core/unit_test/TestTeamScratch.hpp
@@ -54,15 +54,8 @@ TEST(TEST_CATEGORY, team_shared_request) {
 }
 
 TEST(TEST_CATEGORY, team_scratch_request) {
-  // FIXME_HIP the parallel_reduce in this test requires a team size larger than
-  // 256. Fixed in ROCm 3.9
-#if defined(KOKKOS_ENABLE_HIP) && (HIP_VERSION < 309)
-  if (!std::is_same<TEST_EXECSPACE, Kokkos::Experimental::HIP>::value)
-#endif
-  {
-    TestScratchTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static> >();
-    TestScratchTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic> >();
-  }
+  TestScratchTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Static> >();
+  TestScratchTeam<TEST_EXECSPACE, Kokkos::Schedule<Kokkos::Dynamic> >();
 }
 
 #if defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA)
@@ -78,21 +71,14 @@ TEST(TEST_CATEGORY, scratch_align) { TestScratchAlignment<TEST_EXECSPACE>(); }
 TEST(TEST_CATEGORY, shmem_size) { TestShmemSize<TEST_EXECSPACE>(); }
 
 TEST(TEST_CATEGORY, multi_level_scratch) {
-  // FIXME_HIP the parallel_for and the parallel_reduce in this test requires a
-  // team size larger than 256. Fixed In ROCm 3.9
   // FIXME_OPENMPTARGET This unit test needs ~350KB of scratch memory for L0 and
   // L1 combined per team. Currently OpenMPTarget cannot allocate this high
   // amount of scratch memory.
 #if !defined(KOKKOS_ENABLE_OPENMPTARGET)
-#if defined(KOKKOS_ENABLE_HIP) && (HIP_VERSION < 309)
-  if (!std::is_same<TEST_EXECSPACE, Kokkos::Experimental::HIP>::value)
-#endif
-  {
-    TestMultiLevelScratchTeam<TEST_EXECSPACE,
-                              Kokkos::Schedule<Kokkos::Static> >();
-    TestMultiLevelScratchTeam<TEST_EXECSPACE,
-                              Kokkos::Schedule<Kokkos::Dynamic> >();
-  }
+  TestMultiLevelScratchTeam<TEST_EXECSPACE,
+                            Kokkos::Schedule<Kokkos::Static> >();
+  TestMultiLevelScratchTeam<TEST_EXECSPACE,
+                            Kokkos::Schedule<Kokkos::Dynamic> >();
 #endif
 }
 
diff --git a/packages/kokkos/core/unit_test/TestTeamTeamSize.hpp b/packages/kokkos/core/unit_test/TestTeamTeamSize.hpp
index 992e80397bacb9b5dc9a0746ca2543a1792cce22..f64c5b8809a214d4e2376e43df29d7900eccd1de 100644
--- a/packages/kokkos/core/unit_test/TestTeamTeamSize.hpp
+++ b/packages/kokkos/core/unit_test/TestTeamTeamSize.hpp
@@ -110,9 +110,9 @@ void test_team_policy_max_recommended_static_size(int scratch_size) {
   int team_size_rec_reduce = p.team_size_recommended(
       FunctorReduce<T, N, PolicyType, S>(), Kokkos::ParallelReduceTag());
 
-  ASSERT_TRUE(team_size_max_for >= team_size_rec_for);
-  ASSERT_TRUE(team_size_max_reduce >= team_size_rec_reduce);
-  ASSERT_TRUE(team_size_max_for >= team_size_max_reduce);
+  ASSERT_GE(team_size_max_for, team_size_rec_for);
+  ASSERT_GE(team_size_max_reduce, team_size_rec_reduce);
+  ASSERT_GE(team_size_max_for, team_size_max_reduce);
 
   Kokkos::parallel_for(PolicyType(10000, team_size_max_for, 4)
                            .set_scratch_size(0, Kokkos::PerTeam(scratch_size)),
@@ -122,13 +122,6 @@ void test_team_policy_max_recommended_static_size(int scratch_size) {
                        FunctorFor<T, N, PolicyType, S>());
   MyArray<T, N> val;
   double n_leagues = 10000;
-  // FIXME_HIP
-#ifdef KOKKOS_ENABLE_HIP
-  if (N == 2)
-    n_leagues = 1000;
-  else
-    n_leagues = 500;
-#endif
 
   Kokkos::parallel_reduce(
       PolicyType(n_leagues, team_size_max_reduce, 4)
diff --git a/packages/kokkos/core/unit_test/TestTeamVector.hpp b/packages/kokkos/core/unit_test/TestTeamVector.hpp
index ba11dc07a962989f2826a3d0def3649112c00da6..dbed67475615606915cfcc05959de312f9eacbfd 100644
--- a/packages/kokkos/core/unit_test/TestTeamVector.hpp
+++ b/packages/kokkos/core/unit_test/TestTeamVector.hpp
@@ -44,7 +44,7 @@
 
 #include <Kokkos_Core.hpp>
 
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <iostream>
 #include <cstdlib>
 #include <cstdint>
@@ -111,7 +111,7 @@ struct functor_team_for {
 
         if (test != value) {
           KOKKOS_IMPL_DO_NOT_USE_PRINTF(
-              "FAILED team_parallel_for %i %i %f %f\n", team.league_rank(),
+              "FAILED team_parallel_for %i %i %lf %lf\n", team.league_rank(),
               team.team_rank(), static_cast<double>(test),
               static_cast<double>(value));
           flag() = 1;
@@ -321,10 +321,9 @@ struct functor_team_vector_for {
 
         if (test != value) {
           KOKKOS_IMPL_DO_NOT_USE_PRINTF(
-              "FAILED team_vector_parallel_for %i %i %f %f\n",
+              "FAILED team_vector_parallel_for %i %i %lf %lf\n",
               team.league_rank(), team.team_rank(), static_cast<double>(test),
               static_cast<double>(value));
-
           flag() = 1;
         }
       });
@@ -372,7 +371,7 @@ struct functor_team_vector_reduce {
       if (test != value) {
         if (team.league_rank() == 0) {
           KOKKOS_IMPL_DO_NOT_USE_PRINTF(
-              "FAILED team_vector_parallel_reduce %i %i %f %f %lu\n",
+              "FAILED team_vector_parallel_reduce %i %i %lf %lf %lu\n",
               team.league_rank(), team.team_rank(), static_cast<double>(test),
               static_cast<double>(value),
               static_cast<unsigned long>(sizeof(Scalar)));
@@ -424,7 +423,7 @@ struct functor_team_vector_reduce_reducer {
 
       if (test != value) {
         KOKKOS_IMPL_DO_NOT_USE_PRINTF(
-            "FAILED team_vector_parallel_reduce_reducer %i %i %f %f\n",
+            "FAILED team_vector_parallel_reduce_reducer %i %i %lf %lf\n",
             team.league_rank(), team.team_rank(), static_cast<double>(test),
             static_cast<double>(value));
 
@@ -471,8 +470,9 @@ struct functor_vec_single {
 
     if (value2 != (value * Scalar(nEnd - nStart))) {
       KOKKOS_IMPL_DO_NOT_USE_PRINTF(
-          "FAILED vector_single broadcast %i %i %f %f\n", team.league_rank(),
-          team.team_rank(), (double)value2, (double)value);
+          "FAILED vector_single broadcast %i %i %lf %lf\n", team.league_rank(),
+          team.team_rank(), static_cast<double>(value2),
+          static_cast<double>(value));
 
       flag() = 1;
     }
@@ -523,7 +523,7 @@ struct functor_vec_for {
         }
 
         if (test != value) {
-          KOKKOS_IMPL_DO_NOT_USE_PRINTF("FAILED vector_par_for %i %i %f %f\n",
+          KOKKOS_IMPL_DO_NOT_USE_PRINTF("FAILED vector_par_for %i %i %lf %lf\n",
                                         team.league_rank(), team.team_rank(),
                                         static_cast<double>(test),
                                         static_cast<double>(value));
@@ -560,10 +560,9 @@ struct functor_vec_red {
       for (int i = 0; i < 13; i++) test += i;
 
       if (test != value) {
-        KOKKOS_IMPL_DO_NOT_USE_PRINTF("FAILED vector_par_reduce %i %i %f %f\n",
-                                      team.league_rank(), team.team_rank(),
-                                      (double)test, (double)value);
-
+        KOKKOS_IMPL_DO_NOT_USE_PRINTF(
+            "FAILED vector_par_reduce %i %i %lf %lf\n", team.league_rank(),
+            team.team_rank(), (double)test, (double)value);
         flag() = 1;
       }
     });
@@ -600,7 +599,7 @@ struct functor_vec_red_reducer {
 
       if (test != value) {
         KOKKOS_IMPL_DO_NOT_USE_PRINTF(
-            "FAILED vector_par_reduce_reducer %i %i %f %f\n",
+            "FAILED vector_par_reduce_reducer %i %i %lf %lf\n",
             team.league_rank(), team.team_rank(), (double)test, (double)value);
 
         flag() = 1;
@@ -630,9 +629,10 @@ struct functor_vec_scan {
 
                               if (test != val) {
                                 KOKKOS_IMPL_DO_NOT_USE_PRINTF(
-                                    "FAILED vector_par_scan %i %i %f %f\n",
+                                    "FAILED vector_par_scan %i %i %lf %lf\n",
                                     team.league_rank(), team.team_rank(),
-                                    (double)test, (double)val);
+                                    static_cast<double>(test),
+                                    static_cast<double>(val));
 
                                 flag() = 1;
                               }
@@ -723,7 +723,12 @@ template <class ExecutionSpace>
 bool Test(int test) {
   bool passed = true;
 
+// With SYCL 33*8 exceeds the maximum work group size
+#ifdef KOKKOS_ENABLE_SYCL
+  int team_size = 31;
+#else
   int team_size = 33;
+#endif
   if (team_size > int(ExecutionSpace::concurrency()))
     team_size = int(ExecutionSpace::concurrency());
   passed = passed && test_scalar<int, ExecutionSpace>(317, team_size, test);
@@ -856,7 +861,7 @@ template <typename ScalarType, class DeviceType>
 class TestTripleNestedReduce {
  public:
   using execution_space = DeviceType;
-  using size_type       = typename execution_space::size_type;
+  using size_type = typename execution_space::size_type;
 
   TestTripleNestedReduce(const size_type &, const size_type, const size_type &,
                          const size_type) {}
@@ -1000,17 +1005,24 @@ TEST(TEST_CATEGORY, triple_nested_parallelism) {
 // With KOKKOS_ENABLE_DEBUG enabled, the functor uses too many registers to run
 // with a team size of 32 on GPUs, 16 is the max possible (at least on a K80
 // GPU) See https://github.com/kokkos/kokkos/issues/1513
+// For Intel GPUs, the requested workgroup size is just too large here.
 #if defined(KOKKOS_ENABLE_DEBUG) && defined(KOKKOS_ENABLE_CUDA)
-  if (!std::is_same<TEST_EXECSPACE, Kokkos::Cuda>::value) {
+  if (!std::is_same<TEST_EXECSPACE, Kokkos::Cuda>::value)
+#elif defined(KOKKOS_ENABLE_SYCL)
+  if (!std::is_same<TEST_EXECSPACE, Kokkos::Experimental::SYCL>::value)
 #endif
+  {
     TestTripleNestedReduce<double, TEST_EXECSPACE>(8192, 2048, 32, 32);
     TestTripleNestedReduce<double, TEST_EXECSPACE>(8192, 2048, 32, 16);
-#if defined(KOKKOS_ENABLE_DEBUG) && defined(KOKKOS_ENABLE_CUDA)
   }
+#if defined(KOKKOS_ENABLE_SYCL)
+  if (!std::is_same<TEST_EXECSPACE, Kokkos::Experimental::SYCL>::value)
 #endif
+  {
+    TestTripleNestedReduce<double, TEST_EXECSPACE>(8192, 2048, 16, 33);
+    TestTripleNestedReduce<double, TEST_EXECSPACE>(8192, 2048, 16, 19);
+  }
   TestTripleNestedReduce<double, TEST_EXECSPACE>(8192, 2048, 16, 16);
-  TestTripleNestedReduce<double, TEST_EXECSPACE>(8192, 2048, 16, 33);
-  TestTripleNestedReduce<double, TEST_EXECSPACE>(8192, 2048, 16, 19);
   TestTripleNestedReduce<double, TEST_EXECSPACE>(8192, 2048, 7, 16);
 }
 #endif
diff --git a/packages/kokkos/core/unit_test/TestTeamVectorRange.hpp b/packages/kokkos/core/unit_test/TestTeamVectorRange.hpp
index 7342ebad8433526719b52058ff6d6b75e41a107a..c4116b91392e2020ecf0a030f96536c3a47a6dfa 100644
--- a/packages/kokkos/core/unit_test/TestTeamVectorRange.hpp
+++ b/packages/kokkos/core/unit_test/TestTeamVectorRange.hpp
@@ -44,7 +44,7 @@
 
 #include <Kokkos_Core.hpp>
 
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <iostream>
 #include <cstdlib>
 #include <cstdint>
@@ -280,7 +280,7 @@ struct functor_teamvector_for {
 
         if (test != value) {
           KOKKOS_IMPL_DO_NOT_USE_PRINTF(
-              "FAILED teamvector_parallel_for %i %i %f %f\n",
+              "FAILED teamvector_parallel_for %i %i %lf %lf\n",
               team.league_rank(), team.team_rank(), static_cast<double>(test),
               static_cast<double>(value));
           flag() = 1;
@@ -493,7 +493,12 @@ template <class ExecutionSpace>
 bool Test(int test) {
   bool passed = true;
 
+// With SYCL 33*8 exceeds the maximum work group size
+#ifdef KOKKOS_ENABLE_SYCL
+  int team_size = 31;
+#else
   int team_size = 33;
+#endif
   if (team_size > int(ExecutionSpace::concurrency()))
     team_size = int(ExecutionSpace::concurrency());
   passed = passed && test_scalar<int, ExecutionSpace>(317, team_size, test);
diff --git a/packages/kokkos/core/unit_test/TestTemplateMetaFunctions.hpp b/packages/kokkos/core/unit_test/TestTemplateMetaFunctions.hpp
index a0bc7c4304a040a10bc182e5d23d7c9ba08c4110..a0d00ded1b1586e67eb6bc09f93cf386239c3e2d 100644
--- a/packages/kokkos/core/unit_test/TestTemplateMetaFunctions.hpp
+++ b/packages/kokkos/core/unit_test/TestTemplateMetaFunctions.hpp
@@ -138,72 +138,38 @@ struct SumInitJoinFinalValueTypeArray {
   }
 };
 
-template <class Scalar, class ExecutionSpace>
-struct SumWrongInitJoinFinalValueType {
-  using execution_space = ExecutionSpace;
-  using type            = typename Kokkos::View<Scalar*, execution_space>;
-  using value_type      = Scalar;
-
-  type view;
-
-  SumWrongInitJoinFinalValueType(type view_) : view(view_) {}
-
-  KOKKOS_INLINE_FUNCTION
-  void init(double& val) const { val = double(); }
-
-  KOKKOS_INLINE_FUNCTION
-  void join(volatile value_type& val, const value_type& src) const {
-    val += src;
-  }
-
-  KOKKOS_INLINE_FUNCTION
-  void operator()(int /*i*/, value_type& val) const { val += value_type(); }
-};
-
 template <class Scalar, class ExecutionSpace>
 void TestTemplateMetaFunctions() {
-  using type = typename Kokkos::View<Scalar*, ExecutionSpace>;
-  type a("A", 100);
-  /*
-    int sum_plain_has_init_arg = Kokkos::Impl::FunctorHasInit< SumPlain<Scalar,
-    ExecutionSpace>, Scalar & >::value; ASSERT_EQ( sum_plain_has_init_arg, 0 );
-    int sum_initjoinfinalvaluetype_has_init_arg = Kokkos::Impl::FunctorHasInit<
-    SumInitJoinFinalValueType<Scalar, ExecutionSpace>, Scalar >::value;
-    ASSERT_EQ( sum_initjoinfinalvaluetype_has_init_arg, 1 );
-    int sum_initjoinfinalvaluetype_has_init_arg2 = Kokkos::Impl::FunctorHasInit<
-    SumInitJoinFinalValueType2<Scalar,ExecutionSpace>, Scalar >::value;
-    ASSERT_EQ( sum_initjoinfinalvaluetype_has_init_arg2, 1 );
-    int sum_wronginitjoinfinalvaluetype_has_init_arg =
-    Kokkos::Impl::FunctorHasInit< SumWrongInitJoinFinalValueType<Scalar,
-    ExecutionSpace>, Scalar >::value; ASSERT_EQ(
-    sum_wronginitjoinfinalvaluetype_has_init_arg, 0 );
-
-    //int sum_initjoinfinalvaluetypearray_has_init_arg =
-    Kokkos::Impl::FunctorHasInit< SumInitJoinFinalValueTypeArray<Scalar,
-    ExecutionSpace>, Scalar[] >::value;
-    //ASSERT_EQ( sum_initjoinfinalvaluetypearray_has_init_arg, 1 );
-
-    //printf( "Values Init: %i %i %i\n", sum_plain_has_init_arg,
-    sum_initjoinfinalvaluetype_has_init_arg,
-    sum_wronginitjoinfinalvaluetype_has_init_arg );
-
-    int sum_plain_has_join_arg = Kokkos::Impl::FunctorHasJoin< SumPlain<Scalar,
-    ExecutionSpace>, Scalar >::value; ASSERT_EQ( sum_plain_has_join_arg, 0 );
-    int sum_initjoinfinalvaluetype_has_join_arg = Kokkos::Impl::FunctorHasJoin<
-    SumInitJoinFinalValueType<Scalar, ExecutionSpace>, Scalar >::value;
-    ASSERT_EQ( sum_initjoinfinalvaluetype_has_join_arg, 1 );
-    int sum_initjoinfinalvaluetype_has_join_arg2 = Kokkos::Impl::FunctorHasJoin<
-    SumInitJoinFinalValueType2<Scalar, ExecutionSpace>, Scalar >::value;
-    ASSERT_EQ( sum_initjoinfinalvaluetype_has_join_arg2, 1 );
-    int sum_wronginitjoinfinalvaluetype_has_join_arg =
-    Kokkos::Impl::FunctorHasJoin< SumWrongInitJoinFinalValueType<Scalar,
-    ExecutionSpace>, Scalar >::value; ASSERT_EQ(
-    sum_wronginitjoinfinalvaluetype_has_join_arg, 0 );
-
-    //printf( "Values Join: %i %i %i\n", sum_plain_has_join_arg,
-    sum_initjoinfinalvaluetype_has_join_arg,
-    sum_wronginitjoinfinalvaluetype_has_join_arg );
-  */
+  static_assert(
+      Kokkos::Impl::ReduceFunctorHasInit<SumPlain<Scalar, ExecutionSpace>,
+                                         Scalar&>::value == false,
+      "");
+  static_assert(
+      Kokkos::Impl::ReduceFunctorHasInit<
+          SumInitJoinFinalValueType<Scalar, ExecutionSpace>>::value == true,
+      "");
+  static_assert(
+      Kokkos::Impl::ReduceFunctorHasInit<
+          SumInitJoinFinalValueType2<Scalar, ExecutionSpace>>::value == true,
+      "");
+
+  static_assert(
+      Kokkos::Impl::ReduceFunctorHasInit<
+          SumInitJoinFinalValueTypeArray<Scalar, ExecutionSpace>>::value ==
+          true,
+      "");
+
+  static_assert(Kokkos::Impl::ReduceFunctorHasJoin<
+                    SumPlain<Scalar, ExecutionSpace>>::value == false,
+                "");
+  static_assert(
+      Kokkos::Impl::ReduceFunctorHasJoin<
+          SumInitJoinFinalValueType<Scalar, ExecutionSpace>>::value == true,
+      "");
+  static_assert(
+      Kokkos::Impl::ReduceFunctorHasJoin<
+          SumInitJoinFinalValueType2<Scalar, ExecutionSpace>>::value == true,
+      "");
 }
 
 }  // namespace
diff --git a/packages/kokkos/core/unit_test/TestTypeList.cpp b/packages/kokkos/core/unit_test/TestTypeList.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e450d11562819c756334daeea91f3f448df624db
--- /dev/null
+++ b/packages/kokkos/core/unit_test/TestTypeList.cpp
@@ -0,0 +1,73 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#include <impl/Kokkos_Utilities.hpp>
+
+using TypeList2 = Kokkos::Impl::type_list<void, bool>;
+using TypeList3 = Kokkos::Impl::type_list<char, short, int>;
+using TypeList223 =
+    Kokkos::Impl::type_list<void, bool, void, bool, char, short, int>;
+using TypeList223Void   = Kokkos::Impl::type_list<void, void>;
+using TypeList223NoVoid = Kokkos::Impl::type_list<bool, bool, char, short, int>;
+
+// concat_type_list
+using ConcatTypeList2 = Kokkos::Impl::concat_type_list_t<TypeList2>;
+static_assert(std::is_same<TypeList2, ConcatTypeList2>::value,
+              "concat_type_list of a single type_list failed");
+
+using ConcatTypeList223 =
+    Kokkos::Impl::concat_type_list_t<TypeList2, TypeList2, TypeList3>;
+static_assert(std::is_same<TypeList223, ConcatTypeList223>::value,
+              "concat_type_list of three type_lists failed");
+
+// filter_type_list
+using FilterTypeList223Void =
+    Kokkos::Impl::filter_type_list_t<std::is_void, TypeList223>;
+static_assert(std::is_same<TypeList223Void, FilterTypeList223Void>::value,
+              "filter_type_list with predicate value==true failed");
+
+using FilterTypeList223NoVoid =
+    Kokkos::Impl::filter_type_list_t<std::is_void, TypeList223, false>;
+static_assert(std::is_same<TypeList223NoVoid, FilterTypeList223NoVoid>::value,
+              "filter_type_list with predicate value==false failed");
diff --git a/packages/kokkos/core/unit_test/TestViewAPI.hpp b/packages/kokkos/core/unit_test/TestViewAPI.hpp
index 570281f9fd66a230e69b9bb924a84a0078e12168..73531e6196f0ca145789ef98f680328ece747df9 100644
--- a/packages/kokkos/core/unit_test/TestViewAPI.hpp
+++ b/packages/kokkos/core/unit_test/TestViewAPI.hpp
@@ -1060,12 +1060,12 @@ class TestViewAPI {
     dView4 dx, dy, dz;
     hView4 hx, hy, hz;
 
-    ASSERT_TRUE(dx.data() == nullptr);
-    ASSERT_TRUE(dy.data() == nullptr);
-    ASSERT_TRUE(dz.data() == nullptr);
-    ASSERT_TRUE(hx.data() == nullptr);
-    ASSERT_TRUE(hy.data() == nullptr);
-    ASSERT_TRUE(hz.data() == nullptr);
+    ASSERT_EQ(dx.data(), nullptr);
+    ASSERT_EQ(dy.data(), nullptr);
+    ASSERT_EQ(dz.data(), nullptr);
+    ASSERT_EQ(hx.data(), nullptr);
+    ASSERT_EQ(hy.data(), nullptr);
+    ASSERT_EQ(hz.data(), nullptr);
     ASSERT_EQ(dx.extent(0), 0u);
     ASSERT_EQ(dy.extent(0), 0u);
     ASSERT_EQ(dz.extent(0), 0u);
@@ -1116,11 +1116,11 @@ class TestViewAPI {
 
     ASSERT_EQ(dx.use_count(), size_t(2));
 
-    ASSERT_FALSE(dx.data() == nullptr);
-    ASSERT_FALSE(const_dx.data() == nullptr);
-    ASSERT_FALSE(unmanaged_dx.data() == nullptr);
-    ASSERT_FALSE(unmanaged_from_ptr_dx.data() == nullptr);
-    ASSERT_FALSE(dy.data() == nullptr);
+    ASSERT_NE(dx.data(), nullptr);
+    ASSERT_NE(const_dx.data(), nullptr);
+    ASSERT_NE(unmanaged_dx.data(), nullptr);
+    ASSERT_NE(unmanaged_from_ptr_dx.data(), nullptr);
+    ASSERT_NE(dy.data(), nullptr);
     ASSERT_NE(dx, dy);
 
     ASSERT_EQ(dx.extent(0), unsigned(N0));
@@ -1257,19 +1257,19 @@ class TestViewAPI {
     ASSERT_NE(dx, dz);
 
     dx = dView4();
-    ASSERT_TRUE(dx.data() == nullptr);
-    ASSERT_FALSE(dy.data() == nullptr);
-    ASSERT_FALSE(dz.data() == nullptr);
+    ASSERT_EQ(dx.data(), nullptr);
+    ASSERT_NE(dy.data(), nullptr);
+    ASSERT_NE(dz.data(), nullptr);
 
     dy = dView4();
-    ASSERT_TRUE(dx.data() == nullptr);
-    ASSERT_TRUE(dy.data() == nullptr);
-    ASSERT_FALSE(dz.data() == nullptr);
+    ASSERT_EQ(dx.data(), nullptr);
+    ASSERT_EQ(dy.data(), nullptr);
+    ASSERT_NE(dz.data(), nullptr);
 
     dz = dView4();
-    ASSERT_TRUE(dx.data() == nullptr);
-    ASSERT_TRUE(dy.data() == nullptr);
-    ASSERT_TRUE(dz.data() == nullptr);
+    ASSERT_EQ(dx.data(), nullptr);
+    ASSERT_EQ(dy.data(), nullptr);
+    ASSERT_EQ(dz.data(), nullptr);
   }
 
   static void run_test_deep_copy_empty() {
@@ -1304,7 +1304,7 @@ class TestViewAPI {
   static void check_auto_conversion_to_const(
       const Kokkos::View<const DataType, device> &arg_const,
       const Kokkos::View<DataType, device> &arg) {
-    ASSERT_TRUE(arg_const == arg);
+    ASSERT_EQ(arg_const, arg);
   }
 
   static void run_test_const() {
@@ -1317,8 +1317,8 @@ class TestViewAPI {
     const_typeX xc = x;
     const_typeR xr = x;
 
-    ASSERT_TRUE(xc == x);
-    ASSERT_TRUE(x == xc);
+    ASSERT_EQ(xc, x);
+    ASSERT_EQ(x, xc);
 
     // For CUDA the constant random access View does not return
     // an lvalue reference due to retrieving through texture cache
@@ -1327,7 +1327,7 @@ class TestViewAPI {
     if (!std::is_same<typename device::execution_space, Kokkos::Cuda>::value)
 #endif
     {
-      ASSERT_TRUE(x.data() == xr.data());
+      ASSERT_EQ(x.data(), xr.data());
     }
 
     // typeX xf = xc; // Setting non-const from const must not compile.
@@ -1440,29 +1440,29 @@ class TestViewAPI {
     const_vector_right_type cvr2 = Kokkos::subview(mv, Kokkos::ALL(), 1);
     const_vector_right_type cvr3 = Kokkos::subview(mv, Kokkos::ALL(), 2);
 
-    ASSERT_TRUE(&v1[0] == &v1(0));
-    ASSERT_TRUE(&v1[0] == &mv(0, 0));
-    ASSERT_TRUE(&v2[0] == &mv(0, 1));
-    ASSERT_TRUE(&v3[0] == &mv(0, 2));
-
-    ASSERT_TRUE(&cv1[0] == &mv(0, 0));
-    ASSERT_TRUE(&cv2[0] == &mv(0, 1));
-    ASSERT_TRUE(&cv3[0] == &mv(0, 2));
-
-    ASSERT_TRUE(&vr1[0] == &mv(0, 0));
-    ASSERT_TRUE(&vr2[0] == &mv(0, 1));
-    ASSERT_TRUE(&vr3[0] == &mv(0, 2));
-
-    ASSERT_TRUE(&cvr1[0] == &mv(0, 0));
-    ASSERT_TRUE(&cvr2[0] == &mv(0, 1));
-    ASSERT_TRUE(&cvr3[0] == &mv(0, 2));
-
-    ASSERT_TRUE(&mv1(0, 0) == &mv(1, 2));
-    ASSERT_TRUE(&mv1(1, 1) == &mv(2, 3));
-    ASSERT_TRUE(&mv1(3, 2) == &mv(4, 4));
-    ASSERT_TRUE(&mvr1(0, 0) == &mv_right(1, 2));
-    ASSERT_TRUE(&mvr1(1, 1) == &mv_right(2, 3));
-    ASSERT_TRUE(&mvr1(3, 2) == &mv_right(4, 4));
+    ASSERT_EQ(&v1[0], &v1(0));
+    ASSERT_EQ(&v1[0], &mv(0, 0));
+    ASSERT_EQ(&v2[0], &mv(0, 1));
+    ASSERT_EQ(&v3[0], &mv(0, 2));
+
+    ASSERT_EQ(&cv1[0], &mv(0, 0));
+    ASSERT_EQ(&cv2[0], &mv(0, 1));
+    ASSERT_EQ(&cv3[0], &mv(0, 2));
+
+    ASSERT_EQ(&vr1[0], &mv(0, 0));
+    ASSERT_EQ(&vr2[0], &mv(0, 1));
+    ASSERT_EQ(&vr3[0], &mv(0, 2));
+
+    ASSERT_EQ(&cvr1[0], &mv(0, 0));
+    ASSERT_EQ(&cvr2[0], &mv(0, 1));
+    ASSERT_EQ(&cvr3[0], &mv(0, 2));
+
+    ASSERT_EQ(&mv1(0, 0), &mv(1, 2));
+    ASSERT_EQ(&mv1(1, 1), &mv(2, 3));
+    ASSERT_EQ(&mv1(3, 2), &mv(4, 4));
+    ASSERT_EQ(&mvr1(0, 0), &mv_right(1, 2));
+    ASSERT_EQ(&mvr1(1, 1), &mv_right(2, 3));
+    ASSERT_EQ(&mvr1(3, 2), &mv_right(4, 4));
 
     const_vector_type c_cv1(v1);
     typename vector_type::const_type c_cv2(v2);
diff --git a/packages/kokkos/core/unit_test/TestViewAPI_e.hpp b/packages/kokkos/core/unit_test/TestViewAPI_e.hpp
index a5dc6cf29a467bd576bd96bca52f90b3db26324b..d4f484a530c952a33b20dada3222180c7785f06a 100644
--- a/packages/kokkos/core/unit_test/TestViewAPI_e.hpp
+++ b/packages/kokkos/core/unit_test/TestViewAPI_e.hpp
@@ -54,23 +54,24 @@ namespace Test {
 TEST(TEST_CATEGORY, view_remap) {
   enum { N0 = 3, N1 = 2, N2 = 8, N3 = 9 };
 
-#ifdef KOKKOS_ENABLE_CUDA
+#if defined(KOKKOS_ENABLE_CUDA)
 #define EXECSPACE                                                     \
   std::conditional<std::is_same<TEST_EXECSPACE, Kokkos::Cuda>::value, \
                    Kokkos::CudaHostPinnedSpace, TEST_EXECSPACE>::type
-#else
-#ifdef KOKKOS_ENABLE_HIP
+#elif defined(KOKKOS_ENABLE_HIP)
 #define EXECSPACE                                                     \
   std::conditional<                                                   \
       std::is_same<TEST_EXECSPACE, Kokkos::Experimental::HIP>::value, \
       Kokkos::Experimental::HIPHostPinnedSpace, TEST_EXECSPACE>::type
-#else
-#if defined(KOKKOS_ENABLE_OPENMPTARGET) || defined(KOKKOS_ENABLE_SYCL)
+#elif defined(KOKKOS_ENABLE_SYCL)
+#define EXECSPACE                                                      \
+  std::conditional<                                                    \
+      std::is_same<TEST_EXECSPACE, Kokkos::Experimental::SYCL>::value, \
+      Kokkos::Experimental::SYCLHostUSMSpace, TEST_EXECSPACE>::type
+#elif defined(KOKKOS_ENABLE_OPENMPTARGET)
 #define EXECSPACE Kokkos::HostSpace
 #else
 #define EXECSPACE TEST_EXECSPACE
-#endif
-#endif
 #endif
 
   using output_type =
diff --git a/packages/kokkos/core/unit_test/TestViewCopy_a.hpp b/packages/kokkos/core/unit_test/TestViewCopy_a.hpp
index e25cb9e39ca6fd4c3cd45ef2b60b404ed82c03e7..ced0aa3828cffabfc196fd9dac146718c0f005d3 100644
--- a/packages/kokkos/core/unit_test/TestViewCopy_a.hpp
+++ b/packages/kokkos/core/unit_test/TestViewCopy_a.hpp
@@ -96,10 +96,10 @@ TEST(TEST_CATEGORY, view_copy_tests) {
   auto host = Kokkos::DefaultHostExecutionSpace();
 
   constexpr bool DevExecCanAccessHost =
-      Kokkos::Impl::SpaceAccessibility<typename TEST_EXECSPACE::execution_space,
-                                       Kokkos::HostSpace>::accessible;
+      Kokkos::SpaceAccessibility<typename TEST_EXECSPACE::execution_space,
+                                 Kokkos::HostSpace>::accessible;
 
-  constexpr bool HostExecCanAccessDev = Kokkos::Impl::SpaceAccessibility<
+  constexpr bool HostExecCanAccessDev = Kokkos::SpaceAccessibility<
       typename Kokkos::HostSpace::execution_space,
       typename TEST_EXECSPACE::memory_space>::accessible;
 
diff --git a/packages/kokkos/core/unit_test/TestViewMapping_a.hpp b/packages/kokkos/core/unit_test/TestViewMapping_a.hpp
index fdbda099176c79410c1be6599546f09aba3269dc..974d7c98cafb56c91df55b425913b14c0dfd3ca1 100644
--- a/packages/kokkos/core/unit_test/TestViewMapping_a.hpp
+++ b/packages/kokkos/core/unit_test/TestViewMapping_a.hpp
@@ -768,8 +768,8 @@ void test_view_mapping() {
 
     ASSERT_EQ(vr1.extent(0), N);
 
-    if (Kokkos::Impl::SpaceAccessibility<
-            Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+    if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                   typename Space::memory_space>::accessible) {
       for (int i = 0; i < N; ++i) data[i] = i + 1;
       for (int i = 0; i < N; ++i) ASSERT_EQ(vr1[i], i + 1);
       for (int i = 0; i < N; ++i) ASSERT_EQ(cr1[i], i + 1);
@@ -815,8 +815,8 @@ void test_view_mapping() {
 
     ASSERT_EQ(vr1.extent(0), N);
 
-    if (Kokkos::Impl::SpaceAccessibility<
-            Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+    if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                   typename Space::memory_space>::accessible) {
       for (int i = 0; i < N; ++i) vr1(i) = i + 1;
       for (int i = 0; i < N; ++i) ASSERT_EQ(vr1[i], i + 1);
       for (int i = 0; i < N; ++i) ASSERT_EQ(cr1[i], i + 1);
diff --git a/packages/kokkos/core/unit_test/TestViewMapping_subview.hpp b/packages/kokkos/core/unit_test/TestViewMapping_subview.hpp
index 18db67400d6ea03ecb98e891150cb4a154311982..2a15a84380e7c6d979059a8342c64b9ee68d2eb9 100644
--- a/packages/kokkos/core/unit_test/TestViewMapping_subview.hpp
+++ b/packages/kokkos/core/unit_test/TestViewMapping_subview.hpp
@@ -81,7 +81,7 @@ struct TestViewMappingSubview {
   using DLT  = Kokkos::View<int** * [13][14], Kokkos::LayoutLeft, ExecSpace>;
   using DLS1 = Kokkos::Subview<DLT, range, int, int, int, int>;
 
-#if !defined(KOKKOS_IMPL_CUDA_VERSION_9_WORKAROUND)
+#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 1000
   static_assert(
       DLS1::rank == 1 &&
           std::is_same<typename DLS1::array_layout, Kokkos::LayoutLeft>::value,
@@ -92,7 +92,7 @@ struct TestViewMappingSubview {
   using DRT  = Kokkos::View<int** * [13][14], Kokkos::LayoutRight, ExecSpace>;
   using DRS1 = Kokkos::Subview<DRT, int, int, int, int, range>;
 
-#if !defined(KOKKOS_IMPL_CUDA_VERSION_9_WORKAROUND)
+#if defined(__CUDA_ARCH__) && __CUDA_ARCH__ >= 1000
   static_assert(
       DRS1::rank == 1 &&
           std::is_same<typename DRS1::array_layout, Kokkos::LayoutRight>::value,
diff --git a/packages/kokkos/core/unit_test/TestViewSubview.hpp b/packages/kokkos/core/unit_test/TestViewSubview.hpp
index 0125017d93786101e2a23a866effe9d8a5e5242d..93eb5476b57be796f5e1fbcb8e9b3db140bb1615 100644
--- a/packages/kokkos/core/unit_test/TestViewSubview.hpp
+++ b/packages/kokkos/core/unit_test/TestViewSubview.hpp
@@ -184,7 +184,7 @@ void test_auto_1d() {
   Kokkos::deep_copy(X_h, X);
   for (size_type j = 0; j < numCols; ++j) {
     for (size_type i = 0; i < numRows; ++i) {
-      ASSERT_TRUE(X_h(i, j) == ONE);
+      ASSERT_EQ(X_h(i, j), ONE);
     }
   }
 
@@ -194,7 +194,7 @@ void test_auto_1d() {
   Kokkos::deep_copy(X_h, X);
   for (size_type j = 0; j < numCols; ++j) {
     for (size_type i = 0; i < numRows; ++i) {
-      ASSERT_TRUE(X_h(i, j) == ZERO);
+      ASSERT_EQ(X_h(i, j), ZERO);
     }
   }
 
@@ -204,7 +204,7 @@ void test_auto_1d() {
   Kokkos::deep_copy(X_h, X);
   for (size_type j = 0; j < numCols; ++j) {
     for (size_type i = 0; i < numRows; ++i) {
-      ASSERT_TRUE(X_h(i, j) == TWO);
+      ASSERT_EQ(X_h(i, j), TWO);
     }
   }
 
@@ -216,7 +216,7 @@ void test_auto_1d() {
     Kokkos::fence();
     Kokkos::deep_copy(X_h, X);
     for (size_type i = 0; i < numRows; ++i) {
-      ASSERT_TRUE(X_h(i, j) == ZERO);
+      ASSERT_EQ(X_h(i, j), ZERO);
     }
 
     for (size_type jj = 0; jj < numCols; ++jj) {
@@ -226,7 +226,7 @@ void test_auto_1d() {
       Kokkos::fence();
       Kokkos::deep_copy(X_h, X);
       for (size_type i = 0; i < numRows; ++i) {
-        ASSERT_TRUE(X_h(i, jj) == ONE);
+        ASSERT_EQ(X_h(i, jj), ONE);
       }
     }
   }
@@ -240,38 +240,38 @@ void test_1d_strided_assignment_impl(bool a, bool b, bool c, bool d, int n,
   int col = n > 2 ? 2 : 0;
   int row = m > 2 ? 2 : 0;
 
-  if (Kokkos::Impl::SpaceAccessibility<
-          Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+  if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                 typename Space::memory_space>::accessible) {
     if (a) {
       Kokkos::View<double*, LD, Space> l1da =
           Kokkos::subview(l2d, Kokkos::ALL, row);
-      ASSERT_TRUE(&l1da(0) == &l2d(0, row));
+      ASSERT_EQ(&l1da(0), &l2d(0, row));
       if (n > 1) {
-        ASSERT_TRUE(&l1da(1) == &l2d(1, row));
+        ASSERT_EQ(&l1da(1), &l2d(1, row));
       }
     }
 
     if (b && n > 13) {
       Kokkos::View<double*, LD, Space> l1db =
           Kokkos::subview(l2d, std::pair<unsigned, unsigned>(2, 13), row);
-      ASSERT_TRUE(&l1db(0) == &l2d(2, row));
-      ASSERT_TRUE(&l1db(1) == &l2d(3, row));
+      ASSERT_EQ(&l1db(0), &l2d(2, row));
+      ASSERT_EQ(&l1db(1), &l2d(3, row));
     }
 
     if (c) {
       Kokkos::View<double*, LD, Space> l1dc =
           Kokkos::subview(l2d, col, Kokkos::ALL);
-      ASSERT_TRUE(&l1dc(0) == &l2d(col, 0));
+      ASSERT_EQ(&l1dc(0), &l2d(col, 0));
       if (m > 1) {
-        ASSERT_TRUE(&l1dc(1) == &l2d(col, 1));
+        ASSERT_EQ(&l1dc(1), &l2d(col, 1));
       }
     }
 
     if (d && m > 13) {
       Kokkos::View<double*, LD, Space> l1dd =
           Kokkos::subview(l2d, col, std::pair<unsigned, unsigned>(2, 13));
-      ASSERT_TRUE(&l1dd(0) == &l2d(col, 2));
-      ASSERT_TRUE(&l1dd(1) == &l2d(col, 3));
+      ASSERT_EQ(&l1dd(0), &l2d(col, 2));
+      ASSERT_EQ(&l1dd(1), &l2d(col, 3));
     }
   }
 }
@@ -326,8 +326,8 @@ void test_left_0(bool constr) {
   using view_static_8_type =
       Kokkos::View<int[2][3][4][5][2][3][4][5], Kokkos::LayoutLeft, Space>;
 
-  if (Kokkos::Impl::SpaceAccessibility<
-          Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+  if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                 typename Space::memory_space>::accessible) {
     view_static_8_type x_static_8("x_static_left_8");
 
     ASSERT_TRUE(x_static_8.span_is_contiguous());
@@ -337,7 +337,7 @@ void test_left_0(bool constr) {
 
     ASSERT_TRUE(x0.span_is_contiguous());
     ASSERT_EQ(x0.span(), 1);
-    ASSERT_TRUE(&x0() == &x_static_8(0, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_EQ(&x0(), &x_static_8(0, 0, 0, 0, 0, 0, 0, 0));
 
     Kokkos::View<int*, Kokkos::LayoutLeft, Space> x1;
     make_subview(constr, x1, x_static_8, Kokkos::pair<int, int>(0, 2), 1, 2, 3,
@@ -345,8 +345,8 @@ void test_left_0(bool constr) {
 
     ASSERT_TRUE(x1.span_is_contiguous());
     ASSERT_EQ(x1.span(), 2);
-    ASSERT_TRUE(&x1(0) == &x_static_8(0, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&x1(1) == &x_static_8(1, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&x1(0), &x_static_8(0, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&x1(1), &x_static_8(1, 1, 2, 3, 0, 1, 2, 3));
 
     Kokkos::View<int*, Kokkos::LayoutLeft, Space> x_deg1;
     make_subview(constr, x_deg1, x_static_8, Kokkos::pair<int, int>(0, 0), 1, 2,
@@ -369,10 +369,10 @@ void test_left_0(bool constr) {
                  Kokkos::pair<int, int>(0, 2), 1, 2, 3);
 
     ASSERT_TRUE(!x2.span_is_contiguous());
-    ASSERT_TRUE(&x2(0, 0) == &x_static_8(0, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&x2(1, 0) == &x_static_8(1, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&x2(0, 1) == &x_static_8(0, 1, 2, 3, 1, 1, 2, 3));
-    ASSERT_TRUE(&x2(1, 1) == &x_static_8(1, 1, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&x2(0, 0), &x_static_8(0, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&x2(1, 0), &x_static_8(1, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&x2(0, 1), &x_static_8(0, 1, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&x2(1, 1), &x_static_8(1, 1, 2, 3, 1, 1, 2, 3));
 
     // Kokkos::View< int**, Kokkos::LayoutLeft, Space > error_2 =
     Kokkos::View<int**, Kokkos::LayoutStride, Space> sx2;
@@ -380,10 +380,10 @@ void test_left_0(bool constr) {
                  Kokkos::pair<int, int>(0, 2), 1, 2, 3);
 
     ASSERT_TRUE(!sx2.span_is_contiguous());
-    ASSERT_TRUE(&sx2(0, 0) == &x_static_8(1, 0, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&sx2(1, 0) == &x_static_8(1, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&sx2(0, 1) == &x_static_8(1, 0, 2, 3, 1, 1, 2, 3));
-    ASSERT_TRUE(&sx2(1, 1) == &x_static_8(1, 1, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&sx2(0, 0), &x_static_8(1, 0, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&sx2(1, 0), &x_static_8(1, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&sx2(0, 1), &x_static_8(1, 0, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&sx2(1, 1), &x_static_8(1, 1, 2, 3, 1, 1, 2, 3));
 
     Kokkos::View<int****, Kokkos::LayoutStride, Space> sx4;
     make_subview(constr, sx4, x_static_8, 0,
@@ -402,9 +402,8 @@ void test_left_0(bool constr) {
       for (int i1 = 0; i1 < (int)sx4.extent(1); ++i1)
         for (int i2 = 0; i2 < (int)sx4.extent(2); ++i2)
           for (int i3 = 0; i3 < (int)sx4.extent(3); ++i3) {
-            ASSERT_TRUE(&sx4(i0, i1, i2, i3) == &x_static_8(0, 0 + i0, 1,
-                                                            1 + i1, 1, 0 + i2,
-                                                            2, 2 + i3));
+            ASSERT_EQ(&sx4(i0, i1, i2, i3),
+                      &x_static_8(0, 0 + i0, 1, 1 + i1, 1, 0 + i2, 2, 2 + i3));
           }
   }
 }
@@ -420,8 +419,8 @@ void test_left_1(bool use_constr) {
   using view_type =
       Kokkos::View<int*** * [2][3][4][5], Kokkos::LayoutLeft, Space>;
 
-  if (Kokkos::Impl::SpaceAccessibility<
-          Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+  if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                 typename Space::memory_space>::accessible) {
     view_type x8("x_left_8", 2, 3, 4, 5);
 
     ASSERT_TRUE(x8.span_is_contiguous());
@@ -430,15 +429,15 @@ void test_left_1(bool use_constr) {
     make_subview(use_constr, x0, x8, 0, 0, 0, 0, 0, 0, 0, 0);
 
     ASSERT_TRUE(x0.span_is_contiguous());
-    ASSERT_TRUE(&x0() == &x8(0, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_EQ(&x0(), &x8(0, 0, 0, 0, 0, 0, 0, 0));
 
     Kokkos::View<int*, Kokkos::LayoutLeft, Space> x1;
     make_subview(use_constr, x1, x8, Kokkos::pair<int, int>(0, 2), 1, 2, 3, 0,
                  1, 2, 3);
 
     ASSERT_TRUE(x1.span_is_contiguous());
-    ASSERT_TRUE(&x1(0) == &x8(0, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&x1(1) == &x8(1, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&x1(0), &x8(0, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&x1(1), &x8(1, 1, 2, 3, 0, 1, 2, 3));
 
     Kokkos::View<int*, Kokkos::LayoutLeft, Space> x1_deg1;
     make_subview(use_constr, x1_deg1, x8, Kokkos::pair<int, int>(0, 0), 1, 2, 3,
@@ -461,10 +460,10 @@ void test_left_1(bool use_constr) {
                  Kokkos::pair<int, int>(0, 2), 1, 2, 3);
 
     ASSERT_TRUE(!x2.span_is_contiguous());
-    ASSERT_TRUE(&x2(0, 0) == &x8(0, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&x2(1, 0) == &x8(1, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&x2(0, 1) == &x8(0, 1, 2, 3, 1, 1, 2, 3));
-    ASSERT_TRUE(&x2(1, 1) == &x8(1, 1, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&x2(0, 0), &x8(0, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&x2(1, 0), &x8(1, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&x2(0, 1), &x8(0, 1, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&x2(1, 1), &x8(1, 1, 2, 3, 1, 1, 2, 3));
 
     Kokkos::View<int**, Kokkos::LayoutLeft, Space> x2_deg2;
     make_subview(use_constr, x2_deg2, x8, Kokkos::pair<int, int>(2, 2), 2, 3, 4,
@@ -477,10 +476,10 @@ void test_left_1(bool use_constr) {
                  Kokkos::pair<int, int>(0, 2), 1, 2, 3);
 
     ASSERT_TRUE(!sx2.span_is_contiguous());
-    ASSERT_TRUE(&sx2(0, 0) == &x8(1, 0, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&sx2(1, 0) == &x8(1, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&sx2(0, 1) == &x8(1, 0, 2, 3, 1, 1, 2, 3));
-    ASSERT_TRUE(&sx2(1, 1) == &x8(1, 1, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&sx2(0, 0), &x8(1, 0, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&sx2(1, 0), &x8(1, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&sx2(0, 1), &x8(1, 0, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&sx2(1, 1), &x8(1, 1, 2, 3, 1, 1, 2, 3));
 
     Kokkos::View<int**, Kokkos::LayoutStride, Space> sx2_deg;
     make_subview(use_constr, sx2, x8, 1, Kokkos::pair<int, int>(0, 0), 2, 3,
@@ -520,8 +519,8 @@ template <class Space>
 void test_left_2() {
   using view_type = Kokkos::View<int****, Kokkos::LayoutLeft, Space>;
 
-  if (Kokkos::Impl::SpaceAccessibility<
-          Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+  if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                 typename Space::memory_space>::accessible) {
     view_type x4("x4", 2, 3, 4, 5);
 
     ASSERT_TRUE(x4.span_is_contiguous());
@@ -530,35 +529,35 @@ void test_left_2() {
         Kokkos::subview(x4, 0, 0, 0, 0);
 
     ASSERT_TRUE(x0.span_is_contiguous());
-    ASSERT_TRUE(&x0() == &x4(0, 0, 0, 0));
+    ASSERT_EQ(&x0(), &x4(0, 0, 0, 0));
 
     Kokkos::View<int*, Kokkos::LayoutLeft, Space> x1 =
         Kokkos::subview(x4, Kokkos::pair<int, int>(0, 2), 1, 2, 3);
 
     ASSERT_TRUE(x1.span_is_contiguous());
-    ASSERT_TRUE(&x1(0) == &x4(0, 1, 2, 3));
-    ASSERT_TRUE(&x1(1) == &x4(1, 1, 2, 3));
+    ASSERT_EQ(&x1(0), &x4(0, 1, 2, 3));
+    ASSERT_EQ(&x1(1), &x4(1, 1, 2, 3));
 
     Kokkos::View<int**, Kokkos::LayoutLeft, Space> x2 = Kokkos::subview(
         x4, Kokkos::pair<int, int>(0, 2), 1, Kokkos::pair<int, int>(1, 3), 2);
 
     ASSERT_TRUE(!x2.span_is_contiguous());
-    ASSERT_TRUE(&x2(0, 0) == &x4(0, 1, 1, 2));
-    ASSERT_TRUE(&x2(1, 0) == &x4(1, 1, 1, 2));
-    ASSERT_TRUE(&x2(0, 1) == &x4(0, 1, 2, 2));
-    ASSERT_TRUE(&x2(1, 1) == &x4(1, 1, 2, 2));
+    ASSERT_EQ(&x2(0, 0), &x4(0, 1, 1, 2));
+    ASSERT_EQ(&x2(1, 0), &x4(1, 1, 1, 2));
+    ASSERT_EQ(&x2(0, 1), &x4(0, 1, 2, 2));
+    ASSERT_EQ(&x2(1, 1), &x4(1, 1, 2, 2));
 
     // Kokkos::View< int**, Kokkos::LayoutLeft, Space > error_2 =
     Kokkos::View<int**, Kokkos::LayoutStride, Space> sx2 = Kokkos::subview(
         x4, 1, Kokkos::pair<int, int>(0, 2), 2, Kokkos::pair<int, int>(1, 4));
 
     ASSERT_TRUE(!sx2.span_is_contiguous());
-    ASSERT_TRUE(&sx2(0, 0) == &x4(1, 0, 2, 1));
-    ASSERT_TRUE(&sx2(1, 0) == &x4(1, 1, 2, 1));
-    ASSERT_TRUE(&sx2(0, 1) == &x4(1, 0, 2, 2));
-    ASSERT_TRUE(&sx2(1, 1) == &x4(1, 1, 2, 2));
-    ASSERT_TRUE(&sx2(0, 2) == &x4(1, 0, 2, 3));
-    ASSERT_TRUE(&sx2(1, 2) == &x4(1, 1, 2, 3));
+    ASSERT_EQ(&sx2(0, 0), &x4(1, 0, 2, 1));
+    ASSERT_EQ(&sx2(1, 0), &x4(1, 1, 2, 1));
+    ASSERT_EQ(&sx2(0, 1), &x4(1, 0, 2, 2));
+    ASSERT_EQ(&sx2(1, 1), &x4(1, 1, 2, 2));
+    ASSERT_EQ(&sx2(0, 2), &x4(1, 0, 2, 3));
+    ASSERT_EQ(&sx2(1, 2), &x4(1, 1, 2, 3));
 
     Kokkos::View<int****, Kokkos::LayoutStride, Space> sx4 =
         Kokkos::subview(x4, Kokkos::pair<int, int>(1, 2) /* of [2] */
@@ -586,8 +585,8 @@ template <class Space>
 void test_left_3() {
   using view_type = Kokkos::View<int**, Kokkos::LayoutLeft, Space>;
 
-  if (Kokkos::Impl::SpaceAccessibility<
-          Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+  if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                 typename Space::memory_space>::accessible) {
     view_type xm("x4", 10, 5);
 
     ASSERT_TRUE(xm.span_is_contiguous());
@@ -595,14 +594,14 @@ void test_left_3() {
     Kokkos::View<int, Kokkos::LayoutLeft, Space> x0 = Kokkos::subview(xm, 5, 3);
 
     ASSERT_TRUE(x0.span_is_contiguous());
-    ASSERT_TRUE(&x0() == &xm(5, 3));
+    ASSERT_EQ(&x0(), &xm(5, 3));
 
     Kokkos::View<int*, Kokkos::LayoutLeft, Space> x1 =
         Kokkos::subview(xm, Kokkos::ALL, 3);
 
     ASSERT_TRUE(x1.span_is_contiguous());
     for (int i = 0; i < int(xm.extent(0)); ++i) {
-      ASSERT_TRUE(&x1(i) == &xm(i, 3));
+      ASSERT_EQ(&x1(i), &xm(i, 3));
     }
 
     Kokkos::View<int**, Kokkos::LayoutLeft, Space> x2 =
@@ -611,7 +610,7 @@ void test_left_3() {
     ASSERT_TRUE(!x2.span_is_contiguous());
     for (int j = 0; j < int(x2.extent(1)); ++j)
       for (int i = 0; i < int(x2.extent(0)); ++i) {
-        ASSERT_TRUE(&x2(i, j) == &xm(1 + i, j));
+        ASSERT_EQ(&x2(i, j), &xm(1 + i, j));
       }
 
     Kokkos::View<int**, Kokkos::LayoutLeft, Space> x2c =
@@ -620,20 +619,20 @@ void test_left_3() {
     ASSERT_TRUE(x2c.span_is_contiguous());
     for (int j = 0; j < int(x2c.extent(1)); ++j)
       for (int i = 0; i < int(x2c.extent(0)); ++i) {
-        ASSERT_TRUE(&x2c(i, j) == &xm(i, 2 + j));
+        ASSERT_EQ(&x2c(i, j), &xm(i, 2 + j));
       }
 
     Kokkos::View<int**, Kokkos::LayoutLeft, Space> x2_n1 =
         Kokkos::subview(xm, std::pair<int, int>(1, 1), Kokkos::ALL);
 
-    ASSERT_TRUE(x2_n1.extent(0) == 0);
-    ASSERT_TRUE(x2_n1.extent(1) == xm.extent(1));
+    ASSERT_EQ(x2_n1.extent(0), 0);
+    ASSERT_EQ(x2_n1.extent(1), xm.extent(1));
 
     Kokkos::View<int**, Kokkos::LayoutLeft, Space> x2_n2 =
         Kokkos::subview(xm, Kokkos::ALL, std::pair<int, int>(1, 1));
 
-    ASSERT_TRUE(x2_n2.extent(0) == xm.extent(0));
-    ASSERT_TRUE(x2_n2.extent(1) == 0);
+    ASSERT_EQ(x2_n2.extent(0), xm.extent(0));
+    ASSERT_EQ(x2_n2.extent(1), 0);
   }
 }
 
@@ -644,46 +643,46 @@ void test_right_0(bool use_constr) {
   using view_static_8_type =
       Kokkos::View<int[2][3][4][5][2][3][4][5], Kokkos::LayoutRight, Space>;
 
-  if (Kokkos::Impl::SpaceAccessibility<
-          Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+  if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                 typename Space::memory_space>::accessible) {
     view_static_8_type x_static_8("x_static_right_8");
 
     Kokkos::View<int, Kokkos::LayoutRight, Space> x0;
     make_subview(use_constr, x0, x_static_8, 0, 0, 0, 0, 0, 0, 0, 0);
 
-    ASSERT_TRUE(&x0() == &x_static_8(0, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_EQ(&x0(), &x_static_8(0, 0, 0, 0, 0, 0, 0, 0));
 
     Kokkos::View<int*, Kokkos::LayoutRight, Space> x1;
     make_subview(use_constr, x1, x_static_8, 0, 1, 2, 3, 0, 1, 2,
                  Kokkos::pair<int, int>(1, 3));
 
-    ASSERT_TRUE(x1.extent(0) == 2);
-    ASSERT_TRUE(&x1(0) == &x_static_8(0, 1, 2, 3, 0, 1, 2, 1));
-    ASSERT_TRUE(&x1(1) == &x_static_8(0, 1, 2, 3, 0, 1, 2, 2));
+    ASSERT_EQ(x1.extent(0), 2);
+    ASSERT_EQ(&x1(0), &x_static_8(0, 1, 2, 3, 0, 1, 2, 1));
+    ASSERT_EQ(&x1(1), &x_static_8(0, 1, 2, 3, 0, 1, 2, 2));
 
     Kokkos::View<int**, Kokkos::LayoutRight, Space> x2;
     make_subview(use_constr, x2, x_static_8, 0, 1, 2,
                  Kokkos::pair<int, int>(1, 3), 0, 1, 2,
                  Kokkos::pair<int, int>(1, 3));
 
-    ASSERT_TRUE(x2.extent(0) == 2);
-    ASSERT_TRUE(x2.extent(1) == 2);
-    ASSERT_TRUE(&x2(0, 0) == &x_static_8(0, 1, 2, 1, 0, 1, 2, 1));
-    ASSERT_TRUE(&x2(1, 0) == &x_static_8(0, 1, 2, 2, 0, 1, 2, 1));
-    ASSERT_TRUE(&x2(0, 1) == &x_static_8(0, 1, 2, 1, 0, 1, 2, 2));
-    ASSERT_TRUE(&x2(1, 1) == &x_static_8(0, 1, 2, 2, 0, 1, 2, 2));
+    ASSERT_EQ(x2.extent(0), 2);
+    ASSERT_EQ(x2.extent(1), 2);
+    ASSERT_EQ(&x2(0, 0), &x_static_8(0, 1, 2, 1, 0, 1, 2, 1));
+    ASSERT_EQ(&x2(1, 0), &x_static_8(0, 1, 2, 2, 0, 1, 2, 1));
+    ASSERT_EQ(&x2(0, 1), &x_static_8(0, 1, 2, 1, 0, 1, 2, 2));
+    ASSERT_EQ(&x2(1, 1), &x_static_8(0, 1, 2, 2, 0, 1, 2, 2));
 
     // Kokkos::View< int**, Kokkos::LayoutRight, Space > error_2 =
     Kokkos::View<int**, Kokkos::LayoutStride, Space> sx2;
     make_subview(use_constr, sx2, x_static_8, 1, Kokkos::pair<int, int>(0, 2),
                  2, 3, Kokkos::pair<int, int>(0, 2), 1, 2, 3);
 
-    ASSERT_TRUE(sx2.extent(0) == 2);
-    ASSERT_TRUE(sx2.extent(1) == 2);
-    ASSERT_TRUE(&sx2(0, 0) == &x_static_8(1, 0, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&sx2(1, 0) == &x_static_8(1, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&sx2(0, 1) == &x_static_8(1, 0, 2, 3, 1, 1, 2, 3));
-    ASSERT_TRUE(&sx2(1, 1) == &x_static_8(1, 1, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(sx2.extent(0), 2);
+    ASSERT_EQ(sx2.extent(1), 2);
+    ASSERT_EQ(&sx2(0, 0), &x_static_8(1, 0, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&sx2(1, 0), &x_static_8(1, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&sx2(0, 1), &x_static_8(1, 0, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&sx2(1, 1), &x_static_8(1, 1, 2, 3, 1, 1, 2, 3));
 
     Kokkos::View<int****, Kokkos::LayoutStride, Space> sx4;
     make_subview(use_constr, sx4, x_static_8, 0,
@@ -696,17 +695,16 @@ void test_right_0(bool use_constr) {
                  2, Kokkos::pair<int, int>(2, 4) /* of [5] */
     );
 
-    ASSERT_TRUE(sx4.extent(0) == 2);
-    ASSERT_TRUE(sx4.extent(1) == 2);
-    ASSERT_TRUE(sx4.extent(2) == 2);
-    ASSERT_TRUE(sx4.extent(3) == 2);
+    ASSERT_EQ(sx4.extent(0), 2);
+    ASSERT_EQ(sx4.extent(1), 2);
+    ASSERT_EQ(sx4.extent(2), 2);
+    ASSERT_EQ(sx4.extent(3), 2);
     for (int i0 = 0; i0 < (int)sx4.extent(0); ++i0)
       for (int i1 = 0; i1 < (int)sx4.extent(1); ++i1)
         for (int i2 = 0; i2 < (int)sx4.extent(2); ++i2)
           for (int i3 = 0; i3 < (int)sx4.extent(3); ++i3) {
-            ASSERT_TRUE(&sx4(i0, i1, i2, i3) == &x_static_8(0, 0 + i0, 1,
-                                                            1 + i1, 1, 0 + i2,
-                                                            2, 2 + i3));
+            ASSERT_EQ(&sx4(i0, i1, i2, i3),
+                      &x_static_8(0, 0 + i0, 1, 1 + i1, 1, 0 + i2, 2, 2 + i3));
           }
   }
 }
@@ -722,21 +720,21 @@ void test_right_1(bool use_constr) {
   using view_type =
       Kokkos::View<int*** * [2][3][4][5], Kokkos::LayoutRight, Space>;
 
-  if (Kokkos::Impl::SpaceAccessibility<
-          Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+  if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                 typename Space::memory_space>::accessible) {
     view_type x8("x_right_8", 2, 3, 4, 5);
 
     Kokkos::View<int, Kokkos::LayoutRight, Space> x0;
     make_subview(use_constr, x0, x8, 0, 0, 0, 0, 0, 0, 0, 0);
 
-    ASSERT_TRUE(&x0() == &x8(0, 0, 0, 0, 0, 0, 0, 0));
+    ASSERT_EQ(&x0(), &x8(0, 0, 0, 0, 0, 0, 0, 0));
 
     Kokkos::View<int*, Kokkos::LayoutRight, Space> x1;
     make_subview(use_constr, x1, x8, 0, 1, 2, 3, 0, 1, 2,
                  Kokkos::pair<int, int>(1, 3));
 
-    ASSERT_TRUE(&x1(0) == &x8(0, 1, 2, 3, 0, 1, 2, 1));
-    ASSERT_TRUE(&x1(1) == &x8(0, 1, 2, 3, 0, 1, 2, 2));
+    ASSERT_EQ(&x1(0), &x8(0, 1, 2, 3, 0, 1, 2, 1));
+    ASSERT_EQ(&x1(1), &x8(0, 1, 2, 3, 0, 1, 2, 2));
 
     Kokkos::View<int*, Kokkos::LayoutRight, Space> x1_deg1;
     make_subview(use_constr, x1_deg1, x8, 0, 1, 2, 3, 0, 1, 2,
@@ -747,10 +745,10 @@ void test_right_1(bool use_constr) {
     make_subview(use_constr, x2, x8, 0, 1, 2, Kokkos::pair<int, int>(1, 3), 0,
                  1, 2, Kokkos::pair<int, int>(1, 3));
 
-    ASSERT_TRUE(&x2(0, 0) == &x8(0, 1, 2, 1, 0, 1, 2, 1));
-    ASSERT_TRUE(&x2(1, 0) == &x8(0, 1, 2, 2, 0, 1, 2, 1));
-    ASSERT_TRUE(&x2(0, 1) == &x8(0, 1, 2, 1, 0, 1, 2, 2));
-    ASSERT_TRUE(&x2(1, 1) == &x8(0, 1, 2, 2, 0, 1, 2, 2));
+    ASSERT_EQ(&x2(0, 0), &x8(0, 1, 2, 1, 0, 1, 2, 1));
+    ASSERT_EQ(&x2(1, 0), &x8(0, 1, 2, 2, 0, 1, 2, 1));
+    ASSERT_EQ(&x2(0, 1), &x8(0, 1, 2, 1, 0, 1, 2, 2));
+    ASSERT_EQ(&x2(1, 1), &x8(0, 1, 2, 2, 0, 1, 2, 2));
 
     Kokkos::View<int**, Kokkos::LayoutRight, Space> x2_deg2;
     make_subview(use_constr, x2_deg2, x8, 0, 1, 2, Kokkos::pair<int, int>(1, 3),
@@ -762,10 +760,10 @@ void test_right_1(bool use_constr) {
     make_subview(use_constr, sx2, x8, 1, Kokkos::pair<int, int>(0, 2), 2, 3,
                  Kokkos::pair<int, int>(0, 2), 1, 2, 3);
 
-    ASSERT_TRUE(&sx2(0, 0) == &x8(1, 0, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&sx2(1, 0) == &x8(1, 1, 2, 3, 0, 1, 2, 3));
-    ASSERT_TRUE(&sx2(0, 1) == &x8(1, 0, 2, 3, 1, 1, 2, 3));
-    ASSERT_TRUE(&sx2(1, 1) == &x8(1, 1, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&sx2(0, 0), &x8(1, 0, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&sx2(1, 0), &x8(1, 1, 2, 3, 0, 1, 2, 3));
+    ASSERT_EQ(&sx2(0, 1), &x8(1, 0, 2, 3, 1, 1, 2, 3));
+    ASSERT_EQ(&sx2(1, 1), &x8(1, 1, 2, 3, 1, 1, 2, 3));
 
     Kokkos::View<int**, Kokkos::LayoutStride, Space> sx2_deg;
     make_subview(use_constr, sx2_deg, x8, 1, Kokkos::pair<int, int>(0, 2), 2, 3,
@@ -803,8 +801,8 @@ template <class Space>
 void test_right_3() {
   using view_type = Kokkos::View<int**, Kokkos::LayoutRight, Space>;
 
-  if (Kokkos::Impl::SpaceAccessibility<
-          Kokkos::HostSpace, typename Space::memory_space>::accessible) {
+  if (Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                 typename Space::memory_space>::accessible) {
     view_type xm("x4", 10, 5);
 
     ASSERT_TRUE(xm.span_is_contiguous());
@@ -813,14 +811,14 @@ void test_right_3() {
         Kokkos::subview(xm, 5, 3);
 
     ASSERT_TRUE(x0.span_is_contiguous());
-    ASSERT_TRUE(&x0() == &xm(5, 3));
+    ASSERT_EQ(&x0(), &xm(5, 3));
 
     Kokkos::View<int*, Kokkos::LayoutRight, Space> x1 =
         Kokkos::subview(xm, 3, Kokkos::ALL);
 
     ASSERT_TRUE(x1.span_is_contiguous());
     for (int i = 0; i < int(xm.extent(1)); ++i) {
-      ASSERT_TRUE(&x1(i) == &xm(3, i));
+      ASSERT_EQ(&x1(i), &xm(3, i));
     }
 
     Kokkos::View<int**, Kokkos::LayoutRight, Space> x2c =
@@ -829,7 +827,7 @@ void test_right_3() {
     ASSERT_TRUE(x2c.span_is_contiguous());
     for (int j = 0; j < int(x2c.extent(1)); ++j)
       for (int i = 0; i < int(x2c.extent(0)); ++i) {
-        ASSERT_TRUE(&x2c(i, j) == &xm(1 + i, j));
+        ASSERT_EQ(&x2c(i, j), &xm(1 + i, j));
       }
 
     Kokkos::View<int**, Kokkos::LayoutRight, Space> x2 =
@@ -838,20 +836,20 @@ void test_right_3() {
     ASSERT_TRUE(!x2.span_is_contiguous());
     for (int j = 0; j < int(x2.extent(1)); ++j)
       for (int i = 0; i < int(x2.extent(0)); ++i) {
-        ASSERT_TRUE(&x2(i, j) == &xm(i, 2 + j));
+        ASSERT_EQ(&x2(i, j), &xm(i, 2 + j));
       }
 
     Kokkos::View<int**, Kokkos::LayoutRight, Space> x2_n1 =
         Kokkos::subview(xm, std::pair<int, int>(1, 1), Kokkos::ALL);
 
-    ASSERT_TRUE(x2_n1.extent(0) == 0);
-    ASSERT_TRUE(x2_n1.extent(1) == xm.extent(1));
+    ASSERT_EQ(x2_n1.extent(0), 0);
+    ASSERT_EQ(x2_n1.extent(1), xm.extent(1));
 
     Kokkos::View<int**, Kokkos::LayoutRight, Space> x2_n2 =
         Kokkos::subview(xm, Kokkos::ALL, std::pair<int, int>(1, 1));
 
-    ASSERT_TRUE(x2_n2.extent(0) == xm.extent(0));
-    ASSERT_TRUE(x2_n2.extent(1) == 0);
+    ASSERT_EQ(x2_n2.extent(0), xm.extent(0));
+    ASSERT_EQ(x2_n2.extent(1), 0);
   }
 }
 
@@ -979,7 +977,7 @@ struct CheckSubviewCorrectness_1D_1D {
     int errors = 0;
     Kokkos::parallel_reduce("CheckSubView_1D_1D", policy_t(0, b.size()), *this,
                             errors);
-    ASSERT_TRUE(errors == 0);
+    ASSERT_EQ(errors, 0);
   }
 
   KOKKOS_INLINE_FUNCTION
@@ -1005,7 +1003,7 @@ struct CheckSubviewCorrectness_1D_2D {
     int errors = 0;
     Kokkos::parallel_reduce("CheckSubView_1D_2D", policy_t(0, b.size()), *this,
                             errors);
-    ASSERT_TRUE(errors == 0);
+    ASSERT_EQ(errors, 0);
   }
 
   KOKKOS_INLINE_FUNCTION
@@ -1033,7 +1031,7 @@ struct CheckSubviewCorrectness_2D_3D {
     int errors = 0;
     Kokkos::parallel_reduce("CheckSubView_2D_3D", policy_t(0, b.size()), *this,
                             errors);
-    ASSERT_TRUE(errors == 0);
+    ASSERT_EQ(errors, 0);
   }
 
   KOKKOS_INLINE_FUNCTION
@@ -1068,7 +1066,7 @@ struct CheckSubviewCorrectness_3D_3D {
     int errors = 0;
     Kokkos::parallel_reduce("CheckSubView_3D_3D", policy_t(0, b.size()), *this,
                             errors);
-    ASSERT_TRUE(errors == 0);
+    ASSERT_EQ(errors, 0);
   }
 
   KOKKOS_INLINE_FUNCTION
@@ -1107,7 +1105,7 @@ struct CheckSubviewCorrectness_3D_4D {
     int errors = 0;
     Kokkos::parallel_reduce("CheckSubView_3D_4D", policy_t(0, b.size()), *this,
                             errors);
-    ASSERT_TRUE(errors == 0);
+    ASSERT_EQ(errors, 0);
   }
 
   KOKKOS_INLINE_FUNCTION
@@ -1165,7 +1163,7 @@ struct CheckSubviewCorrectness_3D_5D {
     int errors = 0;
     Kokkos::parallel_reduce("CheckSubView_3D_5D", policy_t(0, b.size()), *this,
                             errors);
-    ASSERT_TRUE(errors == 0);
+    ASSERT_EQ(errors, 0);
   }
 
   KOKKOS_INLINE_FUNCTION
diff --git a/packages/kokkos/core/unit_test/TestView_64bit.hpp b/packages/kokkos/core/unit_test/TestView_64bit.hpp
index 50626718b5774ddefa03a453402564986e831ed1..174a07ac1d5b6ca6e9ddc145617ea86bf51de314 100644
--- a/packages/kokkos/core/unit_test/TestView_64bit.hpp
+++ b/packages/kokkos/core/unit_test/TestView_64bit.hpp
@@ -49,9 +49,9 @@ namespace Test {
 template <class Device>
 void test_64bit() {
 #if defined(KOKKOS_ENABLE_CXX11_DISPATCH_LAMBDA)
-  // FIXME_SYCL The SYCL CUDA backend throws an error
+  // We are running out of device memory on Intel GPUs
 #ifdef KOKKOS_ENABLE_SYCL
-  int64_t N = 1000000000;
+  int64_t N = 4000000000;
 #else
   int64_t N = 5000000000;
 #endif
@@ -60,7 +60,7 @@ void test_64bit() {
     Kokkos::parallel_reduce(
         Kokkos::RangePolicy<typename Device::execution_space,
                             Kokkos::IndexType<int64_t>>(0, N),
-        KOKKOS_LAMBDA(const int64_t& /*i*/, int64_t& lsum) { lsum += 1; }, sum);
+        KOKKOS_LAMBDA(const int64_t&, int64_t& lsum) { lsum += 1; }, sum);
     ASSERT_EQ(N, sum);
   }
   {
@@ -111,7 +111,12 @@ void test_64bit() {
     ASSERT_EQ(N0 * N1, sum);
   }
   {
-    int N0    = 1024 * 1024 * 1500;
+// We are running out of device memory on Intel GPUs
+#ifdef KOKKOS_ENABLE_SYCL
+    int64_t N0 = 1024 * 1024 * 900;
+#else
+    int N0 = 1024 * 1024 * 1500;
+#endif
     int64_t P = 1713091;
     Kokkos::View<int*, Device> a("A", N0);
     Kokkos::parallel_for(
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_b.cpp b/packages/kokkos/core/unit_test/category_files/TestSYCLHostUSM_Category.hpp
similarity index 89%
rename from packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_b.cpp
rename to packages/kokkos/core/unit_test/category_files/TestSYCLHostUSM_Category.hpp
index 5eed2ca0d77b828b2431bfce0fe69c4da457bb95..0287829fd61e88d19449e7e82d0b9727a5413fb3 100644
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_b.cpp
+++ b/packages/kokkos/core/unit_test/category_files/TestSYCLHostUSM_Category.hpp
@@ -42,5 +42,12 @@
 //@HEADER
 */
 
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewAPI_b.hpp>
+#ifndef KOKKOS_TEST_SYCL_HOST_USM_SPACE_HPP
+#define KOKKOS_TEST_SYCL_HOST_USM_SPACE_HPP
+
+#include <gtest/gtest.h>
+
+#define TEST_CATEGORY sycl_host_usm
+#define TEST_EXECSPACE Kokkos::Experimental::SYCLHostUSMSpace
+
+#endif
diff --git a/packages/kokkos/core/unit_test/category_files/TestSYCLSharedUSMSpace_Category.hpp b/packages/kokkos/core/unit_test/category_files/TestSYCLSharedUSM_Category.hpp
similarity index 100%
rename from packages/kokkos/core/unit_test/category_files/TestSYCLSharedUSMSpace_Category.hpp
rename to packages/kokkos/core/unit_test/category_files/TestSYCLSharedUSM_Category.hpp
diff --git a/packages/kokkos/core/unit_test/configuration/test-code/test_config_arch_list.bash b/packages/kokkos/core/unit_test/configuration/test-code/test_config_arch_list.bash
index 5ff781b96fc0949361329a61baa4f966f6b8a93a..8fe8e2b5ecea429f750eb4ad8eff6588f4ae9691 100755
--- a/packages/kokkos/core/unit_test/configuration/test-code/test_config_arch_list.bash
+++ b/packages/kokkos/core/unit_test/configuration/test-code/test_config_arch_list.bash
@@ -4,7 +4,7 @@ HostArch=(SNB HSW SKX KNL)
 DeviceArch=(Kepler35 Kepler37 Pascal60 Pascal61 Volta70)
 if [ ! -z "$KOKKOS_HOST_ARCH_TEST" ]; then
   export KOKKOS_ARCH_TEST=1
-  HostArch=(WSM SNB HSW SKX WSM AMDAVX ARMv80 ARMv81 BDW KNC KNL BGQ Power7 Power8 Power9 Zen Zen2 ARMv8_ThunderX ARMv8_ThunderX2)
+  HostArch=(WSM SNB HSW SKX WSM AMDAVX ARMv80 ARMv81 BDW KNC KNL BGQ Power7 Power8 Power9 Zen Zen2 Zen3 ARMv8_ThunderX ARMv8_ThunderX2)
   DeviceArch=()
 fi
 
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_d.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_d.cpp
deleted file mode 100644
index bab29610a3d4ad2e812405ba96ed06c7e2dfb3b8..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_d.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewAPI_d.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_e.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_e.cpp
deleted file mode 100644
index fd227186d5668239b9d9fe3f6a1ae2b3d5510b32..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_e.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewAPI_e.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewCopy_a.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewCopy_a.cpp
deleted file mode 100644
index 669761df979cfd1458f1d5ea78acfb5738af0d38..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewCopy_a.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewCopy_a.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewCopy_b.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewCopy_b.cpp
deleted file mode 100644
index d367fd7e051f49495ce747f6f490bad795f94d86..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewCopy_b.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewCopy_b.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewMapping_a.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewMapping_a.cpp
deleted file mode 100644
index 01b284b2f562299b4f23cc197693c2baad40f38e..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewMapping_a.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewMapping_a.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewMapping_b.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewMapping_b.cpp
deleted file mode 100644
index e15228b1d772a5dba97ee434e17fdb18188a709a..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewMapping_b.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewMapping_b.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewMapping_subview.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewMapping_subview.cpp
deleted file mode 100644
index 52bbd42f292f4b865def36856913dfc6bbe0028f..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewMapping_subview.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewMapping_subview.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_a.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_a.cpp
deleted file mode 100644
index 4aeac8f13f4d28672c671a51c1eacfedbf0e92fd..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_a.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewAPI_a.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_b.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_b.cpp
deleted file mode 100644
index e5cb0103424fd022290998307f086aedaea0cb29..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_b.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewAPI_b.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_c.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_c.cpp
deleted file mode 100644
index a52fcb833ed2a0e959a25e36195460c1ed914a78..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_c.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewAPI_c.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_d.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_d.cpp
deleted file mode 100644
index e345cd9667526671ef898a0d1247343b47f6296c..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_d.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewAPI_d.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_e.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_e.cpp
deleted file mode 100644
index 61547df4f523969f8c93da8315fddb4467e5ade9..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewAPI_e.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewAPI_e.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewCopy_a.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewCopy_a.cpp
deleted file mode 100644
index 75a769bb947485e6e7459c1cb95b7b3b1c26f9b1..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewCopy_a.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewCopy_a.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewCopy_b.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewCopy_b.cpp
deleted file mode 100644
index 7d09f5c9f397b3723599aec64c3c50a6aa77a769..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewCopy_b.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewCopy_b.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewMapping_a.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewMapping_a.cpp
deleted file mode 100644
index ea03f43bd69a318095e6277f4db226241fc9a482..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewMapping_a.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewMapping_a.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewMapping_b.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewMapping_b.cpp
deleted file mode 100644
index 1f754e8f4996cbc3c0fbefd7000bff65451b19f0..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewMapping_b.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewMapping_b.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewMapping_subview.cpp b/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewMapping_subview.cpp
deleted file mode 100644
index 4af7057d2aa47db99a8325159e0ee737feff7767..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_ViewMapping_subview.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestCudaUVM_Category.hpp>
-#include <TestViewMapping_subview.hpp>
diff --git a/packages/kokkos/core/unit_test/cuda/TestCuda_InterOp_Init.cpp b/packages/kokkos/core/unit_test/cuda/TestCuda_InterOp_Init.cpp
index ee7181e1180fdb887a87190605565e42e897409c..d09d4edfdad12e7db332c279398247bfda9ca80a 100644
--- a/packages/kokkos/core/unit_test/cuda/TestCuda_InterOp_Init.cpp
+++ b/packages/kokkos/core/unit_test/cuda/TestCuda_InterOp_Init.cpp
@@ -60,7 +60,7 @@ __global__ void offset(int* p) {
 // Cuda.
 TEST(cuda, raw_cuda_interop) {
   int* p;
-  CUDA_SAFE_CALL(cudaMalloc(&p, sizeof(int) * 100));
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaMalloc(&p, sizeof(int) * 100));
   Kokkos::InitArguments arguments{-1, -1, -1, false};
   Kokkos::initialize(arguments);
 
@@ -70,11 +70,11 @@ TEST(cuda, raw_cuda_interop) {
   Kokkos::finalize();
 
   offset<<<100, 64>>>(p);
-  CUDA_SAFE_CALL(cudaDeviceSynchronize());
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaDeviceSynchronize());
 
   std::array<int, 100> h_p;
   cudaMemcpy(h_p.data(), p, sizeof(int) * 100, cudaMemcpyDefault);
-  CUDA_SAFE_CALL(cudaDeviceSynchronize());
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaDeviceSynchronize());
   int64_t sum        = 0;
   int64_t sum_expect = 0;
   for (int i = 0; i < 100; i++) {
@@ -83,6 +83,6 @@ TEST(cuda, raw_cuda_interop) {
   }
 
   ASSERT_EQ(sum, sum_expect);
-  CUDA_SAFE_CALL(cudaFree(p));
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaFree(p));
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/cuda/TestCuda_InterOp_Streams.cpp b/packages/kokkos/core/unit_test/cuda/TestCuda_InterOp_Streams.cpp
index 526b985c00f2eec2eab6cafb8e862eff5024d575..13388b4c5472c5441d33e9fbfb8f99a995bdcdf0 100644
--- a/packages/kokkos/core/unit_test/cuda/TestCuda_InterOp_Streams.cpp
+++ b/packages/kokkos/core/unit_test/cuda/TestCuda_InterOp_Streams.cpp
@@ -99,12 +99,12 @@ TEST(cuda, raw_cuda_streams) {
   }
   Kokkos::finalize();
   offset_streams<<<100, 64, 0, stream>>>(p);
-  CUDA_SAFE_CALL(cudaDeviceSynchronize());
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaDeviceSynchronize());
   cudaStreamDestroy(stream);
 
   int h_p[100];
   cudaMemcpy(h_p, p, sizeof(int) * 100, cudaMemcpyDefault);
-  CUDA_SAFE_CALL(cudaDeviceSynchronize());
+  KOKKOS_IMPL_CUDA_SAFE_CALL(cudaDeviceSynchronize());
   int64_t sum        = 0;
   int64_t sum_expect = 0;
   for (int i = 0; i < 100; i++) {
diff --git a/packages/kokkos/core/unit_test/cuda/TestCuda_Spaces.cpp b/packages/kokkos/core/unit_test/cuda/TestCuda_Spaces.cpp
index 646b37908654d2af6327158cb49f7d4257e8f8bf..2fa61d43120d338bac3c475fc7cf35e9aeb06776 100644
--- a/packages/kokkos/core/unit_test/cuda/TestCuda_Spaces.cpp
+++ b/packages/kokkos/core/unit_test/cuda/TestCuda_Spaces.cpp
@@ -181,37 +181,33 @@ TEST(cuda, space_access) {
   //--------------------------------------
 
   static_assert(
-      !Kokkos::Impl::SpaceAccessibility<Kokkos::Cuda,
-                                        Kokkos::HostSpace>::accessible,
+      !Kokkos::SpaceAccessibility<Kokkos::Cuda, Kokkos::HostSpace>::accessible,
       "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<Kokkos::Cuda,
-                                                 Kokkos::CudaSpace>::accessible,
-                "");
-
   static_assert(
-      Kokkos::Impl::SpaceAccessibility<Kokkos::Cuda,
-                                       Kokkos::CudaUVMSpace>::accessible,
+      Kokkos::SpaceAccessibility<Kokkos::Cuda, Kokkos::CudaSpace>::accessible,
       "");
 
-  static_assert(
-      Kokkos::Impl::SpaceAccessibility<Kokkos::Cuda,
-                                       Kokkos::CudaHostPinnedSpace>::accessible,
-      "");
+  static_assert(Kokkos::SpaceAccessibility<Kokkos::Cuda,
+                                           Kokkos::CudaUVMSpace>::accessible,
+                "");
 
   static_assert(
-      !Kokkos::Impl::SpaceAccessibility<Kokkos::HostSpace,
-                                        Kokkos::CudaSpace>::accessible,
+      Kokkos::SpaceAccessibility<Kokkos::Cuda,
+                                 Kokkos::CudaHostPinnedSpace>::accessible,
       "");
 
-  static_assert(
-      Kokkos::Impl::SpaceAccessibility<Kokkos::HostSpace,
-                                       Kokkos::CudaUVMSpace>::accessible,
-      "");
+  static_assert(!Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                            Kokkos::CudaSpace>::accessible,
+                "");
+
+  static_assert(Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                           Kokkos::CudaUVMSpace>::accessible,
+                "");
 
   static_assert(
-      Kokkos::Impl::SpaceAccessibility<Kokkos::HostSpace,
-                                       Kokkos::CudaHostPinnedSpace>::accessible,
+      Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                 Kokkos::CudaHostPinnedSpace>::accessible,
       "");
 
   static_assert(std::is_same<Kokkos::Impl::HostMirror<Kokkos::CudaSpace>::Space,
@@ -235,23 +231,23 @@ TEST(cuda, space_access) {
                                             Kokkos::CudaUVMSpace>>::value,
                 "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<
-                    Kokkos::Impl::HostMirror<Kokkos::Cuda>::Space,
-                    Kokkos::HostSpace>::accessible,
-                "");
+  static_assert(
+      Kokkos::SpaceAccessibility<Kokkos::Impl::HostMirror<Kokkos::Cuda>::Space,
+                                 Kokkos::HostSpace>::accessible,
+      "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<
+  static_assert(Kokkos::SpaceAccessibility<
                     Kokkos::Impl::HostMirror<Kokkos::CudaSpace>::Space,
                     Kokkos::HostSpace>::accessible,
                 "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<
+  static_assert(Kokkos::SpaceAccessibility<
                     Kokkos::Impl::HostMirror<Kokkos::CudaUVMSpace>::Space,
                     Kokkos::HostSpace>::accessible,
                 "");
 
   static_assert(
-      Kokkos::Impl::SpaceAccessibility<
+      Kokkos::SpaceAccessibility<
           Kokkos::Impl::HostMirror<Kokkos::CudaHostPinnedSpace>::Space,
           Kokkos::HostSpace>::accessible,
       "");
@@ -265,8 +261,8 @@ TEST(cuda, space_access) {
 
 TEST(cuda, uvm) {
   if (Kokkos::CudaUVMSpace::available()) {
-    int *uvm_ptr = (int *)Kokkos::kokkos_malloc<Kokkos::CudaUVMSpace>(
-        "uvm_ptr", sizeof(int));
+    int *uvm_ptr = static_cast<int *>(
+        Kokkos::kokkos_malloc<Kokkos::CudaUVMSpace>("uvm_ptr", sizeof(int)));
 
     *uvm_ptr = 42;
 
diff --git a/packages/kokkos/core/unit_test/default/TestDefaultDeviceType.cpp b/packages/kokkos/core/unit_test/default/TestDefaultDeviceType.cpp
index 5dcbe566e299c0f013843216b0854dc51582dd6d..6d6ff0a67bc151421556fca487f30677a5119c33 100644
--- a/packages/kokkos/core/unit_test/default/TestDefaultDeviceType.cpp
+++ b/packages/kokkos/core/unit_test/default/TestDefaultDeviceType.cpp
@@ -59,17 +59,17 @@ TEST(TEST_CATEGORY, host_space_access) {
   using mirror_space =
       Kokkos::Impl::HostMirror<Kokkos::DefaultExecutionSpace>::Space;
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<host_exec_space,
-                                                 Kokkos::HostSpace>::accessible,
+  static_assert(Kokkos::SpaceAccessibility<host_exec_space,
+                                           Kokkos::HostSpace>::accessible,
                 "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<device_space,
-                                                 Kokkos::HostSpace>::accessible,
-                "");
+  static_assert(
+      Kokkos::SpaceAccessibility<device_space, Kokkos::HostSpace>::accessible,
+      "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<mirror_space,
-                                                 Kokkos::HostSpace>::accessible,
-                "");
+  static_assert(
+      Kokkos::SpaceAccessibility<mirror_space, Kokkos::HostSpace>::accessible,
+      "");
 }
 
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/default/TestDefaultDeviceType_d.cpp b/packages/kokkos/core/unit_test/default/TestDefaultDeviceType_d.cpp
index bcd49e69bd3af022ede0ca0a188066288c9b1d35..c74090fff93c8b9a529fad2e5d156d4cad55b954 100644
--- a/packages/kokkos/core/unit_test/default/TestDefaultDeviceType_d.cpp
+++ b/packages/kokkos/core/unit_test/default/TestDefaultDeviceType_d.cpp
@@ -54,12 +54,13 @@
 namespace Test {
 
 TEST(defaultdevicetype, malloc) {
-  int* data = (int*)Kokkos::kokkos_malloc(100 * sizeof(int));
-  ASSERT_NO_THROW(data = (int*)Kokkos::kokkos_realloc(data, 120 * sizeof(int)));
+  int* data = static_cast<int*>(Kokkos::kokkos_malloc(100 * sizeof(int)));
+  ASSERT_NO_THROW(data = static_cast<int*>(
+                      Kokkos::kokkos_realloc(data, 120 * sizeof(int))));
   Kokkos::kokkos_free(data);
 
-  int* data2 = (int*)Kokkos::kokkos_malloc(0);
-  ASSERT_TRUE(data2 == nullptr);
+  int* data2 = static_cast<int*>(Kokkos::kokkos_malloc(0));
+  ASSERT_EQ(data2, nullptr);
   Kokkos::kokkos_free(data2);
 }
 
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_a.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_a.cpp
deleted file mode 100644
index 02157836b3f6075c6c18e2919d93ed4b541dbab8..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_a.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewAPI_a.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_b.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_b.cpp
deleted file mode 100644
index 80e2fe3f93716c23979ede23aa81de9b2f694c9e..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_b.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewAPI_b.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_c.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_c.cpp
deleted file mode 100644
index 9694e33ca0ce0f5c2fc6214613f4ae2f03c9750d..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_c.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewAPI_c.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_d.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_d.cpp
deleted file mode 100644
index 0d773494ac6236ce0274cc844fb3369aec81d51d..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_d.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewAPI_d.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_e.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_e.cpp
deleted file mode 100644
index cbbbc810b0e8e588be2892b83279a4137675de66..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewAPI_e.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewAPI_e.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewCopy_a.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewCopy_a.cpp
deleted file mode 100644
index 444a3e6e95d2a62c1ad0e8bedba3767503dd4687..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewCopy_a.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewCopy_a.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewCopy_b.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewCopy_b.cpp
deleted file mode 100644
index f1f90e7acf13c7aaa4820f5bd50ecc403f2d6f5f..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewCopy_b.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewCopy_b.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewMapping_a.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewMapping_a.cpp
deleted file mode 100644
index 5e83121e341db1da440c65cd5dce84dc1a6f6259..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewMapping_a.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewMapping_a.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewMapping_b.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewMapping_b.cpp
deleted file mode 100644
index c024143d6c7b735dfa3b897e0a4503ee50e4caec..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewMapping_b.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewMapping_b.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewMapping_subview.cpp b/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewMapping_subview.cpp
deleted file mode 100644
index dcd6c1dc435982fdf44950c3b606847c29c30b37..0000000000000000000000000000000000000000
--- a/packages/kokkos/core/unit_test/hip/TestHIPHostPinned_ViewMapping_subview.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
-//@HEADER
-// ************************************************************************
-//
-//                        Kokkos v. 3.0
-//       Copyright (2020) National Technology & Engineering
-//               Solutions of Sandia, LLC (NTESS).
-//
-// Under the terms of Contract DE-NA0003525 with NTESS,
-// the U.S. Government retains certain rights in this software.
-//
-// 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 Corporation nor the names of the
-// contributors may be used to endorse or promote products derived from
-// this software without specific prior written permission.
-//
-// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
-// 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.
-//
-// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
-//
-// ************************************************************************
-//@HEADER
-*/
-
-#include <TestHIPHostPinned_Category.hpp>
-#include <TestViewMapping_subview.hpp>
diff --git a/packages/kokkos/core/unit_test/hip/TestHIP_AsyncLauncher.cpp b/packages/kokkos/core/unit_test/hip/TestHIP_AsyncLauncher.cpp
index 0a243e0e8e89c0ef5a7cec6195837909d092bc2a..854f916ba3dad7777f453694ea708a0754872d3d 100644
--- a/packages/kokkos/core/unit_test/hip/TestHIP_AsyncLauncher.cpp
+++ b/packages/kokkos/core/unit_test/hip/TestHIP_AsyncLauncher.cpp
@@ -66,8 +66,8 @@ struct TestAsyncLauncher {
 
 TEST(hip, async_launcher) {
   size_t *flag;
-  HIP_SAFE_CALL(hipMalloc(&flag, sizeof(size_t)));
-  HIP_SAFE_CALL(hipMemset(flag, 0, sizeof(size_t)));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipMalloc(&flag, sizeof(size_t)));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipMemset(flag, 0, sizeof(size_t)));
   // launch # of cycles * 1000 kernels w/ distinct values
   auto space        = Kokkos::Experimental::HIP();
   auto instance     = space.impl_internal_space_instance();
@@ -80,10 +80,10 @@ TEST(hip, async_launcher) {
   // the sum below should fail
   instance->fence();
   size_t h_flag;
-  HIP_SAFE_CALL(
+  KOKKOS_IMPL_HIP_SAFE_CALL(
       hipMemcpy(&h_flag, flag, sizeof(size_t), hipMemcpyHostToDevice));
   ASSERT_EQ(h_flag, (nkernels * (nkernels - 1)) / 2);
-  HIP_SAFE_CALL(hipFree(flag));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipFree(flag));
 }
 
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/hip/TestHIP_BlocksizeDeduction.cpp b/packages/kokkos/core/unit_test/hip/TestHIP_BlocksizeDeduction.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f382e5b568b85cdac1f4943c72aa02be73472d7a
--- /dev/null
+++ b/packages/kokkos/core/unit_test/hip/TestHIP_BlocksizeDeduction.cpp
@@ -0,0 +1,99 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#include <Kokkos_Core.hpp>
+#include <TestHIP_Category.hpp>
+
+namespace Test {
+
+struct TestNone {
+  Kokkos::View<size_t*, TEST_EXECSPACE> view;
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int i) const { view(i) = i; }
+
+  TestNone() { view = Kokkos::View<size_t*, TEST_EXECSPACE>("dummy", 1); }
+};
+
+struct TestSpiller {
+  Kokkos::View<size_t*, TEST_EXECSPACE> view;
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const int i) const {
+    size_t array[1000] = {0};
+    // and update flag
+    size_t value = 0;
+    for (int ii = i; ii < 1000; ++ii) {
+      array[ii] = value;
+      value += ii;
+    }
+    for (int ii = i; ii < 1000; ++ii) {
+      value *= array[ii];
+    }
+    Kokkos::atomic_add(&view[0], value);
+  }
+
+  TestSpiller() { view = Kokkos::View<size_t*, TEST_EXECSPACE>("dummy", 1); }
+};
+
+TEST(hip, preferred_blocksize_deduction) {
+  using execution_space =
+      typename Kokkos::Impl::FunctorPolicyExecutionSpace<TestSpiller,
+                                                         void>::execution_space;
+  using policy = Kokkos::RangePolicy<execution_space>;
+
+  {
+    using DriverType = Kokkos::Impl::ParallelFor<TestNone, policy>;
+    ASSERT_TRUE(Kokkos::Experimental::Impl::HIPParallelLaunch<
+                    DriverType>::get_scratch_size() == 0);
+  }
+
+  {
+    using DriverType = Kokkos::Impl::ParallelFor<TestSpiller, policy>;
+    ASSERT_TRUE(Kokkos::Experimental::Impl::HIPParallelLaunch<
+                    DriverType>::get_scratch_size() > 0);
+  }
+}
+
+}  // namespace Test
diff --git a/packages/kokkos/core/unit_test/hip/TestHIP_InterOp_Init.cpp b/packages/kokkos/core/unit_test/hip/TestHIP_InterOp_Init.cpp
index 3a76ca148cf683a83b84d351e4ebd8b2f7cdec94..73d08abca9d396464e8ba538e6e228c4ad70628b 100644
--- a/packages/kokkos/core/unit_test/hip/TestHIP_InterOp_Init.cpp
+++ b/packages/kokkos/core/unit_test/hip/TestHIP_InterOp_Init.cpp
@@ -60,7 +60,7 @@ __global__ void offset(int* p) {
 // HIP.
 TEST(hip, raw_hip_interop) {
   int* p;
-  HIP_SAFE_CALL(hipMalloc(&p, sizeof(int) * 100));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipMalloc(&p, sizeof(int) * 100));
   Kokkos::InitArguments arguments{-1, -1, -1, false};
   Kokkos::initialize(arguments);
 
@@ -70,11 +70,12 @@ TEST(hip, raw_hip_interop) {
   Kokkos::finalize();
 
   offset<<<dim3(100), dim3(100), 0, nullptr>>>(p);
-  HIP_SAFE_CALL(hipDeviceSynchronize());
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipDeviceSynchronize());
 
   std::array<int, 100> h_p;
-  HIP_SAFE_CALL(hipMemcpy(h_p.data(), p, sizeof(int) * 100, hipMemcpyDefault));
-  HIP_SAFE_CALL(hipDeviceSynchronize());
+  KOKKOS_IMPL_HIP_SAFE_CALL(
+      hipMemcpy(h_p.data(), p, sizeof(int) * 100, hipMemcpyDefault));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipDeviceSynchronize());
   int64_t sum        = 0;
   int64_t sum_expect = 0;
   for (int i = 0; i < 100; i++) {
@@ -83,6 +84,6 @@ TEST(hip, raw_hip_interop) {
   }
 
   ASSERT_EQ(sum, sum_expect);
-  HIP_SAFE_CALL(hipFree(p));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipFree(p));
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/hip/TestHIP_InterOp_Streams.cpp b/packages/kokkos/core/unit_test/hip/TestHIP_InterOp_Streams.cpp
index 8e0880ddbd0b15524be75ab97b90044e5315a8ff..69ca62df6a3a3e95cc77fb4354b96eb6a16e0c2d 100644
--- a/packages/kokkos/core/unit_test/hip/TestHIP_InterOp_Streams.cpp
+++ b/packages/kokkos/core/unit_test/hip/TestHIP_InterOp_Streams.cpp
@@ -51,11 +51,11 @@ namespace Test {
 // bound in HIP due to an error when computing the block size.
 TEST(hip, raw_hip_streams) {
   hipStream_t stream;
-  HIP_SAFE_CALL(hipStreamCreate(&stream));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipStreamCreate(&stream));
   Kokkos::InitArguments arguments{-1, -1, -1, false};
   Kokkos::initialize(arguments);
   int* p;
-  HIP_SAFE_CALL(hipMalloc(&p, sizeof(int) * 100));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipMalloc(&p, sizeof(int) * 100));
   using MemorySpace = typename TEST_EXECSPACE::memory_space;
 
   {
@@ -97,12 +97,13 @@ TEST(hip, raw_hip_streams) {
   }
   Kokkos::finalize();
   offset_streams<<<100, 64, 0, stream>>>(p);
-  HIP_SAFE_CALL(hipDeviceSynchronize());
-  HIP_SAFE_CALL(hipStreamDestroy(stream));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipDeviceSynchronize());
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipStreamDestroy(stream));
 
   int h_p[100];
-  HIP_SAFE_CALL(hipMemcpy(h_p, p, sizeof(int) * 100, hipMemcpyDefault));
-  HIP_SAFE_CALL(hipDeviceSynchronize());
+  KOKKOS_IMPL_HIP_SAFE_CALL(
+      hipMemcpy(h_p, p, sizeof(int) * 100, hipMemcpyDefault));
+  KOKKOS_IMPL_HIP_SAFE_CALL(hipDeviceSynchronize());
   int64_t sum        = 0;
   int64_t sum_expect = 0;
   for (int i = 0; i < 100; i++) {
diff --git a/packages/kokkos/core/unit_test/hip/TestHIP_Spaces.cpp b/packages/kokkos/core/unit_test/hip/TestHIP_Spaces.cpp
index ae1de8ea2d304e41d672ff2e136d16c86cbb8068..d20ea877ec9e1f4aee9f0df5c1d807790cdc932e 100644
--- a/packages/kokkos/core/unit_test/hip/TestHIP_Spaces.cpp
+++ b/packages/kokkos/core/unit_test/hip/TestHIP_Spaces.cpp
@@ -129,27 +129,26 @@ TEST(hip, space_access) {
 
   //--------------------------------------
 
+  static_assert(!Kokkos::SpaceAccessibility<Kokkos::Experimental::HIP,
+                                            Kokkos::HostSpace>::accessible,
+                "");
+
   static_assert(
-      !Kokkos::Impl::SpaceAccessibility<Kokkos::Experimental::HIP,
-                                        Kokkos::HostSpace>::accessible,
+      Kokkos::SpaceAccessibility<Kokkos::Experimental::HIP,
+                                 Kokkos::Experimental::HIPSpace>::accessible,
       "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<
-                    Kokkos::Experimental::HIP,
-                    Kokkos::Experimental::HIPSpace>::accessible,
-                "");
-
-  static_assert(Kokkos::Impl::SpaceAccessibility<
+  static_assert(Kokkos::SpaceAccessibility<
                     Kokkos::Experimental::HIP,
                     Kokkos::Experimental::HIPHostPinnedSpace>::accessible,
                 "");
 
   static_assert(
-      !Kokkos::Impl::SpaceAccessibility<
-          Kokkos::HostSpace, Kokkos::Experimental::HIPSpace>::accessible,
+      !Kokkos::SpaceAccessibility<Kokkos::HostSpace,
+                                  Kokkos::Experimental::HIPSpace>::accessible,
       "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<
+  static_assert(Kokkos::SpaceAccessibility<
                     Kokkos::HostSpace,
                     Kokkos::Experimental::HIPHostPinnedSpace>::accessible,
                 "");
@@ -166,18 +165,18 @@ TEST(hip, space_access) {
                    Kokkos::Experimental::HIPHostPinnedSpace>::value,
       "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<
+  static_assert(Kokkos::SpaceAccessibility<
                     Kokkos::Impl::HostMirror<Kokkos::Experimental::HIP>::Space,
                     Kokkos::HostSpace>::accessible,
                 "");
 
   static_assert(
-      Kokkos::Impl::SpaceAccessibility<
+      Kokkos::SpaceAccessibility<
           Kokkos::Impl::HostMirror<Kokkos::Experimental::HIPSpace>::Space,
           Kokkos::HostSpace>::accessible,
       "");
 
-  static_assert(Kokkos::Impl::SpaceAccessibility<
+  static_assert(Kokkos::SpaceAccessibility<
                     Kokkos::Impl::HostMirror<
                         Kokkos::Experimental::HIPHostPinnedSpace>::Space,
                     Kokkos::HostSpace>::accessible,
diff --git a/packages/kokkos/core/unit_test/hip/TestHIP_TeamScratchStreams.cpp b/packages/kokkos/core/unit_test/hip/TestHIP_TeamScratchStreams.cpp
index db360a99d3d60977cf06479e7662e21350dd5f99..86b2fab3c7e3fc0b53b309646add9f4817b804f2 100644
--- a/packages/kokkos/core/unit_test/hip/TestHIP_TeamScratchStreams.cpp
+++ b/packages/kokkos/core/unit_test/hip/TestHIP_TeamScratchStreams.cpp
@@ -104,7 +104,7 @@ void hip_stream_scratch_test(
   hipStream_t stream[4];
   Kokkos::Experimental::HIP hip[4];
   for (int i = 0; i < K; i++) {
-    HIP_SAFE_CALL(hipStreamCreate(&stream[i]));
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipStreamCreate(&stream[i]));
     hip[i] = Kokkos::Experimental::HIP(stream[i]);
   }
 // Test that growing scratch size in subsequent calls doesn't crash things
@@ -131,7 +131,7 @@ void hip_stream_scratch_test(
   Kokkos::fence();
   for (int i = 0; i < K; i++) {
     hip[i] = Kokkos::Experimental::HIP();
-    HIP_SAFE_CALL(hipStreamDestroy(stream[i]));
+    KOKKOS_IMPL_HIP_SAFE_CALL(hipStreamDestroy(stream[i]));
   }
 }
 }  // namespace Impl
diff --git a/packages/kokkos/core/unit_test/incremental/Test01_execspace.hpp b/packages/kokkos/core/unit_test/incremental/Test01_execspace.hpp
index 419486d7a84673dacd48e7bf2513e054106bab4c..4d5ca46ba6ee6a41c8a9461bc5c26b36e5996a55 100644
--- a/packages/kokkos/core/unit_test/incremental/Test01_execspace.hpp
+++ b/packages/kokkos/core/unit_test/incremental/Test01_execspace.hpp
@@ -88,7 +88,7 @@ struct TestIncrExecSpace {
     ExecSpace().fence();
 
     auto concurrency = ExecSpace().concurrency();
-    ASSERT_TRUE(concurrency > 0);
+    ASSERT_GT(concurrency, 0);
 
     int in_parallel = ExecSpace::in_parallel();
     ASSERT_FALSE(in_parallel);
@@ -107,5 +107,7 @@ TEST(TEST_CATEGORY, IncrTest_01_execspace) {
   ASSERT_TRUE(Kokkos::is_execution_space<TEST_EXECSPACE>::value);
   ASSERT_FALSE(Kokkos::is_execution_space<
                TestIncrExecSpaceTypedef<TEST_EXECSPACE>>::value);
+  TestIncrExecSpace<TEST_EXECSPACE> test;
+  test.testit();
 }
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/incremental/Test02_atomic_host.hpp b/packages/kokkos/core/unit_test/incremental/Test02_atomic_host.hpp
index ff4fb6a89f4d380d0693e8697e27fbf5bde2f4d0..d40cb4dbe7f77627429f9880ce4981b38f414c9d 100644
--- a/packages/kokkos/core/unit_test/incremental/Test02_atomic_host.hpp
+++ b/packages/kokkos/core/unit_test/incremental/Test02_atomic_host.hpp
@@ -78,7 +78,7 @@ struct TestIncrAtomic {
   }
 };
 
-TEST(TEST_CATEGORY, IncrTest_01_AtomicExchange) {
+TEST(TEST_CATEGORY, IncrTest_02_AtomicExchange) {
   TestIncrAtomic test;
   test.testExchange();
 }
diff --git a/packages/kokkos/core/unit_test/incremental/Test06_ParallelFor_MDRangePolicy.hpp b/packages/kokkos/core/unit_test/incremental/Test06_ParallelFor_MDRangePolicy.hpp
index 4adf9e058fd5b1a85b3f7e24cac530876b7251f3..4192d4abe865f10a43e9a87ed6ee4aa877974dc0 100644
--- a/packages/kokkos/core/unit_test/incremental/Test06_ParallelFor_MDRangePolicy.hpp
+++ b/packages/kokkos/core/unit_test/incremental/Test06_ParallelFor_MDRangePolicy.hpp
@@ -94,16 +94,16 @@ struct TestMDRangePolicy {
   using int_index = Kokkos::IndexType<int>;
 
   // An MDRangePolicy for 2 nested loops
-  using MDPolicyType_2D = typename Kokkos::Experimental::MDRangePolicy<
-      ExecSpace, Kokkos::Experimental::Rank<2>, int_index>;
+  using MDPolicyType_2D =
+      typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<2>, int_index>;
 
   // An MDRangePolicy for 3 nested loops
-  using MDPolicyType_3D = typename Kokkos::Experimental::MDRangePolicy<
-      ExecSpace, Kokkos::Experimental::Rank<3>, int_index>;
+  using MDPolicyType_3D =
+      typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<3>, int_index>;
 
   // An MDRangePolicy for 4 nested loops
-  using MDPolicyType_4D = typename Kokkos::Experimental::MDRangePolicy<
-      ExecSpace, Kokkos::Experimental::Rank<4>, int_index>;
+  using MDPolicyType_4D =
+      typename Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<4>, int_index>;
 
   // Device and Host Data structure pointer
   value_type *deviceData, *hostData;
diff --git a/packages/kokkos/core/unit_test/incremental/Test08_deep_copy.hpp b/packages/kokkos/core/unit_test/incremental/Test08_deep_copy.hpp
index 5166f5a9f0de05b24166161654c9eaab4ff2ad82..6e8fc07b8de50e61e1139ad0b7ba6e2752e81229 100644
--- a/packages/kokkos/core/unit_test/incremental/Test08_deep_copy.hpp
+++ b/packages/kokkos/core/unit_test/incremental/Test08_deep_copy.hpp
@@ -61,17 +61,17 @@ const int M      = 10;
 template <class ExecSpace>
 struct TestMDRangePolicy {
   // 2D View
-  using View_2D      = typename Kokkos::View<value_type **, ExecSpace>;
+  using View_2D      = Kokkos::View<value_type **, ExecSpace>;
   using Host_View_2D = typename View_2D::HostMirror;
   Host_View_2D hostDataView_2D;
 
   // 3D View
-  using View_3D      = typename Kokkos::View<value_type ***, ExecSpace>;
+  using View_3D      = Kokkos::View<value_type ***, ExecSpace>;
   using Host_View_3D = typename View_3D::HostMirror;
   Host_View_3D hostDataView_3D;
 
   // 4D View
-  using View_4D      = typename Kokkos::View<value_type ****, ExecSpace>;
+  using View_4D      = Kokkos::View<value_type ****, ExecSpace>;
   using Host_View_4D = typename View_4D::HostMirror;
   Host_View_4D hostDataView_4D;
 
@@ -83,16 +83,16 @@ struct TestMDRangePolicy {
   using int_index = Kokkos::IndexType<int>;
 
   // An MDRangePolicy for 2 nested loops
-  using MDPolicyType_2D = typename Kokkos::Experimental::MDRangePolicy<
-      ExecSpace, Kokkos::Experimental::Rank<2>, int_index>;
+  using MDPolicyType_2D =
+      Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<2>, int_index>;
 
   // An MDRangePolicy for 3 nested loops
-  using MDPolicyType_3D = typename Kokkos::Experimental::MDRangePolicy<
-      ExecSpace, Kokkos::Experimental::Rank<3>, int_index>;
+  using MDPolicyType_3D =
+      Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<3>, int_index>;
 
   // An MDRangePolicy for 4 nested loops
-  using MDPolicyType_4D = typename Kokkos::Experimental::MDRangePolicy<
-      ExecSpace, Kokkos::Experimental::Rank<4>, int_index>;
+  using MDPolicyType_4D =
+      Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<4>, int_index>;
 
   // compare and equal
   void compare_equal_2D() {
diff --git a/packages/kokkos/core/unit_test/incremental/Test12a_ThreadScratch.hpp b/packages/kokkos/core/unit_test/incremental/Test12a_ThreadScratch.hpp
index 5bf1860d8e4a6bcf739656bdc7e1f790ebf60512..ab1cd90d4bfa2e5f5800b8dd13f23a999d2a8fa0 100644
--- a/packages/kokkos/core/unit_test/incremental/Test12a_ThreadScratch.hpp
+++ b/packages/kokkos/core/unit_test/incremental/Test12a_ThreadScratch.hpp
@@ -74,9 +74,15 @@ struct ThreadScratch {
     for (int i = 0; i < sY; ++i) v_S(i) = 0;
 
     Kokkos::parallel_for(Kokkos::TeamThreadRange(team, sX), [&](const int m) {
+    // FIXME_SYCL This deadlocks in the subgroup_barrier when running on CUDA
+    // devices.
+#ifdef KOKKOS_ENABLE_SYCL
+      for (int k = 0; k < sY; ++k) v_S(k) += sX * sY * n + sY * m + k;
+#else
       Kokkos::parallel_for(
           Kokkos::ThreadVectorRange(team, sY),
           [&](const int k) { v_S(k) += sX * sY * n + sY * m + k; });
+#endif
     });
 
     team.team_barrier();
@@ -93,7 +99,7 @@ struct ThreadScratch {
     int scratchSize = scratch_t::shmem_size(sY);
     // So this works with deprecated code enabled:
     policy_t policy =
-        policy_t(pN, Kokkos::AUTO)
+        policy_t(pN, Kokkos::AUTO, 1)
             .set_scratch_size(scratch_level, Kokkos::PerThread(scratchSize));
 
     int max_team_size = policy.team_size_max(*this, Kokkos::ParallelForTag());
diff --git a/packages/kokkos/core/unit_test/incremental/Test12b_TeamScratch.hpp b/packages/kokkos/core/unit_test/incremental/Test12b_TeamScratch.hpp
index b34f652e76d919f14c3afed0656b8bcd86dbc27f..d81822d0da9cfd67b25c8394886509407ecbfeb0 100644
--- a/packages/kokkos/core/unit_test/incremental/Test12b_TeamScratch.hpp
+++ b/packages/kokkos/core/unit_test/incremental/Test12b_TeamScratch.hpp
@@ -68,7 +68,7 @@ struct TeamScratch {
 
     Kokkos::parallel_for(
         "Team",
-        policy_t(pN, Kokkos::AUTO)
+        policy_t(pN, Kokkos::AUTO, 1)
             .set_scratch_size(scratch_level, Kokkos::PerTeam(scratchSize)),
         KOKKOS_LAMBDA(const team_t &team) {
           // Allocate and use scratch pad memory
@@ -77,11 +77,20 @@ struct TeamScratch {
 
           Kokkos::parallel_for(
               Kokkos::TeamThreadRange(team, sX), [&](const int m) {
+      // FIXME_SYCL This deadlocks in the subgroup_barrier
+      // when running on CUDA devices.
+#ifdef KOKKOS_ENABLE_SYCL
+                for (int k = 0; k < sY; ++k) {
+                  v_S(m, k) =
+                      v_S.extent(0) * v_S.extent(1) * n + v_S.extent(1) * m + k;
+                }
+#else
                 Kokkos::parallel_for(
                     Kokkos::ThreadVectorRange(team, sY), [&](const int k) {
                       v_S(m, k) = v_S.extent(0) * v_S.extent(1) * n +
                                   v_S.extent(1) * m + k;
                     });
+#endif
               });
 
           team.team_barrier();
diff --git a/packages/kokkos/core/unit_test/incremental/Test14_MDRangeReduce.hpp b/packages/kokkos/core/unit_test/incremental/Test14_MDRangeReduce.hpp
index d227e834dc64607c4ca01127228527dc71e9e918..7d53b9fb208ea490626634c304dd130b74392461 100644
--- a/packages/kokkos/core/unit_test/incremental/Test14_MDRangeReduce.hpp
+++ b/packages/kokkos/core/unit_test/incremental/Test14_MDRangeReduce.hpp
@@ -82,20 +82,20 @@ struct MyComplex {
 template <class ExecSpace>
 struct TestMDRangeReduce {
   // 1D  View of double
-  using View_1D = typename Kokkos::View<value_type*, ExecSpace>;
+  using View_1D = Kokkos::View<value_type*, ExecSpace>;
 
   // 2D  View of double
-  using View_2D = typename Kokkos::View<value_type**, ExecSpace>;
+  using View_2D = Kokkos::View<value_type**, ExecSpace>;
 
   // Index Type for the iterator
   using int_index = Kokkos::IndexType<int>;
 
   // An MDRangePolicy for 2 nested loops
-  using MDPolicyType_2D = typename Kokkos::Experimental::MDRangePolicy<
-      ExecSpace, Kokkos::Experimental::Rank<2>, int_index>;
+  using MDPolicyType_2D =
+      Kokkos::MDRangePolicy<ExecSpace, Kokkos::Rank<2>, int_index>;
 
   //  1D - complex View
-  using Complex_View_1D = typename Kokkos::View<MyComplex*, ExecSpace>;
+  using Complex_View_1D = Kokkos::View<MyComplex*, ExecSpace>;
 
   // Reduction when ExecPolicy = MDRangePolicy and ReducerArgument =
   // scalar/1-element view
@@ -176,7 +176,11 @@ struct TestMDRangeReduce {
 TEST(TEST_CATEGORY, incr_14_MDrangeReduce) {
   TestMDRangeReduce<TEST_EXECSPACE> test;
   test.reduce_MDRange();
+// FIXME_OPENMPTARGET: custom reductions are not yet supported in the
+// OpenMPTarget backend.
+#if !defined(KOKKOS_ENABLE_OPENMPTARGET)
   test.reduce_custom();
+#endif
 }
 
 }  // namespace Test
diff --git a/packages/kokkos/core/unit_test/sycl/TestSYCL_InterOp_Init.cpp b/packages/kokkos/core/unit_test/sycl/TestSYCL_InterOp_Init.cpp
index 018855963d35f8fef81a93985811dcc3d9b239fc..d145d69d9e0feb4450bfff5080e9955115b5c49e 100644
--- a/packages/kokkos/core/unit_test/sycl/TestSYCL_InterOp_Init.cpp
+++ b/packages/kokkos/core/unit_test/sycl/TestSYCL_InterOp_Init.cpp
@@ -52,13 +52,16 @@ namespace Test {
 // Test whether allocations survive Kokkos initialize/finalize if done via Raw
 // SYCL.
 TEST(sycl, raw_sycl_interop) {
+  Kokkos::InitArguments arguments{-1, -1, -1, false};
+  Kokkos::initialize(arguments);
+
+  Kokkos::Experimental::SYCL default_space;
+  sycl::context default_context = default_space.sycl_context();
+
   sycl::default_selector device_selector;
-  sycl::queue queue(device_selector);
+  sycl::queue queue(default_context, device_selector);
   constexpr int n = 100;
   int* p          = sycl::malloc_device<int>(n, queue);
-
-  Kokkos::InitArguments arguments{-1, -1, -1, false};
-  Kokkos::initialize(arguments);
   {
     TEST_EXECSPACE space(queue);
     Kokkos::View<int*, Kokkos::MemoryTraits<Kokkos::Unmanaged>> v(p, n);
diff --git a/packages/kokkos/core/unit_test/sycl/TestSYCL_Spaces.cpp b/packages/kokkos/core/unit_test/sycl/TestSYCL_Spaces.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..91fdaac6e097fb9b127816301ca2e5c514a4f374
--- /dev/null
+++ b/packages/kokkos/core/unit_test/sycl/TestSYCL_Spaces.cpp
@@ -0,0 +1,356 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#include <Kokkos_Core.hpp>
+#include <TestSYCL_Category.hpp>
+
+namespace Test {
+
+TEST(sycl, space_access) {
+  static_assert(Kokkos::Impl::MemorySpaceAccess<Kokkos::HostSpace,
+                                                Kokkos::HostSpace>::assignable,
+                "");
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::HostSpace,
+                    Kokkos::Experimental::SYCLHostUSMSpace>::assignable,
+                "");
+
+  static_assert(!Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::HostSpace,
+                    Kokkos::Experimental::SYCLDeviceUSMSpace>::assignable,
+                "");
+
+  static_assert(!Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::HostSpace,
+                    Kokkos::Experimental::SYCLDeviceUSMSpace>::accessible,
+                "");
+
+  static_assert(!Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::HostSpace,
+                    Kokkos::Experimental::SYCLSharedUSMSpace>::assignable,
+                "");
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::HostSpace,
+                    Kokkos::Experimental::SYCLSharedUSMSpace>::accessible,
+                "");
+
+  //--------------------------------------
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLDeviceUSMSpace,
+                    Kokkos::Experimental::SYCLDeviceUSMSpace>::assignable,
+                "");
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLDeviceUSMSpace,
+                    Kokkos::Experimental::SYCLSharedUSMSpace>::assignable,
+                "");
+
+  static_assert(!Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLDeviceUSMSpace,
+                    Kokkos::Experimental::SYCLHostUSMSpace>::assignable,
+                "");
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLDeviceUSMSpace,
+                    Kokkos::Experimental::SYCLHostUSMSpace>::accessible,
+                "");
+
+  static_assert(
+      !Kokkos::Impl::MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                                       Kokkos::HostSpace>::assignable,
+      "");
+
+  static_assert(
+      !Kokkos::Impl::MemorySpaceAccess<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                                       Kokkos::HostSpace>::accessible,
+      "");
+
+  //--------------------------------------
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLSharedUSMSpace,
+                    Kokkos::Experimental::SYCLSharedUSMSpace>::assignable,
+                "");
+
+  static_assert(!Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLSharedUSMSpace,
+                    Kokkos::Experimental::SYCLDeviceUSMSpace>::assignable,
+                "");
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLSharedUSMSpace,
+                    Kokkos::Experimental::SYCLDeviceUSMSpace>::accessible,
+                "");
+
+  static_assert(
+      !Kokkos::Impl::MemorySpaceAccess<Kokkos::Experimental::SYCLSharedUSMSpace,
+                                       Kokkos::HostSpace>::assignable,
+      "");
+
+  static_assert(
+      !Kokkos::Impl::MemorySpaceAccess<Kokkos::Experimental::SYCLSharedUSMSpace,
+                                       Kokkos::HostSpace>::accessible,
+      "");
+
+  static_assert(!Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLSharedUSMSpace,
+                    Kokkos::Experimental::SYCLHostUSMSpace>::assignable,
+                "");
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLSharedUSMSpace,
+                    Kokkos::Experimental::SYCLHostUSMSpace>::accessible,
+                "");
+
+  //--------------------------------------
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLHostUSMSpace,
+                    Kokkos::Experimental::SYCLHostUSMSpace>::assignable,
+                "");
+
+  static_assert(
+      !Kokkos::Impl::MemorySpaceAccess<Kokkos::Experimental::SYCLHostUSMSpace,
+                                       Kokkos::HostSpace>::assignable,
+      "");
+
+  static_assert(
+      Kokkos::Impl::MemorySpaceAccess<Kokkos::Experimental::SYCLHostUSMSpace,
+                                      Kokkos::HostSpace>::accessible,
+      "");
+
+  static_assert(!Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLHostUSMSpace,
+                    Kokkos::Experimental::SYCLDeviceUSMSpace>::assignable,
+                "");
+
+  static_assert(!Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLHostUSMSpace,
+                    Kokkos::Experimental::SYCLDeviceUSMSpace>::accessible,
+                "");
+
+  static_assert(!Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLHostUSMSpace,
+                    Kokkos::Experimental::SYCLSharedUSMSpace>::assignable,
+                "");
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::Experimental::SYCLHostUSMSpace,
+                    Kokkos::Experimental::SYCLSharedUSMSpace>::accessible,
+                "");
+
+  //--------------------------------------
+
+  static_assert(!Kokkos::SpaceAccessibility<Kokkos::Experimental::SYCL,
+                                            Kokkos::HostSpace>::accessible,
+                "");
+
+  static_assert(Kokkos::SpaceAccessibility<
+                    Kokkos::Experimental::SYCL,
+                    Kokkos::Experimental::SYCLDeviceUSMSpace>::accessible,
+                "");
+
+  static_assert(Kokkos::SpaceAccessibility<
+                    Kokkos::Experimental::SYCL,
+                    Kokkos::Experimental::SYCLSharedUSMSpace>::accessible,
+                "");
+
+  static_assert(Kokkos::SpaceAccessibility<
+                    Kokkos::Experimental::SYCL,
+                    Kokkos::Experimental::SYCLHostUSMSpace>::accessible,
+                "");
+
+  static_assert(!Kokkos::SpaceAccessibility<
+                    Kokkos::HostSpace,
+                    Kokkos::Experimental::SYCLDeviceUSMSpace>::accessible,
+                "");
+
+  static_assert(Kokkos::SpaceAccessibility<
+                    Kokkos::HostSpace,
+                    Kokkos::Experimental::SYCLSharedUSMSpace>::accessible,
+                "");
+
+  static_assert(Kokkos::SpaceAccessibility<
+                    Kokkos::HostSpace,
+                    Kokkos::Experimental::SYCLHostUSMSpace>::accessible,
+                "");
+
+  static_assert(
+      std::is_same<Kokkos::Impl::HostMirror<
+                       Kokkos::Experimental::SYCLDeviceUSMSpace>::Space,
+                   Kokkos::HostSpace>::value,
+      "");
+
+  static_assert(
+      std::is_same<
+          Kokkos::Impl::HostMirror<
+              Kokkos::Experimental::SYCLSharedUSMSpace>::Space,
+          Kokkos::Device<Kokkos::HostSpace::execution_space,
+                         Kokkos::Experimental::SYCLSharedUSMSpace>>::value,
+      "");
+
+  static_assert(
+      Kokkos::Impl::MemorySpaceAccess<Kokkos::Experimental::SYCLHostUSMSpace,
+                                      Kokkos::HostSpace>::accessible,
+      "");
+
+  static_assert(Kokkos::Impl::MemorySpaceAccess<
+                    Kokkos::HostSpace,
+                    Kokkos::Experimental::SYCLHostUSMSpace>::accessible,
+                "");
+
+  static_assert(std::is_same<Kokkos::Impl::HostMirror<
+                                 Kokkos::Experimental::SYCLHostUSMSpace>::Space,
+                             Kokkos::Experimental::SYCLHostUSMSpace>::value,
+                "");
+
+  static_assert(
+      std::is_same<
+          Kokkos::Device<Kokkos::HostSpace::execution_space,
+                         Kokkos::Experimental::SYCLSharedUSMSpace>,
+          Kokkos::Device<Kokkos::HostSpace::execution_space,
+                         Kokkos::Experimental::SYCLSharedUSMSpace>>::value,
+      "");
+
+  static_assert(Kokkos::SpaceAccessibility<
+                    Kokkos::Impl::HostMirror<Kokkos::Experimental::SYCL>::Space,
+                    Kokkos::HostSpace>::accessible,
+                "");
+
+  static_assert(Kokkos::SpaceAccessibility<
+                    Kokkos::Impl::HostMirror<
+                        Kokkos::Experimental::SYCLDeviceUSMSpace>::Space,
+                    Kokkos::HostSpace>::accessible,
+                "");
+
+  static_assert(Kokkos::SpaceAccessibility<
+                    Kokkos::Impl::HostMirror<
+                        Kokkos::Experimental::SYCLSharedUSMSpace>::Space,
+                    Kokkos::HostSpace>::accessible,
+                "");
+
+  static_assert(Kokkos::SpaceAccessibility<
+                    Kokkos::Impl::HostMirror<
+                        Kokkos::Experimental::SYCLHostUSMSpace>::Space,
+                    Kokkos::HostSpace>::accessible,
+                "");
+}
+
+TEST(sycl, uvm) {
+  int *uvm_ptr = static_cast<int *>(
+      Kokkos::kokkos_malloc<Kokkos::Experimental::SYCLSharedUSMSpace>(
+          "uvm_ptr", sizeof(int)));
+
+  *uvm_ptr = 42;
+
+  Kokkos::Experimental::SYCL().fence();
+  Kokkos::parallel_for(
+      Kokkos::RangePolicy<Kokkos::Experimental::SYCL>(0, 1),
+      KOKKOS_LAMBDA(int) {
+        if (*uvm_ptr == 42) {
+          *uvm_ptr = 2 * 42;
+        }
+      });
+  Kokkos::Experimental::SYCL().fence();
+
+  EXPECT_EQ(*uvm_ptr, int(2 * 42));
+
+  Kokkos::kokkos_free<Kokkos::Experimental::SYCLSharedUSMSpace>(uvm_ptr);
+}
+
+template <class MemSpace, class ExecSpace>
+struct TestViewSYCLAccessible {
+  enum { N = 1000 };
+
+  using V = Kokkos::View<double *, MemSpace>;
+
+  V m_base;
+
+  struct TagInit {};
+  struct TagTest {};
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const TagInit &, const int i) const { m_base[i] = i + 1; }
+
+  KOKKOS_INLINE_FUNCTION
+  void operator()(const TagTest &, const int i, long &error_count) const {
+    if (m_base[i] != i + 1) ++error_count;
+  }
+
+  TestViewSYCLAccessible() : m_base("base", N) {}
+
+  static void run() {
+    TestViewSYCLAccessible self;
+    Kokkos::parallel_for(
+        Kokkos::RangePolicy<typename MemSpace::execution_space, TagInit>(0, N),
+        self);
+    typename MemSpace::execution_space().fence();
+
+    // Next access is a different execution space, must complete prior kernel.
+    long error_count = -1;
+    Kokkos::parallel_reduce(Kokkos::RangePolicy<ExecSpace, TagTest>(0, N), self,
+                            error_count);
+    EXPECT_EQ(error_count, 0);
+  }
+};
+
+TEST(sycl, impl_view_accessible) {
+  TestViewSYCLAccessible<Kokkos::Experimental::SYCLDeviceUSMSpace,
+                         Kokkos::Experimental::SYCL>::run();
+
+  TestViewSYCLAccessible<Kokkos::Experimental::SYCLSharedUSMSpace,
+                         Kokkos::Experimental::SYCL>::run();
+  TestViewSYCLAccessible<Kokkos::Experimental::SYCLSharedUSMSpace,
+                         Kokkos::HostSpace::execution_space>::run();
+
+  TestViewSYCLAccessible<Kokkos::Experimental::SYCLHostUSMSpace,
+                         Kokkos::Experimental::SYCL>::run();
+  TestViewSYCLAccessible<Kokkos::Experimental::SYCLHostUSMSpace,
+                         Kokkos::HostSpace::execution_space>::run();
+}
+
+}  // namespace Test
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_SharedAlloc.cpp b/packages/kokkos/core/unit_test/sycl/TestSYCL_Task.cpp
similarity index 96%
rename from packages/kokkos/core/unit_test/cuda/TestCudaUVM_SharedAlloc.cpp
rename to packages/kokkos/core/unit_test/sycl/TestSYCL_Task.cpp
index 6602d7396a7c2fdec7e16e83079764962dbeab75..95a7b68088c1238672b2257d285d7329d52cbec7 100644
--- a/packages/kokkos/core/unit_test/cuda/TestCudaUVM_SharedAlloc.cpp
+++ b/packages/kokkos/core/unit_test/sycl/TestSYCL_Task.cpp
@@ -1,3 +1,4 @@
+
 /*
 //@HEADER
 // ************************************************************************
@@ -42,5 +43,5 @@
 //@HEADER
 */
 
-#include <TestCudaUVM_Category.hpp>
-#include <TestSharedAlloc.hpp>
+#include <TestSYCL_Category.hpp>
+#include <TestTaskScheduler.hpp>
diff --git a/packages/kokkos/core/unit_test/sycl/TestSYCL_TeamScratchStreams.cpp b/packages/kokkos/core/unit_test/sycl/TestSYCL_TeamScratchStreams.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ab0d09880f03b56d81ae693d26b5c838b2436a24
--- /dev/null
+++ b/packages/kokkos/core/unit_test/sycl/TestSYCL_TeamScratchStreams.cpp
@@ -0,0 +1,154 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+#include <TestSYCL_Category.hpp>
+#include <Kokkos_Core.hpp>
+
+namespace Test {
+
+namespace Impl {
+
+struct SYCLQueueScratchTestFunctor {
+  using team_t = Kokkos::TeamPolicy<Kokkos::Experimental::SYCL>::member_type;
+  using scratch_t =
+      Kokkos::View<int64_t*, Kokkos::Experimental::SYCL::scratch_memory_space>;
+
+  Kokkos::View<int64_t, Kokkos::Experimental::SYCLDeviceUSMSpace,
+               Kokkos::MemoryTraits<Kokkos::Atomic>>
+      counter;
+  int N, M;
+  SYCLQueueScratchTestFunctor(
+      Kokkos::View<int64_t, Kokkos::Experimental::SYCLDeviceUSMSpace> counter_,
+      int N_, int M_)
+      : counter(counter_), N(N_), M(M_) {}
+
+  KOKKOS_FUNCTION
+  void operator()(const team_t& team) const {
+    scratch_t scr(team.team_scratch(1), M);
+    Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, M),
+                         [&](int i) { scr[i] = 0; });
+    team.team_barrier();
+    for (int i = 0; i < N; i++) {
+      Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, M),
+                           [&](int j) { scr[j] += 1; });
+    }
+    team.team_barrier();
+    Kokkos::parallel_for(Kokkos::TeamThreadRange(team, 0, M), [&](int i) {
+      if (scr[i] != N) counter()++;
+    });
+  }
+};
+
+void sycl_queue_scratch_test_one(
+    int N, int T, int M_base,
+    Kokkos::View<int64_t, Kokkos::Experimental::SYCLDeviceUSMSpace> counter,
+    Kokkos::Experimental::SYCL sycl, int tid) {
+  int M = M_base + tid * 5;
+  Kokkos::TeamPolicy<Kokkos::Experimental::SYCL> p(sycl, T, 64);
+  using scratch_t =
+      Kokkos::View<int64_t*, Kokkos::Experimental::SYCL::scratch_memory_space>;
+
+  int bytes = scratch_t::shmem_size(M);
+
+  for (int r = 0; r < 15; r++) {
+    Kokkos::parallel_for("Run", p.set_scratch_size(1, Kokkos::PerTeam(bytes)),
+                         SYCLQueueScratchTestFunctor(counter, N, M));
+  }
+}
+
+void sycl_queue_scratch_test(
+    int N, int T, int M_base,
+    Kokkos::View<int64_t, Kokkos::Experimental::SYCLDeviceUSMSpace> counter) {
+  constexpr int K = 4;
+  Kokkos::Experimental::SYCL default_space;
+  sycl::context default_context = default_space.sycl_context();
+
+  sycl::default_selector device_selector;
+  sycl::queue queue(default_context, device_selector);
+
+  std::array<Kokkos::Experimental::SYCL, K> sycl;
+  for (int i = 0; i < K; i++) {
+    sycl[i] = Kokkos::Experimental::SYCL(
+        sycl::queue(default_context, device_selector));
+  }
+
+  // Test that growing scratch size in subsequent calls doesn't crash things
+#if defined(KOKKOS_ENABLE_OPENMP)
+#pragma omp parallel
+  {
+    int tid = omp_get_thread_num();
+    // Limit how many threads submit
+    if (tid < 4) {
+      sycl_queue_scratch_test_one(N, T, M_base, counter, sycl[tid], tid);
+    }
+  }
+#else
+  for (int tid = 0; tid < K; tid++) {
+    sycl_queue_scratch_test_one(N, T, M_base, counter, sycl[tid], tid);
+  }
+#endif
+  // Test that if everything is large enough, multiple launches with different
+  // scratch sizes don't step on each other
+  for (int tid = K - 1; tid >= 0; tid--) {
+    sycl_queue_scratch_test_one(N, T, M_base, counter, sycl[tid], tid);
+  }
+
+  Kokkos::fence();
+}
+}  // namespace Impl
+
+TEST(sycl, team_scratch_1_queues) {
+  int N      = 1000000;
+  int T      = 10;
+  int M_base = 150;
+
+  Kokkos::View<int64_t, Kokkos::Experimental::SYCLDeviceUSMSpace> counter("C");
+
+  Impl::sycl_queue_scratch_test(N, T, M_base, counter);
+
+  int64_t result;
+  Kokkos::deep_copy(result, counter);
+  ASSERT_EQ(0, result);
+}
+}  // namespace Test
diff --git a/packages/kokkos/core/unit_test/tools/TestBuiltinTuners.cpp b/packages/kokkos/core/unit_test/tools/TestBuiltinTuners.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..870621c1e0d3e4530573fc70ee24208b3b5a7911
--- /dev/null
+++ b/packages/kokkos/core/unit_test/tools/TestBuiltinTuners.cpp
@@ -0,0 +1,123 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+#include <Kokkos_Core.hpp>
+using ExecSpace  = Kokkos::DefaultHostExecutionSpace;
+using TeamMember = Kokkos::TeamPolicy<ExecSpace>::member_type;
+struct TestTeamFunctor {
+  KOKKOS_FUNCTION void operator()(TeamMember) const {}
+};
+struct TestMDFunctor {
+  KOKKOS_FUNCTION void operator()(const int, const int) const {}
+};
+int main(int argc, char* argv[]) {
+  Kokkos::initialize(argc, argv);
+  {
+    Kokkos::TeamPolicy<ExecSpace> teamp(1, Kokkos::AUTO, Kokkos::AUTO);
+    Kokkos::MDRangePolicy<Kokkos::Rank<2>> mdp({0, 0}, {1, 1});
+    Kokkos::Tools::Experimental::TeamSizeTuner team_tune_this(
+        "team_tuner", teamp, TestTeamFunctor{}, Kokkos::ParallelForTag{},
+        Kokkos::Tools::Impl::Impl::SimpleTeamSizeCalculator{});
+
+    Kokkos::Tools::Experimental::MDRangeTuner<2> md_tune_this(
+        "md_tuner", mdp, TestMDFunctor{}, Kokkos::ParallelForTag{},
+        Kokkos::Tools::Impl::Impl::SimpleTeamSizeCalculator{});
+
+    std::vector<int> options{1, 2, 3, 4, 5};
+
+    auto new_team_tuner = team_tune_this.combine("options", options);
+    auto new_md_tuner   = md_tune_this.combine("options", options);
+    using namespace Kokkos::Tools::Experimental;
+    VariableInfo info;
+    info.category      = StatisticalCategory::kokkos_value_categorical;
+    info.valueQuantity = CandidateValueType::kokkos_value_unbounded;
+    info.type          = ValueType::kokkos_value_string;
+    size_t input       = declare_input_type("kernel", info);
+    VariableValue team_kernel_value = make_variable_value(input, "abs");
+    VariableValue md_kernel_value   = make_variable_value(input, "abs");
+    size_t kernel_context           = get_new_context_id();
+    begin_context(kernel_context);
+    set_input_values(kernel_context, 1, &team_kernel_value);
+    for (int x = 0; x < 10000; ++x) {
+      auto config = new_md_tuner.begin();
+      int option  = std::get<0>(config);
+      (void)option;
+      int tile_x = std::get<1>(config);
+      int tile_y = std::get<2>(config);
+      Kokkos::parallel_for("mdrange",
+                           Kokkos::MDRangePolicy<Kokkos::Rank<2>>(
+                               {0, 0}, {1, 1}, {tile_x, tile_y}),
+                           TestMDFunctor{});
+      new_md_tuner.end();
+    }
+    end_context(kernel_context);
+    begin_context(kernel_context);
+    set_input_values(kernel_context, 1, &md_kernel_value);
+
+    /**
+     * Note that 0.0 is basically a floating point index into
+     * the outermost index in this, which is the options vector
+     * above. The At 0.0, this will be the first element (1).
+     * At 0.9 this will be the last element (5)
+     */
+    auto begin_point = new_team_tuner.get_point(0.0, 0.0, 0.0);
+    assert(std::get<0>(begin_point) == 1);
+    (void)begin_point;  // to avoid warnings in some compilers
+    auto end_point = new_team_tuner.get_point(0.9, 0.0, 0.0);
+    (void)end_point;  // to avoid warnings in some compilers
+    assert(std::get<0>(end_point) == 5);
+    for (int x = 0; x < 10000; ++x) {
+      auto config = new_team_tuner.begin();
+      int option  = std::get<0>(config);
+      (void)option;
+      int team   = std::get<1>(config);
+      int vector = std::get<2>(config);
+      Kokkos::parallel_for("mdrange",
+                           Kokkos::TeamPolicy<ExecSpace>(1, team, vector),
+                           TestTeamFunctor{});
+      new_team_tuner.end();
+    }
+    end_context(kernel_context);
+  }
+  Kokkos::finalize();
+}
diff --git a/packages/kokkos/core/unit_test/tools/TestCategoricalTuner.cpp b/packages/kokkos/core/unit_test/tools/TestCategoricalTuner.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2177556d392f002ad17499ad08fe93268a3c7937
--- /dev/null
+++ b/packages/kokkos/core/unit_test/tools/TestCategoricalTuner.cpp
@@ -0,0 +1,86 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+
+// This file tests the categorical tuner
+
+#include <Kokkos_Core.hpp>
+#include <unistd.h>
+struct point {
+  float x;
+  float y;
+  float z;
+};
+void do_computation(const point& test_point) {
+  usleep(((unsigned int)test_point.x) * 100);
+}
+using namespace Kokkos::Tools::Experimental;
+int main(int argc, char* argv[]) {
+  Kokkos::initialize(argc, argv);
+  {
+    VariableInfo info;
+    info.category              = StatisticalCategory::kokkos_value_categorical;
+    info.valueQuantity         = CandidateValueType::kokkos_value_unbounded;
+    info.type                  = ValueType::kokkos_value_string;
+    size_t input               = declare_input_type("kernel", info);
+    VariableValue kernel_value = make_variable_value(input, "abs");
+    size_t kernel_context      = get_new_context_id();
+    begin_context(kernel_context);
+    set_input_values(kernel_context, 1, &kernel_value);
+
+    std::vector<point> points;
+    points.push_back({1.0, 1.0, 1.0});
+    points.push_back({10.0, 10.0, 10.0});
+    points.push_back({0.0, 0.0, 0.0});
+    auto tuner =
+        Kokkos::Tools::Experimental::make_categorical_tuner("points", points);
+    for (decltype(points)::size_type x = 0; x < 3000; ++x) {
+      point test_point = tuner.begin();
+      do_computation(test_point);
+      tuner.end();
+    }
+
+    end_context(kernel_context);
+  }
+  Kokkos::finalize();
+}
diff --git a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_c.cpp b/packages/kokkos/core/unit_test/tools/TestEventCorrectness.cpp
similarity index 94%
rename from packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_c.cpp
rename to packages/kokkos/core/unit_test/tools/TestEventCorrectness.cpp
index 26dc9b0e000096ab1809412c4a29fc563844cbd1..ac0b4d26196351c6654c9b7996931784e4fa2653 100644
--- a/packages/kokkos/core/unit_test/cuda/TestCudaHostPinned_ViewAPI_c.cpp
+++ b/packages/kokkos/core/unit_test/tools/TestEventCorrectness.cpp
@@ -42,5 +42,8 @@
 //@HEADER
 */
 
-#include <TestCudaHostPinned_Category.hpp>
-#include <TestViewAPI_c.hpp>
+#include <iostream>
+#include "Kokkos_Core.hpp"
+
+#include <tools/TestEventCorrectness.hpp>
+#include "../UnitTestMainInit.cpp"
diff --git a/packages/kokkos/core/unit_test/tools/TestEventCorrectness.hpp b/packages/kokkos/core/unit_test/tools/TestEventCorrectness.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..430677a335df32737a08520467cd26513f2e83e7
--- /dev/null
+++ b/packages/kokkos/core/unit_test/tools/TestEventCorrectness.hpp
@@ -0,0 +1,284 @@
+/*
+//@HEADER
+// ************************************************************************
+//
+//                        Kokkos v. 3.0
+//       Copyright (2020) National Technology & Engineering
+//               Solutions of Sandia, LLC (NTESS).
+//
+// Under the terms of Contract DE-NA0003525 with NTESS,
+// the U.S. Government retains certain rights in this software.
+//
+// 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 Corporation nor the names of the
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY NTESS "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 NTESS OR THE
+// 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.
+//
+// Questions? Contact Christian R. Trott (crtrott@sandia.gov)
+//
+// ************************************************************************
+//@HEADER
+*/
+#include <iostream>
+#include <gtest/gtest.h>
+#include "Kokkos_Core.hpp"
+
+#include <impl/Kokkos_Stacktrace.hpp>
+#include <vector>
+#include <algorithm>
+namespace Kokkos {
+class Serial;
+class OpenMP;
+class Cuda;
+class Threads;
+namespace Experimental {
+class SYCL;
+class HIP;
+class OpenMPTarget;
+class HPX;
+}  // namespace Experimental
+}  // namespace Kokkos
+namespace Test {
+struct FencePayload {
+  std::string name;
+  enum distinguishable_devices { yes, no };
+  distinguishable_devices distinguishable;
+  uint32_t dev_id;
+};
+
+std::vector<FencePayload> found_payloads;
+template <typename Lambda>
+void expect_fence_events(std::vector<FencePayload>& expected, Lambda lam) {
+  found_payloads = {};
+  Kokkos::Tools::Experimental::set_begin_fence_callback(
+      [](const char* name, const uint32_t dev_id, uint64_t*) {
+        found_payloads.push_back(
+            FencePayload{std::string(name),
+                         FencePayload::distinguishable_devices::no, dev_id});
+      });
+  Kokkos::Tools::Experimental::set_begin_parallel_for_callback(
+      [](const char* name, const uint32_t dev_id, uint64_t*) {
+        found_payloads.push_back(
+            FencePayload{std::string(name),
+                         FencePayload::distinguishable_devices::no, dev_id});
+      });
+  lam();
+  for (auto& entry : expected) {
+    std::cout << "Ref: " << entry.dev_id << std::endl;
+    std::cout << "Ref: " << entry.name << std::endl;
+    auto search = std::find_if(
+        found_payloads.begin(), found_payloads.end(),
+        [&](const auto& found_entry) {
+          auto name_match =
+              (found_entry.name.find(entry.name) != std::string::npos);
+          auto id_match = (entry.dev_id == found_entry.dev_id);
+          std::cout << found_entry.dev_id << std::endl;
+          std::cout << found_entry.name << std::endl;
+          if (!name_match) {
+            std::cout << "Miss on name\n";
+          }
+          if (!id_match) {
+            std::cout << "Miss on id\n";
+          }
+          return (name_match && id_match);
+        });
+    auto found = (search != found_payloads.end());
+    ASSERT_TRUE(found);
+  }
+  Kokkos::Tools::Experimental::set_begin_fence_callback(
+      [](const char*, const uint32_t, uint64_t*) {});
+  Kokkos::Tools::Experimental::set_begin_parallel_for_callback(
+      [](const char*, const uint32_t, uint64_t*) {});
+}
+
+template <class>
+struct increment {
+  constexpr static const int size = 0;
+};
+int num_instances = 1;
+struct TestFunctor {
+  KOKKOS_FUNCTION void operator()(const int) const {}
+};
+template <typename Lambda>
+void test_wrapper(const Lambda& lambda) {
+  if (!std::is_same<Kokkos::DefaultExecutionSpace, Kokkos::Serial>::value) {
+    lambda();
+  }
+}
+/**
+ * Test that fencing an instance with a name yields a fence
+ * event of that name, and the correct device ID
+ */
+TEST(defaultdevicetype, test_named_instance_fence) {
+  test_wrapper([&]() {
+    auto root = Kokkos::Tools::Experimental::device_id_root<
+        Kokkos::DefaultExecutionSpace>();
+    std::vector<FencePayload> expected{
+
+        {"named_instance", FencePayload::distinguishable_devices::no,
+         root + num_instances}};
+    expect_fence_events(expected, [=]() {
+      Kokkos::DefaultExecutionSpace ex;
+      ex.fence("named_instance");
+    });
+    num_instances += increment<Kokkos::DefaultExecutionSpace>::size;
+  });
+}
+/**
+ * Test that fencing an instance without a name yields a fence
+ * event of a correct name, and the correct device ID
+ */
+TEST(defaultdevicetype, test_unnamed_instance_fence) {
+  test_wrapper([&]() {
+    auto root = Kokkos::Tools::Experimental::device_id_root<
+        Kokkos::DefaultExecutionSpace>();
+    std::vector<FencePayload> expected{
+
+        {"Unnamed Instance Fence", FencePayload::distinguishable_devices::no,
+         root + num_instances}};
+    expect_fence_events(expected, [=]() {
+      Kokkos::DefaultExecutionSpace ex;
+      ex.fence();
+    });
+    num_instances += increment<Kokkos::DefaultExecutionSpace>::size;
+  });
+}
+
+/**
+ * Test that invoking a global fence with a name yields a fence
+ * event of a correct name, and fences the root of the default device
+ */
+TEST(defaultdevicetype, test_named_global_fence) {
+  test_wrapper([&]() {
+    auto root = Kokkos::Tools::Experimental::device_id_root<
+        Kokkos::DefaultExecutionSpace>();
+
+    std::vector<FencePayload> expected{
+
+        {"test global fence", FencePayload::distinguishable_devices::no, root}};
+    expect_fence_events(expected,
+                        [=]() { Kokkos::fence("test global fence"); });
+  });
+}
+
+/**
+ * Test that invoking a global fence with no name yields a fence
+ * event of a correct name, and fences the root of the default device
+ */
+TEST(defaultdevicetype, test_unnamed_global_fence) {
+  test_wrapper([&]() {
+    auto root = Kokkos::Tools::Experimental::device_id_root<
+        Kokkos::DefaultExecutionSpace>();
+
+    std::vector<FencePayload> expected{
+
+        {"Unnamed Global Fence", FencePayload::distinguishable_devices::no,
+         root}};
+    expect_fence_events(expected, [=]() { Kokkos::fence(); });
+    num_instances += increment<Kokkos::DefaultExecutionSpace>::size;
+  });
+}
+/**
+ * Test that creating two default instances and fencing both yields
+ * fence on the same device ID, as these should yield the same instance
+ */
+TEST(defaultdevicetype, test_multiple_default_instances) {
+  test_wrapper([&]() {
+    std::vector<FencePayload> expected{};
+    expect_fence_events(expected, [=]() {
+      Kokkos::DefaultExecutionSpace ex1;
+      Kokkos::DefaultExecutionSpace ex2;
+      ex1.fence("named_instance_fence_one");
+      ex2.fence("named_instance_fence_two");
+    });
+    ASSERT_TRUE(found_payloads[0].dev_id == found_payloads[1].dev_id);
+  });
+}
+
+/**
+ * Test that fencing and kernels yield events on the correct device ID's
+ */
+TEST(defaultdevicetype, test_kernel_sequence) {
+  test_wrapper([&]() {
+    auto root = Kokkos::Tools::Experimental::device_id_root<
+        Kokkos::DefaultExecutionSpace>();
+    std::vector<FencePayload> expected{
+
+        {"named_instance", FencePayload::distinguishable_devices::no,
+         root + num_instances},
+        {"test_kernel", FencePayload::distinguishable_devices::no,
+         root + num_instances}
+
+    };
+    expect_fence_events(expected, [=]() {
+      Kokkos::DefaultExecutionSpace ex;
+      TestFunctor tf;
+      ex.fence("named_instance");
+      Kokkos::parallel_for(
+          "test_kernel",
+          Kokkos::RangePolicy<Kokkos::DefaultExecutionSpace>(ex, 0, 1), tf);
+    });
+    num_instances += increment<Kokkos::DefaultExecutionSpace>::size;
+  });
+}
+#ifdef KOKKOS_ENABLE_CUDA
+/**
+ * CUDA ONLY: test that creating instances from streams leads to events
+ * on different device ID's
+ */
+TEST(defaultdevicetype, test_streams) {
+  test_wrapper([&]() {
+    // auto root = Kokkos::Tools::Experimental::device_id_root<
+    //    Kokkos::DefaultExecutionSpace>();
+    std::vector<FencePayload> expected{};
+    expect_fence_events(expected, [=]() {
+      cudaStream_t s1, s2;
+      cudaStreamCreate(&s1);
+      cudaStreamCreate(&s2);
+      Kokkos::Cuda default_space;
+      Kokkos::Cuda space_s1(s1);
+      Kokkos::Cuda space_s2(s2);
+      default_space.fence();
+      space_s1.fence();
+      space_s2.fence();
+    });
+    num_instances += increment<Kokkos::DefaultExecutionSpace>::size;
+    found_payloads.erase(
+        std::remove_if(found_payloads.begin(), found_payloads.end(),
+                       [&](const auto& entry) {
+                         return (
+                             entry.name.find("Fence on space initialization") !=
+                             std::string::npos);
+                       }),
+        found_payloads.end());
+    ASSERT_TRUE(found_payloads[0].dev_id != found_payloads[1].dev_id);
+    ASSERT_TRUE(found_payloads[2].dev_id != found_payloads[1].dev_id);
+    ASSERT_TRUE(found_payloads[2].dev_id != found_payloads[0].dev_id);
+  });
+}
+
+#endif
+
+}  // namespace Test
diff --git a/packages/kokkos/example/query_device/query_device.cpp b/packages/kokkos/example/query_device/query_device.cpp
index a563b06b2864d5d0e855a80b836f3ef70f33f3a1..9c4e9a8c835938c0b301fa1927a2cb5d08e654c1 100644
--- a/packages/kokkos/example/query_device/query_device.cpp
+++ b/packages/kokkos/example/query_device/query_device.cpp
@@ -47,7 +47,8 @@
 
 #include <Kokkos_Macros.hpp>
 
-#if defined(KOKKOS_ENABLE_MPI)
+//#define USE_MPI
+#if defined(USE_MPI)
 #include <mpi.h>
 #endif
 
@@ -61,7 +62,7 @@ int main(int argc, char** argv) {
 
   (void)argc;
   (void)argv;
-#if defined(KOKKOS_ENABLE_MPI)
+#if defined(USE_MPI)
 
   MPI_Init(&argc, &argv);
 
@@ -72,7 +73,7 @@ int main(int argc, char** argv) {
   msg << "MPI rank(" << mpi_rank << ") ";
 
 #endif
-
+  Kokkos::initialize(argc, argv);
   msg << "{" << std::endl;
 
   if (Kokkos::hwloc::available()) {
@@ -82,15 +83,13 @@ int main(int argc, char** argv) {
         << std::endl;
   }
 
-#if defined(KOKKOS_ENABLE_CUDA)
-  Kokkos::Cuda::print_configuration(msg);
-#endif
+  Kokkos::print_configuration(msg);
 
   msg << "}" << std::endl;
 
   std::cout << msg.str();
-
-#if defined(KOKKOS_ENABLE_MPI)
+  Kokkos::finalize();
+#if defined(USE_MPI)
 
   MPI_Finalize();
 
diff --git a/packages/kokkos/example/tutorial/06_simple_mdrangepolicy/simple_mdrangepolicy.cpp b/packages/kokkos/example/tutorial/06_simple_mdrangepolicy/simple_mdrangepolicy.cpp
index 07b99087d4c310e6cf0d82c026f52bd610dd0ecb..5ac7f4fbb060ae952a0685313ec357ffa05abf96 100644
--- a/packages/kokkos/example/tutorial/06_simple_mdrangepolicy/simple_mdrangepolicy.cpp
+++ b/packages/kokkos/example/tutorial/06_simple_mdrangepolicy/simple_mdrangepolicy.cpp
@@ -107,8 +107,8 @@ int main(int argc, char* argv[]) {
 
   // ViewType aliases for Rank<2>, Rank<3> for example usage
   using ScalarType  = double;
-  using ViewType_2D = typename Kokkos::View<ScalarType**>;
-  using ViewType_3D = typename Kokkos::View<ScalarType***>;
+  using ViewType_2D = Kokkos::View<ScalarType**>;
+  using ViewType_3D = Kokkos::View<ScalarType***>;
 
   /////////////////////////////////////////////////////////////////////////////
   // Explanation of MDRangePolicy usage, template parameters, constructor
@@ -160,8 +160,7 @@ int main(int argc, char* argv[]) {
   long incorrect_count_2d = 0;
   {
     // Rank<2> Case: Rank is provided, all other parameters are default
-    using MDPolicyType_2D = typename Kokkos::Experimental::MDRangePolicy<
-        Kokkos::Experimental::Rank<2> >;
+    using MDPolicyType_2D = Kokkos::MDRangePolicy<Kokkos::Rank<2> >;
 
     // Construct 2D MDRangePolicy: lower and upper bounds provided, tile dims
     // defaulted
@@ -185,9 +184,8 @@ int main(int argc, char* argv[]) {
   long incorrect_count_3d = 0;
   {
     // Rank<3> Case: Rank, inner iterate pattern, outer iterate pattern provided
-    using MDPolicyType_3D = typename Kokkos::Experimental::MDRangePolicy<
-        Kokkos::Experimental::Rank<3, Kokkos::Experimental::Iterate::Left,
-                                   Kokkos::Experimental::Iterate::Left> >;
+    using MDPolicyType_3D = Kokkos::MDRangePolicy<
+        Kokkos::Rank<3, Kokkos::Iterate::Left, Kokkos::Iterate::Left> >;
 
     // Construct 3D MDRangePolicy: lower, upper bounds, tile dims provided
     MDPolicyType_3D mdpolicy_3d({{0, 0, 0}}, {{n, n, n}}, {{4, 4, 4}});
diff --git a/packages/kokkos/example/tutorial/Advanced_Views/01_data_layouts/data_layouts.cpp b/packages/kokkos/example/tutorial/Advanced_Views/01_data_layouts/data_layouts.cpp
index 597d1e3056ece9ef5865a3fb79dfef09ccf50a6a..75eca5403fd12ae09f2839ef696fafefa9f8f277 100644
--- a/packages/kokkos/example/tutorial/Advanced_Views/01_data_layouts/data_layouts.cpp
+++ b/packages/kokkos/example/tutorial/Advanced_Views/01_data_layouts/data_layouts.cpp
@@ -43,7 +43,7 @@
 */
 
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <cstdio>
 
 // These two View types are both 2-D arrays of double.  However, they
diff --git a/packages/kokkos/example/tutorial/Advanced_Views/02_memory_traits/memory_traits.cpp b/packages/kokkos/example/tutorial/Advanced_Views/02_memory_traits/memory_traits.cpp
index 00bfeea36b972e6ea08ab8c82ec5aaca1a4e2af5..0544e572e7e9785369bfc824db783ea2fcd5af53 100644
--- a/packages/kokkos/example/tutorial/Advanced_Views/02_memory_traits/memory_traits.cpp
+++ b/packages/kokkos/example/tutorial/Advanced_Views/02_memory_traits/memory_traits.cpp
@@ -43,7 +43,7 @@
 */
 
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <cstdio>
 #include <cstdlib>
 
diff --git a/packages/kokkos/example/tutorial/Advanced_Views/03_subviews/subviews.cpp b/packages/kokkos/example/tutorial/Advanced_Views/03_subviews/subviews.cpp
index 20e5c5a284f415e7627fd07df20ffbe5856f3428..52af4bd3b5ba84b3b5c1b53111900a9104e41922 100644
--- a/packages/kokkos/example/tutorial/Advanced_Views/03_subviews/subviews.cpp
+++ b/packages/kokkos/example/tutorial/Advanced_Views/03_subviews/subviews.cpp
@@ -49,7 +49,7 @@
 // the mesh.
 
 #include <Kokkos_Core.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <cstdio>
 
 using mesh_type = Kokkos::View<double***, Kokkos::LayoutRight>;
diff --git a/packages/kokkos/example/tutorial/Advanced_Views/04_dualviews/dual_view.cpp b/packages/kokkos/example/tutorial/Advanced_Views/04_dualviews/dual_view.cpp
index 3c0fcd085c7c2afe29a328dfa3f574ab9ac81276..622b24b93131094ebd3c331d4c4f01ae14cca325 100644
--- a/packages/kokkos/example/tutorial/Advanced_Views/04_dualviews/dual_view.cpp
+++ b/packages/kokkos/example/tutorial/Advanced_Views/04_dualviews/dual_view.cpp
@@ -44,7 +44,7 @@
 
 #include <Kokkos_Core.hpp>
 #include <Kokkos_DualView.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <cstdio>
 #include <cstdlib>
 
diff --git a/packages/kokkos/example/tutorial/Advanced_Views/05_NVIDIA_UVM/uvm_example.cpp b/packages/kokkos/example/tutorial/Advanced_Views/05_NVIDIA_UVM/uvm_example.cpp
index a906ba1447283f3a5b2517e1f6c21839b458b597..596b25aaade065c9ade57f90107e17d6fda3d06a 100644
--- a/packages/kokkos/example/tutorial/Advanced_Views/05_NVIDIA_UVM/uvm_example.cpp
+++ b/packages/kokkos/example/tutorial/Advanced_Views/05_NVIDIA_UVM/uvm_example.cpp
@@ -44,7 +44,7 @@
 
 #include <Kokkos_Core.hpp>
 #include <Kokkos_DualView.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <cstdio>
 #include <cstdlib>
 
diff --git a/packages/kokkos/example/tutorial/Advanced_Views/07_Overlapping_DeepCopy/overlapping_deepcopy.cpp b/packages/kokkos/example/tutorial/Advanced_Views/07_Overlapping_DeepCopy/overlapping_deepcopy.cpp
index c582fa17043629bd65b253e6afabd76134f1817b..c03515479d0d7e0365272f87cbe49d11b21f13aa 100644
--- a/packages/kokkos/example/tutorial/Advanced_Views/07_Overlapping_DeepCopy/overlapping_deepcopy.cpp
+++ b/packages/kokkos/example/tutorial/Advanced_Views/07_Overlapping_DeepCopy/overlapping_deepcopy.cpp
@@ -46,7 +46,7 @@
 #include <cstdio>
 #include <typeinfo>
 #include <cmath>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 
 struct FillDevice {
   double value;
diff --git a/packages/kokkos/example/tutorial/Algorithms/01_random_numbers/random_numbers.cpp b/packages/kokkos/example/tutorial/Algorithms/01_random_numbers/random_numbers.cpp
index 9c5f2d62fc58b86cbdd723e3328cf0ba1e38df27..602122b61f1b94788b95052478a6123ae41fbfd9 100644
--- a/packages/kokkos/example/tutorial/Algorithms/01_random_numbers/random_numbers.cpp
+++ b/packages/kokkos/example/tutorial/Algorithms/01_random_numbers/random_numbers.cpp
@@ -45,7 +45,7 @@
 #include <Kokkos_Core.hpp>
 #include <Kokkos_Random.hpp>
 #include <Kokkos_DualView.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <cstdlib>
 
 using DefaultHostType = Kokkos::HostSpace::execution_space;
@@ -74,7 +74,7 @@ using DefaultHostType = Kokkos::HostSpace::execution_space;
 template <class GeneratorPool>
 struct generate_random {
   // Output View for the random numbers
-  Kokkos::View<uint64_t*> vals;
+  Kokkos::View<uint64_t**> vals;
 
   // The GeneratorPool
   GeneratorPool rand_pool;
@@ -82,7 +82,7 @@ struct generate_random {
   int samples;
 
   // Initialize all members
-  generate_random(Kokkos::View<uint64_t*> vals_, GeneratorPool rand_pool_,
+  generate_random(Kokkos::View<uint64_t**> vals_, GeneratorPool rand_pool_,
                   int samples_)
       : vals(vals_), rand_pool(rand_pool_), samples(samples_) {}
 
@@ -94,8 +94,7 @@ struct generate_random {
     // Draw samples numbers from the pool as urand64 between 0 and
     // rand_pool.MAX_URAND64 Note there are function calls to get other type of
     // scalars, and also to specify Ranges or get a normal distributed float.
-    for (int k = 0; k < samples; k++)
-      vals(i * samples + k) = rand_gen.urand64();
+    for (int k = 0; k < samples; k++) vals(i, k) = rand_gen.urand64();
 
     // Give the state back, which will allow another thread to acquire it
     rand_pool.free_state(rand_gen);
@@ -103,11 +102,11 @@ struct generate_random {
 };
 
 int main(int argc, char* args[]) {
+  Kokkos::initialize(argc, args);
   if (argc != 3) {
     printf("Please pass two integers on the command line\n");
   } else {
     // Initialize Kokkos
-    Kokkos::initialize(argc, args);
     int size    = std::stoi(args[1]);
     int samples = std::stoi(args[2]);
 
@@ -117,7 +116,7 @@ int main(int argc, char* args[]) {
     // pool.
     Kokkos::Random_XorShift64_Pool<> rand_pool64(5374857);
     Kokkos::Random_XorShift1024_Pool<> rand_pool1024(5374857);
-    Kokkos::DualView<uint64_t*> vals("Vals", size * samples);
+    Kokkos::DualView<uint64_t**> vals("Vals", size, samples);
 
     // Run some performance comparisons
     Kokkos::Timer timer;
@@ -151,8 +150,7 @@ int main(int argc, char* args[]) {
            1.0e-9 * samples * size / time_1024);
 
     Kokkos::deep_copy(vals.h_view, vals.d_view);
-
-    Kokkos::finalize();
   }
+  Kokkos::finalize();
   return 0;
 }
diff --git a/packages/kokkos/example/tutorial/Hierarchical_Parallelism/04_team_scan/team_scan.cpp b/packages/kokkos/example/tutorial/Hierarchical_Parallelism/04_team_scan/team_scan.cpp
index d36010892597bbcc9d1be710cae06574e7410ba7..cc20a497b2325825cf5faf01e2fd527e71efee53 100644
--- a/packages/kokkos/example/tutorial/Hierarchical_Parallelism/04_team_scan/team_scan.cpp
+++ b/packages/kokkos/example/tutorial/Hierarchical_Parallelism/04_team_scan/team_scan.cpp
@@ -44,7 +44,7 @@
 
 #include <Kokkos_Core.hpp>
 #include <Kokkos_DualView.hpp>
-#include <impl/Kokkos_Timer.hpp>
+#include <Kokkos_Timer.hpp>
 #include <cstdio>
 #include <cstdlib>
 
diff --git a/packages/kokkos/generate_makefile.bash b/packages/kokkos/generate_makefile.bash
index e9871b436971a551c82751756b2b18de9175839a..5e33f592183b9da2a7f079a09feeab6943bceebf 100755
--- a/packages/kokkos/generate_makefile.bash
+++ b/packages/kokkos/generate_makefile.bash
@@ -157,10 +157,12 @@ display_help_text() {
       echo "                 AMDAVX          = AMD CPU"
       echo "                 ZEN             = AMD Zen-Core CPU"
       echo "                 ZEN2            = AMD Zen2-Core CPU"
+      echo "                 ZEN3            = AMD Zen3-Core CPU"
       echo "               [AMD: GPU]"
       echo "                 VEGA900         = AMD GPU MI25 GFX900"
       echo "                 VEGA906         = AMD GPU MI50/MI60 GFX906"
       echo "                 VEGA908         = AMD GPU MI100 GFX908"
+      echo "                 VEGA90A         = "
       echo "               [ARM]"
       echo "                 ARMV80          = ARMv8.0 Compatible CPU"
       echo "                 ARMV81          = ARMv8.1 Compatible CPU"
@@ -477,5 +479,5 @@ if [[ ${COMPILER} == *clang* ]]; then
    fi
 fi
 
-echo cmake $COMPILER_CMD  -DCMAKE_CXX_FLAGS="${KOKKOS_CXXFLAGS}" -DCMAKE_EXE_LINKER_FLAGS="${KOKKOS_LDFLAGS}" -DCMAKE_INSTALL_PREFIX=${PREFIX} ${KOKKOS_DEVICE_CMD} ${KOKKOS_ARCH_CMD} -DKokkos_ENABLE_TESTS=${KOKKOS_DO_TESTS} -DKokkos_ENABLE_EXAMPLES=${KOKKOS_DO_EXAMPLES} ${KOKKOS_OPTION_CMD} ${KOKKOS_CUDA_OPTION_CMD} ${KOKKOS_HIP_OPTION_CMD} -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_EXTENSIONS=OFF ${STANDARD_CMD} ${KOKKOS_DEBUG_CMD} ${KOKKOS_BC_CMD} ${KOKKOS_HWLOC_CMD} ${KOKKOS_HWLOC_PATH_CMD} ${KOKKOS_MEMKIND_CMD} ${KOKKOS_MEMKIND_PATH_CMD} ${KOKKOS_PATH}
-cmake $COMPILER_CMD  -DCMAKE_CXX_FLAGS="${KOKKOS_CXXFLAGS//\"}" -DCMAKE_EXE_LINKER_FLAGS="${KOKKOS_LDFLAGS//\"}" -DCMAKE_INSTALL_PREFIX=${PREFIX} ${KOKKOS_DEVICE_CMD} ${KOKKOS_ARCH_CMD} -DKokkos_ENABLE_TESTS=${KOKKOS_DO_TESTS} -DKokkos_ENABLE_EXAMPLES=${KOKKOS_DO_EXAMPLES} ${KOKKOS_OPTION_CMD} ${KOKKOS_CUDA_OPTION_CMD} ${KOKKOS_HIP_OPTION_CMD} -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_EXTENSIONS=OFF ${STANDARD_CMD} ${KOKKOS_DEBUG_CMD} ${KOKKOS_BC_CMD} ${KOKKOS_HWLOC_CMD} ${KOKKOS_HWLOC_PATH_CMD} ${KOKKOS_MEMKIND_CMD} ${KOKKOS_MEMKIND_PATH_CMD} ${PASSTHRU_CMAKE_FLAGS} ${KOKKOS_PATH}
+echo cmake $COMPILER_CMD  -DCMAKE_CXX_FLAGS="${KOKKOS_CXXFLAGS}" -DCMAKE_EXE_LINKER_FLAGS="${KOKKOS_LDFLAGS}" -DCMAKE_INSTALL_PREFIX=${PREFIX} ${KOKKOS_DEVICE_CMD} ${KOKKOS_ARCH_CMD} -DKokkos_ENABLE_TESTS=${KOKKOS_DO_TESTS} -DKokkos_ENABLE_EXAMPLES=${KOKKOS_DO_EXAMPLES} ${KOKKOS_OPTION_CMD} ${KOKKOS_CUDA_OPTION_CMD} ${KOKKOS_HIP_OPTION_CMD} -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_EXTENSIONS=OFF ${STANDARD_CMD} ${KOKKOS_DEBUG_CMD} ${KOKKOS_BC_CMD} ${KOKKOS_HWLOC_CMD} ${KOKKOS_HWLOC_PATH_CMD} ${KOKKOS_MEMKIND_CMD} ${KOKKOS_MEMKIND_PATH_CMD} -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF ${KOKKOS_PATH}
+cmake $COMPILER_CMD  -DCMAKE_CXX_FLAGS="${KOKKOS_CXXFLAGS//\"}" -DCMAKE_EXE_LINKER_FLAGS="${KOKKOS_LDFLAGS//\"}" -DCMAKE_INSTALL_PREFIX=${PREFIX} ${KOKKOS_DEVICE_CMD} ${KOKKOS_ARCH_CMD} -DKokkos_ENABLE_TESTS=${KOKKOS_DO_TESTS} -DKokkos_ENABLE_EXAMPLES=${KOKKOS_DO_EXAMPLES} ${KOKKOS_OPTION_CMD} ${KOKKOS_CUDA_OPTION_CMD} ${KOKKOS_HIP_OPTION_CMD} -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_CXX_EXTENSIONS=OFF ${STANDARD_CMD} ${KOKKOS_DEBUG_CMD} ${KOKKOS_BC_CMD} ${KOKKOS_HWLOC_CMD} ${KOKKOS_HWLOC_PATH_CMD} ${KOKKOS_MEMKIND_CMD} ${KOKKOS_MEMKIND_PATH_CMD} ${PASSTHRU_CMAKE_FLAGS} -DKokkos_ENABLE_DEPRECATION_WARNINGS=OFF ${KOKKOS_PATH}
diff --git a/packages/kokkos/gnu_generate_makefile.bash b/packages/kokkos/gnu_generate_makefile.bash
index ea509669f068d677a0354c83891d7caf298b1e34..8a463270c85548e4d14fb8c8cda9d5c478bdb96f 100755
--- a/packages/kokkos/gnu_generate_makefile.bash
+++ b/packages/kokkos/gnu_generate_makefile.bash
@@ -137,6 +137,7 @@ do
       echo "                 AMDAVX          = AMD CPU"
       echo "                 ZEN             = AMD Zen-Core CPU"
       echo "                 ZEN2            = AMD Zen2-Core CPU"
+      echo "                 ZEN3            = AMD Zen3-Core CPU"
       echo "               [ARM]"
       echo "                 ARMv80          = ARMv8.0 Compatible CPU"
       echo "                 ARMv81          = ARMv8.1 Compatible CPU"
diff --git a/packages/kokkos/master_history.txt b/packages/kokkos/master_history.txt
index 7a58f593d00e424b7d7dcbda226f5c4c6d7ccd3c..69cd133b44b013145a3a4cfa4bb8c0124ef68d73 100644
--- a/packages/kokkos/master_history.txt
+++ b/packages/kokkos/master_history.txt
@@ -24,3 +24,5 @@ tag:  3.2.00     date: 08:19:2020    master: 3b2fdc7e    release: 5dc6d303
 tag:  3.3.00     date: 12:16:2020    master: 734f577a    release: 1535ba5c
 tag:  3.3.01     date: 01:06:2021    master: 6d65b5a3    release: 4d23839c
 tag:  3.4.00     date: 04:26:2021    master: 1fb0c284    release: 5d7738d6
+tag:  3.4.01     date: 05:20:2021    master: 4b97a22f    release: 410b15c8
+tag:  3.5.00     date: 10:28:2021    master: c28a8b03    release: ddad6256
diff --git a/packages/kokkos/scripts/docker/Dockerfile.clang b/packages/kokkos/scripts/docker/Dockerfile.clang
index 6aaf75fae55ff975df5045bb73a0813236871d89..92999a8a44a54c22a40e717a5858a9d0dc5b7199 100644
--- a/packages/kokkos/scripts/docker/Dockerfile.clang
+++ b/packages/kokkos/scripts/docker/Dockerfile.clang
@@ -9,16 +9,22 @@ RUN apt-get update && apt-get install -y \
     apt-get clean && \
     rm -rf /var/lib/apt/lists/*
 
+RUN KEYDUMP_URL=https://cloud.cees.ornl.gov/download && \
+    KEYDUMP_FILE=keydump && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE} && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE}.sig && \
+    gpg --import ${KEYDUMP_FILE} && \
+    gpg --verify ${KEYDUMP_FILE}.sig ${KEYDUMP_FILE} && \
+    rm ${KEYDUMP_FILE}*
+
 ARG CMAKE_VERSION=3.16.8
 ENV CMAKE_DIR=/opt/cmake
-RUN CMAKE_KEY=2D2CEF1034921684 && \
-    CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
+RUN CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
     CMAKE_SCRIPT=cmake-${CMAKE_VERSION}-Linux-x86_64.sh && \
     CMAKE_SHA256=cmake-${CMAKE_VERSION}-SHA-256.txt && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256} && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256}.asc && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SCRIPT} && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys ${CMAKE_KEY} && \
     gpg --verify ${CMAKE_SHA256}.asc ${CMAKE_SHA256} && \
     grep ${CMAKE_SCRIPT} ${CMAKE_SHA256} | sha256sum --check && \
     mkdir -p ${CMAKE_DIR} && \
@@ -28,13 +34,11 @@ ENV PATH=${CMAKE_DIR}/bin:$PATH
 
 ENV LLVM_DIR=/opt/llvm
 RUN LLVM_VERSION=8.0.0 && \
-    LLVM_KEY=345AD05D && \
     LLVM_URL=http://releases.llvm.org/${LLVM_VERSION}/clang+llvm-${LLVM_VERSION}-x86_64-linux-gnu-ubuntu-16.04.tar.xz && \
     LLVM_ARCHIVE=llvm-${LLVM_VERSION}.tar.xz && \
     SCRATCH_DIR=/scratch && mkdir -p ${SCRATCH_DIR} && cd ${SCRATCH_DIR} && \
     wget --quiet ${LLVM_URL} --output-document=${LLVM_ARCHIVE} && \
     wget --quiet ${LLVM_URL}.sig --output-document=${LLVM_ARCHIVE}.sig && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys ${LLVM_KEY} && \
     gpg --verify ${LLVM_ARCHIVE}.sig ${LLVM_ARCHIVE} && \
     mkdir -p ${LLVM_DIR} && \
     tar -xvf ${LLVM_ARCHIVE} -C ${LLVM_DIR} --strip-components=1 && \
diff --git a/packages/kokkos/scripts/docker/Dockerfile.gcc b/packages/kokkos/scripts/docker/Dockerfile.gcc
index 56972d3185d0f62e6b9effb64e8f2cedefe25c66..51d50e64063b611a79a86c8bea159c6435bdc492 100644
--- a/packages/kokkos/scripts/docker/Dockerfile.gcc
+++ b/packages/kokkos/scripts/docker/Dockerfile.gcc
@@ -1,15 +1,21 @@
 FROM gcc:5.3.0
 
+RUN KEYDUMP_URL=https://cloud.cees.ornl.gov/download && \
+    KEYDUMP_FILE=keydump && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE} && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE}.sig && \
+    gpg --import ${KEYDUMP_FILE} && \
+    gpg --verify ${KEYDUMP_FILE}.sig ${KEYDUMP_FILE} && \
+    rm ${KEYDUMP_FILE}*
+
 ARG CMAKE_VERSION=3.16.8
 ENV CMAKE_DIR=/opt/cmake
-RUN CMAKE_KEY=2D2CEF1034921684 && \
-    CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
+RUN CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
     CMAKE_SCRIPT=cmake-${CMAKE_VERSION}-Linux-x86_64.sh && \
     CMAKE_SHA256=cmake-${CMAKE_VERSION}-SHA-256.txt && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256} && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256}.asc && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SCRIPT} && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys ${CMAKE_KEY} && \
     gpg --verify ${CMAKE_SHA256}.asc ${CMAKE_SHA256} && \
     grep ${CMAKE_SCRIPT} ${CMAKE_SHA256} | sha256sum --check && \
     mkdir -p ${CMAKE_DIR} && \
diff --git a/packages/kokkos/scripts/docker/Dockerfile.hipcc b/packages/kokkos/scripts/docker/Dockerfile.hipcc
index d3b6b93a023396aa785703a5aeec0c4001af34e8..5bef7f2ef814ad7420b8c0d4bdefa0961f4dd211 100644
--- a/packages/kokkos/scripts/docker/Dockerfile.hipcc
+++ b/packages/kokkos/scripts/docker/Dockerfile.hipcc
@@ -1,8 +1,7 @@
-ARG BASE=rocm/dev-ubuntu-20.04:3.8
+ARG BASE=rocm/dev-ubuntu-20.04:4.2
 FROM $BASE
 
 RUN apt-get update && apt-get install -y \
-        git \
         kmod \
         wget \
         ccache \
@@ -13,16 +12,22 @@ RUN apt-get update && apt-get install -y \
 
 ENV PATH=/opt/rocm/bin:$PATH
 
+RUN KEYDUMP_URL=https://cloud.cees.ornl.gov/download && \
+    KEYDUMP_FILE=keydump && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE} && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE}.sig && \
+    gpg --import ${KEYDUMP_FILE} && \
+    gpg --verify ${KEYDUMP_FILE}.sig ${KEYDUMP_FILE} && \
+    rm ${KEYDUMP_FILE}*
+
 ARG CMAKE_VERSION=3.16.8
 ENV CMAKE_DIR=/opt/cmake
-RUN CMAKE_KEY=2D2CEF1034921684 && \
-    CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
+RUN CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
     CMAKE_SCRIPT=cmake-${CMAKE_VERSION}-Linux-x86_64.sh && \
     CMAKE_SHA256=cmake-${CMAKE_VERSION}-SHA-256.txt && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256} && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256}.asc && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SCRIPT} && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys ${CMAKE_KEY} && \
     gpg --verify ${CMAKE_SHA256}.asc ${CMAKE_SHA256} && \
     grep ${CMAKE_SCRIPT} ${CMAKE_SHA256} | sha256sum --check && \
     mkdir -p ${CMAKE_DIR} && \
diff --git a/packages/kokkos/scripts/docker/Dockerfile.kokkosllvmproject b/packages/kokkos/scripts/docker/Dockerfile.kokkosllvmproject
index 5d53a645e4bc7c551698719d3edb1c3768467ca7..3de9a7f5804f938d3c4056c723f9a25ceb189242 100644
--- a/packages/kokkos/scripts/docker/Dockerfile.kokkosllvmproject
+++ b/packages/kokkos/scripts/docker/Dockerfile.kokkosllvmproject
@@ -11,16 +11,22 @@ RUN apt-get update && apt-get install -y \
     apt-get clean && \
     rm -rf /var/lib/apt/lists/*
 
+RUN KEYDUMP_URL=https://cloud.cees.ornl.gov/download && \
+    KEYDUMP_FILE=keydump && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE} && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE}.sig && \
+    gpg --import ${KEYDUMP_FILE} && \
+    gpg --verify ${KEYDUMP_FILE}.sig ${KEYDUMP_FILE} && \
+    rm ${KEYDUMP_FILE}*
+
 ARG CMAKE_VERSION=3.16.8
 ENV CMAKE_DIR=/opt/cmake
-RUN CMAKE_KEY=2D2CEF1034921684 && \
-    CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
+RUN CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
     CMAKE_SCRIPT=cmake-${CMAKE_VERSION}-Linux-x86_64.sh && \
     CMAKE_SHA256=cmake-${CMAKE_VERSION}-SHA-256.txt && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256} && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256}.asc && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SCRIPT} && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys ${CMAKE_KEY} && \
     gpg --verify ${CMAKE_SHA256}.asc ${CMAKE_SHA256} && \
     grep ${CMAKE_SCRIPT} ${CMAKE_SHA256} | sha256sum --check && \
     mkdir -p ${CMAKE_DIR} && \
diff --git a/packages/kokkos/scripts/docker/Dockerfile.nvcc b/packages/kokkos/scripts/docker/Dockerfile.nvcc
index e17accc0663980694821b8002b976277fcd9ca42..8a054066bde8e1983c9b9baf511836c88eabefa5 100644
--- a/packages/kokkos/scripts/docker/Dockerfile.nvcc
+++ b/packages/kokkos/scripts/docker/Dockerfile.nvcc
@@ -5,7 +5,6 @@ ARG ADDITIONAL_PACKAGES
 
 RUN apt-get update && apt-get install -y \
         bc \
-        git \
         wget \
         ccache \
         $ADDITIONAL_PACKAGES \
@@ -13,16 +12,22 @@ RUN apt-get update && apt-get install -y \
     apt-get clean && \
     rm -rf /var/lib/apt/lists/*
 
+RUN KEYDUMP_URL=https://cloud.cees.ornl.gov/download && \
+    KEYDUMP_FILE=keydump && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE} && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE}.sig && \
+    gpg --import ${KEYDUMP_FILE} && \
+    gpg --verify ${KEYDUMP_FILE}.sig ${KEYDUMP_FILE} && \
+    rm ${KEYDUMP_FILE}*
+
 ARG CMAKE_VERSION=3.16.8
 ENV CMAKE_DIR=/opt/cmake
-RUN CMAKE_KEY=2D2CEF1034921684 && \
-    CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
+RUN CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
     CMAKE_SCRIPT=cmake-${CMAKE_VERSION}-Linux-x86_64.sh && \
     CMAKE_SHA256=cmake-${CMAKE_VERSION}-SHA-256.txt && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256} && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256}.asc && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SCRIPT} && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys ${CMAKE_KEY} && \
     gpg --verify ${CMAKE_SHA256}.asc ${CMAKE_SHA256} && \
     grep ${CMAKE_SCRIPT} ${CMAKE_SHA256} | sha256sum --check && \
     mkdir -p ${CMAKE_DIR} && \
diff --git a/packages/kokkos/scripts/docker/Dockerfile.openmptarget b/packages/kokkos/scripts/docker/Dockerfile.openmptarget
index b6efcb82cae1a8da1cf82e050bf4ad7b8a7870e4..5a676ca32a484dec5ee6e89ca3b4acf21acbbd81 100644
--- a/packages/kokkos/scripts/docker/Dockerfile.openmptarget
+++ b/packages/kokkos/scripts/docker/Dockerfile.openmptarget
@@ -14,16 +14,22 @@ RUN apt-get update && apt-get install -y \
 
 ARG NPROC=8
 
+RUN KEYDUMP_URL=https://cloud.cees.ornl.gov/download && \
+    KEYDUMP_FILE=keydump && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE} && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE}.sig && \
+    gpg --import ${KEYDUMP_FILE} && \
+    gpg --verify ${KEYDUMP_FILE}.sig ${KEYDUMP_FILE} && \
+    rm ${KEYDUMP_FILE}*
+
 ARG CMAKE_VERSION=3.18.5
 ENV CMAKE_DIR=/opt/cmake
-RUN CMAKE_KEY=2D2CEF1034921684 && \
-    CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
+RUN CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
     CMAKE_SCRIPT=cmake-${CMAKE_VERSION}-Linux-x86_64.sh && \
     CMAKE_SHA256=cmake-${CMAKE_VERSION}-SHA-256.txt && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256} && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256}.asc && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SCRIPT} && \
-    gpg --keyserver hkps.pool.sks-keyservers.net --recv-keys ${CMAKE_KEY} && \
     gpg --verify ${CMAKE_SHA256}.asc ${CMAKE_SHA256} && \
     grep ${CMAKE_SCRIPT} ${CMAKE_SHA256} | sha256sum --check && \
     mkdir -p ${CMAKE_DIR} && \
diff --git a/packages/kokkos/scripts/docker/Dockerfile.sycl b/packages/kokkos/scripts/docker/Dockerfile.sycl
index fdcd6d01fb8e3158000aa1507bb5bfcf7e0d9b4e..3393d0da8a7f257f71d79eb5dfd9f76b5bfd6a31 100644
--- a/packages/kokkos/scripts/docker/Dockerfile.sycl
+++ b/packages/kokkos/scripts/docker/Dockerfile.sycl
@@ -3,25 +3,31 @@ FROM $BASE
 
 RUN apt-get update && apt-get install -y \
         bc \
-        git \
         wget \
         ccache \
         ninja-build \
         python3 \
+        git \
         && \
     apt-get clean && \
     rm -rf /var/lib/apt/lists/*
 
+RUN KEYDUMP_URL=https://cloud.cees.ornl.gov/download && \
+    KEYDUMP_FILE=keydump && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE} && \
+    wget --quiet ${KEYDUMP_URL}/${KEYDUMP_FILE}.sig && \
+    gpg --import ${KEYDUMP_FILE} && \
+    gpg --verify ${KEYDUMP_FILE}.sig ${KEYDUMP_FILE} && \
+    rm ${KEYDUMP_FILE}*
+
 ARG CMAKE_VERSION=3.18.5
 ENV CMAKE_DIR=/opt/cmake
-RUN CMAKE_KEY=2D2CEF1034921684 && \
-    CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
+RUN CMAKE_URL=https://github.com/Kitware/CMake/releases/download/v${CMAKE_VERSION} && \
     CMAKE_SCRIPT=cmake-${CMAKE_VERSION}-Linux-x86_64.sh && \
     CMAKE_SHA256=cmake-${CMAKE_VERSION}-SHA-256.txt && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256} && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SHA256}.asc && \
     wget --quiet ${CMAKE_URL}/${CMAKE_SCRIPT} && \
-    gpg --keyserver pool.sks-keyservers.net --recv-keys ${CMAKE_KEY} && \
     gpg --verify ${CMAKE_SHA256}.asc ${CMAKE_SHA256} && \
     grep ${CMAKE_SCRIPT} ${CMAKE_SHA256} | sha256sum --check && \
     mkdir -p ${CMAKE_DIR} && \
@@ -30,7 +36,7 @@ RUN CMAKE_KEY=2D2CEF1034921684 && \
 ENV PATH=${CMAKE_DIR}/bin:$PATH
 
 ENV SYCL_DIR=/opt/sycl
-RUN SYCL_VERSION=20210311 && \
+RUN SYCL_VERSION=20210621 && \
     SYCL_URL=https://github.com/intel/llvm/archive/sycl-nightly && \
     SYCL_ARCHIVE=${SYCL_VERSION}.tar.gz && \
     SCRATCH_DIR=/scratch && mkdir -p ${SCRATCH_DIR} && cd ${SCRATCH_DIR} && \
diff --git a/packages/kokkos/scripts/testing_scripts/generate_makefile.bash b/packages/kokkos/scripts/testing_scripts/generate_makefile.bash
index f21124ed6e716844e876cf209ee2af5cb9a7dbbd..ff9620efa689254af88bf64637e4998ca2d75265 100755
--- a/packages/kokkos/scripts/testing_scripts/generate_makefile.bash
+++ b/packages/kokkos/scripts/testing_scripts/generate_makefile.bash
@@ -129,6 +129,7 @@ do
       echo "                 AMDAVX          = AMD CPU"
       echo "                 ZEN             = AMD Zen-Core CPU"
       echo "                 ZEN2            = AMD Zen2-Core CPU"
+      echo "                 ZEN3            = AMD Zen3-Core CPU"
       echo "               [ARM]"
       echo "                 ARMv80          = ARMv8.0 Compatible CPU"
       echo "                 ARMv81          = ARMv8.1 Compatible CPU"
diff --git a/packages/kokkos/scripts/testing_scripts/test_all_sandia b/packages/kokkos/scripts/testing_scripts/test_all_sandia
index 877b35b73e1aef7c64cdb2d7e5f00f7bc235781c..3e0295643e48b8af85a8aa39874545f7340b157b 100755
--- a/packages/kokkos/scripts/testing_scripts/test_all_sandia
+++ b/packages/kokkos/scripts/testing_scripts/test_all_sandia
@@ -108,6 +108,10 @@ if [[ "$HOSTNAME" == cn* ]]; then # Warning: very generic name
   MACHINE=mayer
 fi
 
+if [[ "$HOSTNAME" == caraway* ]]; then
+  MACHINE=caraway
+fi
+
 if [[ "$HOSTNAME" == kokkos-dev\.sandia\.gov* ]]; then
   MACHINE=kokkos-dev
 fi
@@ -302,6 +306,7 @@ if [ "$MACHINE" = "sems" ]; then
                "clang/5.0.1 $BASE_MODULE_LIST $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS"
                "clang/7.0.1 $BASE_MODULE_LIST $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS"
                "clang/9.0.0 $BASE_MODULE_LIST $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS"
+               "clang/10.0.0 $BASE_MODULE_LIST $CLANG_BUILD_LIST clang++ $CLANG_WARNING_FLAGS"
                "intel/17.0.1 $BASE_MODULE_LIST $INTEL_BUILD_LIST icpc $INTEL_WARNING_FLAGS"
                "intel/18.0.5 $BASE_MODULE_LIST $INTEL_BUILD_LIST icpc $INTEL_WARNING_FLAGS"
                "intel/19.0.5 $BASE_MODULE_LIST $INTEL_BUILD_LIST icpc $INTEL_WARNING_FLAGS"
@@ -430,8 +435,8 @@ elif [ "$MACHINE" = "weaver" ]; then
 
   BASE_MODULE_LIST="cmake/3.19.3,<COMPILER_NAME>/<COMPILER_VERSION>"
   IBM_MODULE_LIST="cmake/3.19.3,<COMPILER_NAME>/xl/<COMPILER_VERSION>,gcc/7.2.0"
-  CUDA_MODULE_LIST="cmake/3.19.3,<COMPILER_NAME>/<COMPILER_VERSION>,gcc/7.2.0,ibm/xl/16.1.1"
-  CUDA10_MODULE_LIST="cmake/3.19.3,<COMPILER_NAME>/<COMPILER_VERSION>,gcc/7.4.0,ibm/xl/16.1.1"
+  CUDA_MODULE_LIST="cmake/3.19.3,<COMPILER_NAME>/<COMPILER_VERSION>,ibm/xl/16.1.1,gcc/7.2.0"
+  CUDA10_MODULE_LIST="cmake/3.19.3,<COMPILER_NAME>/<COMPILER_VERSION>,ibm/xl/16.1.1,gcc/7.4.0"
 
   # Don't do pthread with Power
   GCC_BUILD_LIST="OpenMP,Serial,OpenMP_Serial"
@@ -494,6 +499,23 @@ elif [ "$MACHINE" = "mayer" ]; then
     ARCH_FLAG="--arch=ARMV8_THUNDERX2"
   fi
 
+elif [ "$MACHINE" = "caraway" ]; then
+  SKIP_HWLOC=True
+
+  BASE_MODULE_LIST="cmake/3.19.3,<COMPILER_NAME>/<COMPILER_VERSION>"
+
+  HIPCLANG_BUILD_LIST="Hip_Serial,Hip_OpenMP"
+  HIPCLANG_WARNING_FLAGS="-Werror -Wno-unused-command-line-argument -DNDEBUG"
+
+  # Format: (compiler module-list build-list exe-name warning-flag)
+  COMPILERS=("rocm/4.2.0 $BASE_MODULE_LIST $HIPCLANG_BUILD_LIST hipcc $HIPCLANG_WARNING_FLAGS"
+             "rocm/4.3.0 $BASE_MODULE_LIST $HIPCLANG_BUILD_LIST hipcc $HIPCLANG_WARNING_FLAGS"
+  )
+
+  if [ -z "$ARCH_FLAG" ]; then
+    ARCH_FLAG="--arch=VEGA906"
+  fi
+
 elif [ "$MACHINE" = "blake" ]; then
   source /etc/profile.d/modules.sh
   SKIP_HWLOC=True
@@ -597,8 +619,9 @@ elif [ "$MACHINE" = "kokkos-dev-2" ]; then
 
   BASE_MODULE_LIST="sems-env,sems-cmake/3.17.1,sems-<COMPILER_NAME>/<COMPILER_VERSION>"
   GCC91_MODULE_LIST="sems-env,sems-cmake/3.17.1,<COMPILER_NAME>/<COMPILER_VERSION>"
-  NVCC9_MODULE_LIST="sems-env,sems-cmake/3.17.1,<COMPILER_NAME>/<COMPILER_VERSION>,sems-gcc/5.3.0"
+  NVCC9_MODULE_LIST="sems-env,sems-cmake/3.17.1,sems-<COMPILER_NAME>/<COMPILER_VERSION>,sems-gcc/5.3.0"
   NVCC_MODULE_LIST="sems-env,sems-cmake/3.17.1,<COMPILER_NAME>/<COMPILER_VERSION>,sems-gcc/7.3.0"
+  NVCC_SEMSMODULE_LIST="sems-env,sems-cmake/3.17.1,sems-<COMPILER_NAME>/<COMPILER_VERSION>,sems-gcc/7.3.0"
   NVCC11_MODULE_LIST="sems-env,sems-cmake/3.17.1,<COMPILER_NAME>/<COMPILER_VERSION>,sems-gcc/9.2.0"
 
   CLANG8_MODULE_LIST="sems-env,sems-cmake/3.17.1,<COMPILER_NAME>/<COMPILER_VERSION>,cuda/10.0"
@@ -620,13 +643,16 @@ elif [ "$MACHINE" = "kokkos-dev-2" ]; then
   else
     # Format: (compiler module-list build-list exe-name warning-flag)
     COMPILERS=("cuda/10.0 $NVCC_MODULE_LIST $BUILD_LIST_CUDA_NVCC $KOKKOS_PATH/bin/nvcc_wrapper $CUDA_WARNING_FLAGS"
-               "cuda/10.1 $NVCC_MODULE_LIST $BUILD_LIST_CUDA_NVCC $KOKKOS_PATH/bin/nvcc_wrapper $CUDA_WARNING_FLAGS"
+               "cuda/10.1 $NVCC_SEMSMODULE_LIST $BUILD_LIST_CUDA_NVCC $KOKKOS_PATH/bin/nvcc_wrapper $CUDA_WARNING_FLAGS"
                "cuda/11.0 $NVCC11_MODULE_LIST $BUILD_LIST_CUDA_NVCC $KOKKOS_PATH/bin/nvcc_wrapper $CUDA_WARNING_FLAGS"
+               "cuda/11.1 $NVCC_SEMSMODULE_LIST $CUDA_BUILD_LIST $KOKKOS_PATH/bin/nvcc_wrapper $CUDA_WARNING_FLAGS"
+               "cuda/11.2 $NVCC11_MODULE_LIST $CUDA_BUILD_LIST $KOKKOS_PATH/bin/nvcc_wrapper $CUDA_WARNING_FLAGS"
                "cuda/9.2 $NVCC9_MODULE_LIST $BUILD_LIST_CUDA_NVCC $KOKKOS_PATH/bin/nvcc_wrapper $CUDA_WARNING_FLAGS"
                "clang/8.0 $CLANG8_MODULE_LIST $BUILD_LIST_CUDA_CLANG clang++ $CUDA_WARNING_FLAGS"
                "clang/8.0 $CLANG8_MODULE_LIST $BUILD_LIST_CLANG clang++ $CLANG_WARNING_FLAGS"
                "gcc/5.3.0 $BASE_MODULE_LIST $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS"
                "gcc/6.1.0 $BASE_MODULE_LIST $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS"
+               "gcc/7.2.0 $BASE_MODULE_LIST $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS"
                "gcc/7.3.0 $BASE_MODULE_LIST $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS"
                "gcc/8.3.0 $BASE_MODULE_LIST $GCC_BUILD_LIST g++ $GCC_WARNING_FLAGS"
                "gcc/9.1 $GCC91_MODULE_LIST "$GCC_BUILD_LIST" g++ $GCC_WARNING_FLAGS"
diff --git a/packages/kokkos/scripts/testing_scripts/update_lib.sh b/packages/kokkos/scripts/testing_scripts/update_lib.sh
index 34ab5dd3c9a0afae4b10b70d99772308f35b3f9f..ee2f66dc407a76dd7f70c6c14b789fd42584fb11 100755
--- a/packages/kokkos/scripts/testing_scripts/update_lib.sh
+++ b/packages/kokkos/scripts/testing_scripts/update_lib.sh
@@ -20,7 +20,7 @@ check_sems_clang() {
   CLANGVER=$(clang --version | grep "clang version" | cut -d " " -f 3)
   if [[ "${CLANGVER}" = 9.* ]] || [[ "${CLANGVER}" = 10.* ]]; then
     # Newer gcc needed for c++ standard beyond c++14
-    module swap sems-gcc/5.3.0 sems-gcc/6.4.0
+    module swap sems-gcc/5.3.0 sems-gcc/8.3.0
     module list
   fi
 }
diff --git a/packages/rang/.gitrepo b/packages/rang/.gitrepo
index 063e422bea8d80263774104cf5de98b8bd77b5ad..07b36cbe1e0f9f53ba70936e5f34128e3d6ab287 100644
--- a/packages/rang/.gitrepo
+++ b/packages/rang/.gitrepo
@@ -6,7 +6,7 @@
 [subrepo]
 	remote = git@github.com:agauniyal/rang.git
 	branch = master
-	commit = cabe04d6d6b05356fa8f9741704924788f0dd762
-	parent = 61d96553d759fb36c6592e9b2b41a4af8acc45f1
-	cmdver = 0.4.0
+	commit = a083cae9d64edd66d0ecdd4ddebac20d090c8693
+	parent = d97199d99e580797e39a626ad2ac6d87c125bcb5
+	cmdver = 0.4.3
 	method = merge
diff --git a/packages/rang/.travis.yml b/packages/rang/.travis.yml
deleted file mode 100644
index c85aa7957a0db8a456310c9d3e13b92156835a89..0000000000000000000000000000000000000000
--- a/packages/rang/.travis.yml
+++ /dev/null
@@ -1,154 +0,0 @@
-dist: trusty
-sudo: false
-group: travis_latest
-language: c++
-
-env:
-  global:
-  - COMPILER=g++
-
-addons:
-  apt:
-    sources: &apt_sources
-      - ubuntu-toolchain-r-test
-      - llvm-toolchain-precise-3.5
-      - llvm-toolchain-precise-3.6
-      - llvm-toolchain-precise-3.7
-      - llvm-toolchain-precise-3.8
-      - llvm-toolchain-trusty-3.9
-      - llvm-toolchain-trusty-4.0
-      - llvm-toolchain-trusty-5.0
-
-compiler: clang
-os: linux
-
-matrix:
-  fast_finish: true
-  include:
-    - env: COMPILER=g++-5
-      compiler: gcc
-      addons: &gcc5
-        apt:
-          packages: ["g++-5", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-    - env: COMPILER=g++-6
-      compiler: gcc
-      addons: &gcc6
-        apt:
-          packages: ["g++-6", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-    - env: COMPILER=g++-7
-      compiler: gcc
-      addons: &gcc7
-        apt:
-          packages: ["g++-7", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-    - env: COMPILER=clang++-3.5
-      compiler: clang
-      addons: &clang35
-        apt:
-          packages: ["clang-3.5", "g++-7", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-    - env: COMPILER=clang++-3.6
-      compiler: clang
-      addons: &clang36
-        apt:
-          packages: ["clang-3.6", "g++-7", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-    - env: COMPILER=clang++-3.7
-      compiler: clang
-      addons: &clang37
-        apt:
-          packages: ["clang-3.7", "g++-7", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-    - env: COMPILER=clang++-3.8
-      compiler: clang
-      addons: &clang38
-        apt:
-          packages: ["clang-3.8", "g++-7", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-    - env: COMPILER=clang++-3.9
-      compiler: clang
-      addons: &clang39
-        apt:
-          packages: ["clang-3.9", "g++-7", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-    - env: COMPILER=clang++-4.0
-      compiler: clang
-      addons: &clang40
-        apt:
-          packages: ["clang-4.0", "g++-7", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-    - env: COMPILER=clang++-5.0
-      compiler: clang
-      addons: &clang50
-        apt:
-          packages: ["clang-5.0", "g++-7", "python3-pip", "lcov"]
-          sources: *apt_sources
-
-  allow_failures:
-    - env: COMPILER=clang++-3.7
-      compiler: clang
-
-    - env: COMPILER=clang++-3.9
-      compiler: clang
-
-
-install:
-  - wget https://github.com/ninja-build/ninja/releases/download/v1.8.2/ninja-linux.zip && unzip -q ninja-linux.zip -d build-ninja
-  - wget https://github.com/danmar/cppcheck/releases/download/1.81/cppcheck-1.81.zip && unzip -q cppcheck-1.81.zip
-  - cd cppcheck-1.81/ && make SRCDIR=build CFGDIR=cfg CXXFLAGS="-O1 -DNDEBUG" -j2 && cd ..
-  - pip3 install --user meson
-  - pip3 install --user conan
-  - conan remote add bincrafters https://api.bintray.com/conan/bincrafters/public-conan
-  - export PATH="`pwd`/build-ninja:${PATH}"
-  - export PATH="`pwd`/cppcheck-1.81/:${PATH}"
-  - export PATH="~/.local/bin:${PATH}"
-
-before_script:
-  - export CXX=$COMPILER
-  - mkdir debug && cd debug
-  - conan install ..
-  - conan build ..
-  - meson configure -Dbuildtype=debug
-  - meson configure -Dwarning_level=3
-  - if [[ "${COMPILER}" == clang++* ]]; then meson configure -Db_sanitize="address,undefined"; fi;
-  - meson configure -Db_coverage=true
-  - ninja
-  - cd ..
-  - mkdir release-sanitize && cd release-sanitize
-  - conan install ..
-  - conan build ..
-  - meson configure -Dbuildtype=release
-  - meson configure -Dwarning_level=3
-  - if [[ "${COMPILER}" == clang* ]]; then meson configure -Db_sanitize="address,undefined"; fi;
-  - ninja
-  - cd ..
-  - mkdir release && cd release
-  - conan install ..
-  - conan build ..
-  - meson configure -Dbuildtype=release
-  - meson configure -Dwarning_level=3
-  - ninja
-  - cd ..
-
-script:
-  - cd release && ./test/mainTest && ./test/colorTest && ./test/envTermMissing
-  - ninja cppcheck && cd ..
-
-after_success:
-  - cd debug && ./test/mainTest && ./test/colorTest && ./test/envTermMissing
-  - bash <(curl -s https://codecov.io/bash)
-  - cd .. && cd release-sanitize && ./test/mainTest && ./test/colorTest && ./test/envTermMissing
-
-notifications:
-  email: false
diff --git a/packages/rang/CMakeLists.txt b/packages/rang/CMakeLists.txt
index bca467e9da651fe940eced7991f10442c343fb9a..680f96fe1be2ee95891af9ce33a5c255836d9093 100644
--- a/packages/rang/CMakeLists.txt
+++ b/packages/rang/CMakeLists.txt
@@ -1,5 +1,86 @@
-cmake_minimum_required (VERSION 2.8)
-project (rang)
+cmake_minimum_required(VERSION 3.1)
 
-include_directories(include)
-install(FILES ${PROJECT_SOURCE_DIR}/include/rang.hpp DESTINATION include)
+project(rang 
+    VERSION 3.2.0
+    LANGUAGES CXX)
+
+set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+
+include(cmake/CMakeUtilities.cmake)
+include(GNUInstallDirs)
+set_verbose(RANG_INC_DIR ${CMAKE_INSTALL_INCLUDEDIR} CACHE STRING
+    "Installation directory for include files, a relative path that "
+    "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute path.")
+
+set(RANG_HEADERS include/rang.hpp)
+
+add_library(${PROJECT_NAME} INTERFACE)
+
+target_include_directories(rang INTERFACE
+  $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>
+  $<INSTALL_INTERFACE:${RANG_INC_DIR}>
+  )
+
+include(CMakePackageConfigHelpers)
+
+set_verbose(RANG_CMAKE_DIR ${CMAKE_INSTALL_LIBDIR}/cmake/rang CACHE STRING
+  "Installation directory for cmake files, a relative path that "
+  "will be joined with ${CMAKE_INSTALL_PREFIX} or an absolute "
+  "path.")
+set(version_config ${PROJECT_BINARY_DIR}/rang-config-version.cmake)
+set(project_config ${PROJECT_BINARY_DIR}/rang-config.cmake)
+set(pkgconfig ${PROJECT_BINARY_DIR}/rang.pc)
+set(targets_export_name rang-targets)
+
+set_verbose(RANG_PKGCONFIG_DIR ${CMAKE_INSTALL_LIBDIR}/pkgconfig CACHE PATH
+  "Installation directory for pkgconfig (.pc) files, a relative "
+  "path that will be joined with ${CMAKE_INSTALL_PREFIX} or an "
+  "absolute path.")
+
+write_basic_package_version_file(
+    ${version_config}
+    VERSION ${PROJECT_VERSION}
+    COMPATIBILITY AnyNewerVersion
+    )
+
+join_paths(includedir_for_pc_file "\${prefix}" "${RANG_INC_DIR}")
+
+# Configure the PkgConfig
+configure_file(
+    "${PROJECT_SOURCE_DIR}/cmake/rang.pc.in"
+    "${pkgconfig}"
+    @ONLY
+    )
+
+# Configuring the CMake Installer Helper
+configure_package_config_file(
+    ${PROJECT_SOURCE_DIR}/cmake/rang-config.cmake.in
+    ${project_config}
+    INSTALL_DESTINATION ${RANG_CMAKE_DIR}
+    )
+
+set(INSTALL_TARGETS rang)
+
+# Install the library and headers.
+install(TARGETS ${INSTALL_TARGETS} EXPORT ${targets_export_name}
+      RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+
+# Use a namespace because CMake provides better diagnostics for namespaced
+# imported targets.
+export(TARGETS ${INSTALL_TARGETS} NAMESPACE rang::
+    FILE ${PROJECT_BINARY_DIR}/${targets_export_name}.cmake
+    )
+
+# Install version, config and target files.
+install(
+    FILES ${project_config} ${version_config}
+    DESTINATION ${RANG_CMAKE_DIR}
+    )
+
+install(EXPORT ${targets_export_name}
+    DESTINATION ${RANG_CMAKE_DIR}
+    NAMESPACE rang::)
+
+install(FILES ${RANG_HEADERS} DESTINATION "${RANG_INC_DIR}")
+install(FILES "${pkgconfig}" DESTINATION "${RANG_PKGCONFIG_DIR}")
diff --git a/packages/rang/cmake/CMakeUtilities.cmake b/packages/rang/cmake/CMakeUtilities.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..3d4a10b316cf72f371a2f88a4e1b9209f014d57c
--- /dev/null
+++ b/packages/rang/cmake/CMakeUtilities.cmake
@@ -0,0 +1,60 @@
+# This module provides function for joining paths
+# known from from most languages
+#
+# Original license:
+# SPDX-License-Identifier: (MIT OR CC0-1.0)
+# Explicit permission given to distribute this module under
+# the terms of the project as described in /LICENSE.rst.
+# Copyright 2020 Jan Tojnar
+# https://github.com/jtojnar/cmake-snips
+#
+# Modelled after Python’s os.path.join
+# https://docs.python.org/3.7/library/os.path.html#os.path.join
+# Windows not supported
+function(join_paths joined_path first_path_segment)
+    set(temp_path "${first_path_segment}")
+    foreach(current_segment IN LISTS ARGN)
+        if(NOT ("${current_segment}" STREQUAL ""))
+            if(IS_ABSOLUTE "${current_segment}")
+                set(temp_path "${current_segment}")
+            else()
+                set(temp_path "${temp_path}/${current_segment}")
+            endif()
+        endif()
+    endforeach()
+    set(${joined_path} "${temp_path}" PARENT_SCOPE)
+endfunction()
+
+# Joins arguments and places the results in ${result_var}.
+function(join result_var)
+  set(result "")
+  foreach (arg ${ARGN})
+    set(result "${result}${arg}")
+  endforeach ()
+  set(${result_var} "${result}" PARENT_SCOPE)
+endfunction()
+
+function(enable_module target)
+  if (MSVC)
+    set(BMI ${CMAKE_CURRENT_BINARY_DIR}/${target}.ifc)
+    target_compile_options(${target}
+      PRIVATE /interface /ifcOutput ${BMI}
+      INTERFACE /reference fmt=${BMI})
+  endif ()
+  set_target_properties(${target} PROPERTIES ADDITIONAL_CLEAN_FILES ${BMI})
+  set_source_files_properties(${BMI} PROPERTIES GENERATED ON)
+endfunction()
+
+function(set_verbose)
+  # cmake_parse_arguments is broken in CMake 3.4 (cannot parse CACHE) so use
+  # list instead.
+  list(GET ARGN 0 var)
+  list(REMOVE_AT ARGN 0)
+  list(GET ARGN 0 val)
+  list(REMOVE_AT ARGN 0)
+  list(REMOVE_AT ARGN 0)
+  list(GET ARGN 0 type)
+  list(REMOVE_AT ARGN 0)
+  join(doc ${ARGN})
+  set(${var} ${val} CACHE ${type} ${doc})
+endfunction()
\ No newline at end of file
diff --git a/packages/rang/cmake/rang-config.cmake.in b/packages/rang/cmake/rang-config.cmake.in
new file mode 100644
index 0000000000000000000000000000000000000000..ea9c953fdc9d937b1a5005557e614f46ab844d6e
--- /dev/null
+++ b/packages/rang/cmake/rang-config.cmake.in
@@ -0,0 +1,4 @@
+@PACKAGE_INIT@
+
+include(${CMAKE_CURRENT_LIST_DIR}/@targets_export_name@.cmake)
+check_required_components(rang)
\ No newline at end of file
diff --git a/packages/rang/cmake/rang.pc.in b/packages/rang/cmake/rang.pc.in
new file mode 100644
index 0000000000000000000000000000000000000000..5e98792af5b094a580ae87269bf2ef0de587c93b
--- /dev/null
+++ b/packages/rang/cmake/rang.pc.in
@@ -0,0 +1,8 @@
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+includedir=@includedir_for_pc_file@
+
+Name: rang
+Description: A Minimal, Header only Modern c++ library for terminal goodies
+Version: @PROJECT_VERSION@
+Cflags: -I${includedir}
diff --git a/packages/rang/conanfile.py b/packages/rang/conanfile.py
index 9ec7632d3f4197bf1ed854f6242ec5b435bb75f1..79c2b8c3162e5ef5926b041329d53981ad53610c 100644
--- a/packages/rang/conanfile.py
+++ b/packages/rang/conanfile.py
@@ -3,7 +3,7 @@ from conans import ConanFile, Meson
 
 class RangConan(ConanFile):
     name = "rang"
-    version = "3.1.0"
+    version = "3.2.0"
     license = "The Unlicense"
     url = "https://github.com/agauniyal/rang"
     description = "A Minimal, Header only Modern c++ library for colors in your terminal"
diff --git a/packages/rang/meson.build b/packages/rang/meson.build
index 1af4a10444920899249377cf83dee45b3c53c9eb..0a7491a81949d3d19119e81aa67417ebaa7363fd 100644
--- a/packages/rang/meson.build
+++ b/packages/rang/meson.build
@@ -1,4 +1,4 @@
-project('rang', 'cpp', version : '3.1.0',
+project('rang', 'cpp', version : '3.2.0',
 		default_options : ['cpp_std=c++11'])
 
 inc = include_directories('include')
diff --git a/packages/rang/test/CMakeLists.txt b/packages/rang/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..39ced202ede13ddb8eb33aef889d021ecfd0d646
--- /dev/null
+++ b/packages/rang/test/CMakeLists.txt
@@ -0,0 +1,32 @@
+cmake_minimum_required(VERSION 2.8.12)
+
+project(rang-test)
+
+set(CMAKE_CXX_STANDARD          11 )
+set(CMAKE_CXX_STANDARD_REQUIRED ON )
+set(CMAKE_CXX_EXTENSIONS        OFF)
+
+function(rang_add_test file_name)
+    add_executable("${file_name}" "${file_name}.cpp")
+    target_link_libraries("${file_name}" rang)
+endfunction()
+
+# simple tests #################################################################
+
+rang_add_test(colorTest)
+rang_add_test(envTermMissing)
+
+# test that uses doctest #######################################################
+
+set(doctest_DIR "" CACHE PATH "Directory containing doctestConfig.cmake")
+find_package(doctest)
+
+if (${doctest_FOUND} EQUAL 1)
+    add_executable(all_rang_tests "test.cpp")
+    target_link_libraries(all_rang_tests rang doctest::doctest)
+
+    enable_testing()
+
+    # cd build_dir && ctest --test-command all_tests
+    add_test(NAME all_tests COMMAND "$<TARGET_FILE:all_rang_tests>")
+endif()
diff --git a/packages/rang/test_package/meson.build b/packages/rang/test_package/meson.build
index 7930ce6a9bb0d5cae49361fbae9f6f5054199b40..638dc5cc2d6daefb456c3c308e320a76ef3f9730 100644
--- a/packages/rang/test_package/meson.build
+++ b/packages/rang/test_package/meson.build
@@ -1,4 +1,4 @@
-project('rang', 'cpp', version : '3.1.0',
+project('rang', 'cpp', version : '3.2.0',
 		default_options : ['cpp_std=c++11'])
 
 inc = include_directories('include')
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 16904a7be0cddc9aa54206638ab4b5c759cd0de8..54cae625bc7c899c433bfdf4021ef6242dd5aaf4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -12,6 +12,9 @@ add_subdirectory(language)
 # Pugs algebra
 add_subdirectory(algebra)
 
+# Pugs analysis
+add_subdirectory(analysis)
+
 # Pugs mesh
 add_subdirectory(mesh)
 
diff --git a/src/algebra/EigenvalueSolver.cpp b/src/algebra/EigenvalueSolver.cpp
index 6125e67e6c5fad94a122028ce78bd78ef7b12e6c..c71be58ddcb1c37498a172f30a1eb39b89f43aab 100644
--- a/src/algebra/EigenvalueSolver.cpp
+++ b/src/algebra/EigenvalueSolver.cpp
@@ -32,6 +32,7 @@ struct EigenvalueSolver::Internals
   computeAllEigenvaluesOfSymmetricMatrixInInterval(EPS& eps, const PetscReal left_bound, const PetscReal right_bound)
   {
     Assert(left_bound < right_bound);
+    EPSSetType(eps, EPSKRYLOVSCHUR);
     EPSSetWhichEigenpairs(eps, EPS_ALL);
     EPSSetInterval(eps, left_bound - 0.01 * std::abs(left_bound), right_bound + 0.01 * std::abs(right_bound));
 
@@ -40,7 +41,11 @@ struct EigenvalueSolver::Internals
     STSetType(st, STSINVERT);
 
     KSP ksp;
+#if (SLEPC_VERSION_MAJOR >= 3) && (SLEPC_VERSION_MINOR >= 15)
+    EPSKrylovSchurGetKSP(eps, &ksp);
+#else
     STGetKSP(st, &ksp);
+#endif
     KSPSetType(ksp, KSPPREONLY);
 
     PC pc;
diff --git a/src/algebra/TinyMatrix.hpp b/src/algebra/TinyMatrix.hpp
index 0d3b9c0e5fef46f85fa576b68f940708c8ed529d..f4c66232816b33d8ee7e8ac897436507181d5644 100644
--- a/src/algebra/TinyMatrix.hpp
+++ b/src/algebra/TinyMatrix.hpp
@@ -14,6 +14,10 @@ template <size_t M, size_t N = M, typename T = double>
 class [[nodiscard]] TinyMatrix
 {
  public:
+  inline static constexpr size_t Dimension       = M * N;
+  inline static constexpr size_t NumberOfRows    = M;
+  inline static constexpr size_t NumberOfColumns = N;
+
   using data_type = T;
 
  private:
@@ -278,7 +282,7 @@ class [[nodiscard]] TinyMatrix
   constexpr TinyMatrix& operator=(TinyMatrix&& A) noexcept = default;
 
   template <typename... Args>
-  PUGS_INLINE constexpr TinyMatrix(const T& t, Args&&... args) noexcept
+  PUGS_INLINE explicit constexpr TinyMatrix(const T& t, Args&&... args) noexcept
   {
     static_assert(sizeof...(args) == M * N - 1, "wrong number of parameters");
     this->_unpackVariadicInput(t, std::forward<Args>(args)...);
@@ -423,24 +427,24 @@ getMinor(const TinyMatrix<M, N, T>& A, size_t I, size_t J)
 {
   static_assert(M >= 2 and N >= 2, "minor calculation requires at least 2x2 matrices");
   Assert((I < M) and (J < N));
-  TinyMatrix<M - 1, N - 1, T> minor;
+  TinyMatrix<M - 1, N - 1, T> m;
   for (size_t i = 0; i < I; ++i) {
     for (size_t j = 0; j < J; ++j) {
-      minor(i, j) = A(i, j);
+      m(i, j) = A(i, j);
     }
     for (size_t j = J + 1; j < N; ++j) {
-      minor(i, j - 1) = A(i, j);
+      m(i, j - 1) = A(i, j);
     }
   }
   for (size_t i = I + 1; i < M; ++i) {
     for (size_t j = 0; j < J; ++j) {
-      minor(i - 1, j) = A(i, j);
+      m(i - 1, j) = A(i, j);
     }
     for (size_t j = J + 1; j < N; ++j) {
-      minor(i - 1, j - 1) = A(i, j);
+      m(i - 1, j - 1) = A(i, j);
     }
   }
-  return minor;
+  return m;
 }
 
 template <size_t N, typename T>
diff --git a/src/algebra/TinyVector.hpp b/src/algebra/TinyVector.hpp
index 382741093f351cea80ff1e261399df9583dc4d0c..3ca3d2a38550b5a31bb16843d25a35cebdb10fd0 100644
--- a/src/algebra/TinyVector.hpp
+++ b/src/algebra/TinyVector.hpp
@@ -15,7 +15,8 @@ class [[nodiscard]] TinyVector
 {
  public:
   inline static constexpr size_t Dimension = N;
-  using data_type                          = T;
+
+  using data_type = T;
 
  private:
   T m_values[N];
@@ -202,7 +203,7 @@ class [[nodiscard]] TinyVector
   constexpr TinyVector& operator=(TinyVector&& v) noexcept = default;
 
   template <typename... Args>
-  PUGS_INLINE constexpr TinyVector(const T& t, Args&&... args) noexcept
+  PUGS_INLINE explicit constexpr TinyVector(const T& t, Args&&... args) noexcept
   {
     static_assert(sizeof...(args) == N - 1, "wrong number of parameters");
     this->_unpackVariadicInput(t, std::forward<Args>(args)...);
diff --git a/src/analysis/CMakeLists.txt b/src/analysis/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3cbded76d7fadead5565ba862289aec3dcdbaad8
--- /dev/null
+++ b/src/analysis/CMakeLists.txt
@@ -0,0 +1,13 @@
+# ------------------- Source files --------------------
+
+add_library(
+  PugsAnalysis
+  CubeGaussQuadrature.cpp
+  QuadratureManager.cpp
+  PrismGaussQuadrature.cpp
+  PyramidGaussQuadrature.cpp
+  SquareGaussQuadrature.cpp
+  TensorialGaussLegendreQuadrature.cpp
+  TensorialGaussLobattoQuadrature.cpp
+  TetrahedronGaussQuadrature.cpp
+  TriangleGaussQuadrature.cpp)
diff --git a/src/analysis/CubeGaussQuadrature.cpp b/src/analysis/CubeGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8f49fccbeb62b5e4e06da65fbccc0050bb0a0ce8
--- /dev/null
+++ b/src/analysis/CubeGaussQuadrature.cpp
@@ -0,0 +1,502 @@
+#include <analysis/CubeGaussQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+void
+CubeGaussQuadrature::_buildPointAndWeightLists(const size_t degree)
+{
+  using R3 = TinyVector<3>;
+
+  struct Descriptor
+  {
+    int id;
+    double weight;
+    std::vector<double> lambda_list;
+  };
+
+  auto fill_quadrature_points = [](auto descriptor_list, auto& point_list, auto& weight_list) {
+    Assert(point_list.size() == weight_list.size());
+
+    size_t k = 0;
+    for (size_t i = 0; i < descriptor_list.size(); ++i) {
+      const auto [id, unit_weight, position_list] = descriptor_list[i];
+
+      const double w = 8. * unit_weight;
+
+      switch (id) {
+      case 1: {
+        Assert(position_list.size() == 0);
+        point_list[k]  = R3{0, 0, 0};
+        weight_list[k] = w;
+        ++k;
+        break;
+      }
+      case 2: {
+        Assert(position_list.size() == 1);
+        const double a = position_list[0];
+
+        point_list[k + 0] = R3{+a, 0, 0};
+        point_list[k + 1] = R3{-a, 0, 0};
+        point_list[k + 2] = R3{0, +a, 0};
+        point_list[k + 3] = R3{0, -a, 0};
+        point_list[k + 4] = R3{0, 0, +a};
+        point_list[k + 5] = R3{0, 0, -a};
+
+        for (size_t l = 0; l < 6; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 6;
+        break;
+      }
+      case 3: {
+        Assert(position_list.size() == 1);
+        const double a = position_list[0];
+
+        point_list[k + 0] = R3{+a, +a, +a};
+        point_list[k + 1] = R3{+a, +a, -a};
+        point_list[k + 2] = R3{+a, -a, +a};
+        point_list[k + 3] = R3{+a, -a, -a};
+        point_list[k + 4] = R3{-a, +a, +a};
+        point_list[k + 5] = R3{-a, +a, -a};
+        point_list[k + 6] = R3{-a, -a, +a};
+        point_list[k + 7] = R3{-a, -a, -a};
+
+        for (size_t l = 0; l < 8; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 8;
+        break;
+      }
+      case 4: {
+        Assert(position_list.size() == 1);
+        const double a = position_list[0];
+
+        point_list[k + 0]  = R3{+a, +a, 0};
+        point_list[k + 1]  = R3{+a, -a, 0};
+        point_list[k + 2]  = R3{-a, +a, 0};
+        point_list[k + 3]  = R3{-a, -a, 0};
+        point_list[k + 4]  = R3{+a, 0, +a};
+        point_list[k + 5]  = R3{+a, 0, -a};
+        point_list[k + 6]  = R3{-a, 0, +a};
+        point_list[k + 7]  = R3{-a, 0, -a};
+        point_list[k + 8]  = R3{0, +a, +a};
+        point_list[k + 9]  = R3{0, +a, -a};
+        point_list[k + 10] = R3{0, -a, +a};
+        point_list[k + 11] = R3{0, -a, -a};
+
+        for (size_t l = 0; l < 12; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 12;
+        break;
+      }
+      case 5: {
+        Assert(position_list.size() == 2);
+        const double a = position_list[0];
+        const double b = position_list[1];
+
+        point_list[k + 0]  = R3{+a, +b, 0};
+        point_list[k + 1]  = R3{+a, -b, 0};
+        point_list[k + 2]  = R3{-a, +b, 0};
+        point_list[k + 3]  = R3{-a, -b, 0};
+        point_list[k + 4]  = R3{+b, +a, 0};
+        point_list[k + 5]  = R3{-b, +a, 0};
+        point_list[k + 6]  = R3{+b, -a, 0};
+        point_list[k + 7]  = R3{-b, -a, 0};
+        point_list[k + 8]  = R3{+a, 0, +b};
+        point_list[k + 9]  = R3{+a, 0, -b};
+        point_list[k + 10] = R3{-a, 0, +b};
+        point_list[k + 11] = R3{-a, 0, -b};
+        point_list[k + 12] = R3{+b, 0, +a};
+        point_list[k + 13] = R3{-b, 0, +a};
+        point_list[k + 14] = R3{+b, 0, -a};
+        point_list[k + 15] = R3{-b, 0, -a};
+        point_list[k + 16] = R3{0, +a, +b};
+        point_list[k + 17] = R3{0, +a, -b};
+        point_list[k + 18] = R3{0, -a, +b};
+        point_list[k + 19] = R3{0, -a, -b};
+        point_list[k + 20] = R3{0, +b, +a};
+        point_list[k + 21] = R3{0, -b, +a};
+        point_list[k + 22] = R3{0, +b, -a};
+        point_list[k + 23] = R3{0, -b, -a};
+
+        for (size_t l = 0; l < 24; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 24;
+        break;
+      }
+      case 6: {
+        Assert(position_list.size() == 2);
+        const double a = position_list[0];
+        const double b = position_list[1];
+
+        point_list[k + 0]  = R3{+a, +a, +b};
+        point_list[k + 1]  = R3{+a, +a, -b};
+        point_list[k + 2]  = R3{+a, -a, +b};
+        point_list[k + 3]  = R3{+a, -a, -b};
+        point_list[k + 4]  = R3{-a, +a, +b};
+        point_list[k + 5]  = R3{-a, +a, -b};
+        point_list[k + 6]  = R3{-a, -a, +b};
+        point_list[k + 7]  = R3{-a, -a, -b};
+        point_list[k + 8]  = R3{+a, +b, +a};
+        point_list[k + 9]  = R3{+a, -b, +a};
+        point_list[k + 10] = R3{+a, +b, -a};
+        point_list[k + 11] = R3{+a, -b, -a};
+        point_list[k + 12] = R3{-a, +b, +a};
+        point_list[k + 13] = R3{-a, -b, +a};
+        point_list[k + 14] = R3{-a, +b, -a};
+        point_list[k + 15] = R3{-a, -b, -a};
+        point_list[k + 16] = R3{+b, +a, +a};
+        point_list[k + 17] = R3{-b, +a, +a};
+        point_list[k + 18] = R3{+b, +a, -a};
+        point_list[k + 19] = R3{-b, +a, -a};
+        point_list[k + 20] = R3{+b, -a, +a};
+        point_list[k + 21] = R3{-b, -a, +a};
+        point_list[k + 22] = R3{+b, -a, -a};
+        point_list[k + 23] = R3{-b, -a, -a};
+
+        for (size_t l = 0; l < 24; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 24;
+        break;
+      }
+      case 7: {
+        Assert(position_list.size() == 3);
+        const double a = position_list[0];
+        const double b = position_list[1];
+        const double c = position_list[2];
+
+        point_list[k + 0]  = R3{+a, +b, +c};
+        point_list[k + 1]  = R3{+a, +b, -c};
+        point_list[k + 2]  = R3{+a, -b, +c};
+        point_list[k + 3]  = R3{+a, -b, -c};
+        point_list[k + 4]  = R3{-a, +b, +c};
+        point_list[k + 5]  = R3{-a, +b, -c};
+        point_list[k + 6]  = R3{-a, -b, +c};
+        point_list[k + 7]  = R3{-a, -b, -c};
+        point_list[k + 8]  = R3{+a, +c, +b};
+        point_list[k + 9]  = R3{+a, -c, +b};
+        point_list[k + 10] = R3{+a, +c, -b};
+        point_list[k + 11] = R3{+a, -c, -b};
+        point_list[k + 12] = R3{-a, +c, +b};
+        point_list[k + 13] = R3{-a, -c, +b};
+        point_list[k + 14] = R3{-a, +c, -b};
+        point_list[k + 15] = R3{-a, -c, -b};
+        point_list[k + 16] = R3{+b, +a, +c};
+        point_list[k + 17] = R3{+b, +a, -c};
+        point_list[k + 18] = R3{-b, +a, +c};
+        point_list[k + 19] = R3{-b, +a, -c};
+        point_list[k + 20] = R3{+b, -a, +c};
+        point_list[k + 21] = R3{+b, -a, -c};
+        point_list[k + 22] = R3{-b, -a, +c};
+        point_list[k + 23] = R3{-b, -a, -c};
+        point_list[k + 24] = R3{+b, +c, +a};
+        point_list[k + 25] = R3{+b, -c, +a};
+        point_list[k + 26] = R3{-b, +c, +a};
+        point_list[k + 27] = R3{-b, -c, +a};
+        point_list[k + 28] = R3{+b, +c, -a};
+        point_list[k + 29] = R3{+b, -c, -a};
+        point_list[k + 30] = R3{-b, +c, -a};
+        point_list[k + 31] = R3{-b, -c, -a};
+        point_list[k + 32] = R3{+c, +a, +b};
+        point_list[k + 33] = R3{-c, +a, +b};
+        point_list[k + 34] = R3{+c, +a, -b};
+        point_list[k + 35] = R3{-c, +a, -b};
+        point_list[k + 36] = R3{+c, -a, +b};
+        point_list[k + 37] = R3{-c, -a, +b};
+        point_list[k + 38] = R3{+c, -a, -b};
+        point_list[k + 39] = R3{-c, -a, -b};
+        point_list[k + 40] = R3{+c, +b, +a};
+        point_list[k + 41] = R3{-c, +b, +a};
+        point_list[k + 42] = R3{+c, -b, +a};
+        point_list[k + 43] = R3{-c, -b, +a};
+        point_list[k + 44] = R3{+c, +b, -a};
+        point_list[k + 45] = R3{-c, +b, -a};
+        point_list[k + 46] = R3{+c, -b, -a};
+        point_list[k + 47] = R3{-c, -b, -a};
+
+        for (size_t l = 0; l < 48; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 48;
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("invalid quadrature id");
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+  };
+
+  switch (degree) {
+  case 0:
+  case 1: {
+    constexpr size_t nb_points = 1;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.000000000000000e+00, {}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 2:
+  case 3: {
+    constexpr size_t nb_points = 6;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.666666666666667e-01, {+1.000000000000000e+00}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 4:
+  case 5: {
+    constexpr size_t nb_points = 14;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.108033240997230e-01, {-7.958224257542215e-01}},
+       Descriptor{3, 4.189750692520776e-02, {+7.587869106393281e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 6:
+  case 7: {
+    constexpr size_t nb_points = 34;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 3.400458474980031e-02, {-9.388053721060176e-01}},
+       Descriptor{3, 2.529672440135186e-02, {+7.463336100128160e-01}},
+       Descriptor{3, 5.417063533485642e-02, {+4.101308983320144e-01}},
+       Descriptor{4, 1.335280113429432e-02, {+9.064606901228371e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 8:
+  case 9: {
+    constexpr size_t nb_points = 58;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 5.415937446870682e-02, {-6.136814695917090e-01}},
+       Descriptor{3, 6.268599412418628e-03, {+8.700997846619759e-01}},
+       Descriptor{3, 2.485747976800294e-02, {+5.641108070200300e-01}},
+       Descriptor{4, 1.147372576702221e-02, {+8.776871232576783e-01}},
+       Descriptor{6, 1.201460043917167e-02, {+4.322679026308622e-01, +9.385304218646717e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 10:
+  case 11: {
+    constexpr size_t nb_points = 90;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.961692452402056e-02, {+7.221330388744185e-01}},
+       Descriptor{3, 7.751183787523073e-03, {+8.094882019630989e-01}},
+       Descriptor{3, 2.222100810905048e-02, {-5.336540088804971e-01}},
+       Descriptor{3, 1.698870214455200e-02, {+2.807725866512744e-01}},
+       Descriptor{4, 1.602728177693560e-02, {+8.039334672152845e-01}},
+       Descriptor{6, 1.618821190032323e-03, {+9.800994910090715e-01, -5.307838311938264e-01}},
+       Descriptor{6, 8.976342110119554e-03, {+4.056859801950964e-01, +9.545832189295661e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 12:
+  case 13: {
+    constexpr size_t nb_points = 154;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.739128248747042e-02, {-6.344099893707986e-01}},
+       Descriptor{3, 1.838657780061070e-03, {+9.078540479628353e-01}},
+       Descriptor{3, 1.129102483142921e-02, {-2.362052584516462e-01}},
+       Descriptor{4, 5.975759635289482e-03, {+7.375746343132366e-01}},
+       Descriptor{5, 8.691231524417118e-03, {+4.607161476964481e-01, +9.333452030108366e-01}},
+       Descriptor{6, 1.061455871034256e-02, {+3.733841515799997e-01, +6.642163895931538e-01}},
+       Descriptor{6, 1.690358916666084e-03, {+9.495110814642814e-01, +2.576763878569212e-01}},
+       Descriptor{6, 2.284240552839863e-03, {+6.656694827601780e-01, +9.948700428018972e-01}},
+       Descriptor{6, 6.674015652391932e-03, {-8.051009105032320e-01, -4.521405059548300e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 14:
+  case 15: {
+    constexpr size_t nb_points = 256;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 3.340305010694517e-03, {+9.239155149504112e-01}},
+       Descriptor{2, 5.018552337877279e-03, {-2.173948613666498e-01}},
+       Descriptor{3, 9.082758817224784e-03, {+3.384731869494254e-01}},
+       Descriptor{3, 2.936408738387597e-03, {+7.930891310393379e-01}},
+       Descriptor{4, 9.318238647048784e-03, {+6.965519027266717e-01}},
+       Descriptor{5, 6.990786235751096e-03, {+2.642667592628472e-01, +5.849899486873243e-01}},
+       Descriptor{5, 2.841005747490632e-03, {+5.529505913370675e-01, +9.828048727801773e-01}},
+       Descriptor{6, 5.309574072330840e-03, {+2.720325890860906e-01, +8.780894076733160e-01}},
+       Descriptor{6, 3.570894536567292e-03, {+6.186478600855522e-01, +9.399813353075436e-01}},
+       Descriptor{6, 3.505872179585672e-03, {+8.631544625530380e-01, +3.042225951752605e-01}},
+       Descriptor{6, 6.665199051138311e-03, {+4.616057726468051e-01, +7.371361183496182e-01}},
+       Descriptor{6, 7.781518386120085e-04, {+9.603141805416257e-01, +7.670146080341008e-01}},
+       Descriptor{7, 6.249800796596737e-04, {+2.802518397917547e-01, +8.885938329579797e-01, +9.942601945848643e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 16:
+  case 17: {
+    constexpr size_t nb_points = 346;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 9.614592128929721e-03, {+4.853150302302675e-01}},
+       Descriptor{3, 4.393827602159037e-03, {+7.406788158740985e-01}},
+       Descriptor{3, 4.964725194523337e-05, {+9.999995835822517e-01}},
+       Descriptor{4, 2.506284464832531e-03, {+7.546268052131762e-01}},
+       Descriptor{4, 6.133232482070283e-03, {+5.496818141686911e-01}},
+       Descriptor{4, 3.052513689971173e-04, {+9.996685037422811e-01}},
+       Descriptor{5, 2.825437733033075e-03, {+6.157290991684734e-01, +9.530419407049950e-01}},
+       Descriptor{5, 4.817704598799924e-03, {+2.756812731993550e-01, +8.171240681070916e-01}},
+       Descriptor{6, 2.283142338953617e-03, {+2.553429540372821e-01, +9.697740446619386e-01}},
+       Descriptor{6, 1.177437487278679e-03, {+9.352363848980443e-01, +7.467233074183380e-01}},
+       Descriptor{6, 6.184060750627916e-03, {+3.430591461388653e-01, +6.186639578085585e-01}},
+       Descriptor{6, 2.674655837593652e-03, {+2.555841601150721e-01, -1.540702494043778e-01}},
+       Descriptor{6, 3.419177677578799e-03, {+5.416211416468879e-01, +8.956569517854811e-01}},
+       Descriptor{6, 2.334617556615003e-03, {+8.874561091440486e-01, +2.414649778934788e-01}},
+       Descriptor{7, 1.052790164887146e-03, {+8.014733742030056e-01, +9.887847181449660e-01, +4.683593080344387e-01}},
+       Descriptor{7, 2.743830940763945e-03, {+6.099140660050306e-01, +7.868230999431114e-01, +3.205108203811766e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 18:
+  case 19: {
+    constexpr size_t nb_points = 454;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.057371005731085e-03, {+2.357950388975374e-01}},
+       Descriptor{3, 9.660877915728369e-04, {+8.884950400440639e-01}},
+       Descriptor{3, 1.635901446799172e-03, {+6.691818469930052e-01}},
+       Descriptor{5, 8.499912813105116e-04, {+7.123100397018546e-01, +9.982977792850621e-01}},
+       Descriptor{5, 3.595003721736422e-03, {+3.997141162742279e-01, +1.720241294475643e-01}},
+       Descriptor{5, 3.836063429622649e-03, {+8.765805264721680e-01, -4.319090088935915e-01}},
+       Descriptor{6, 5.044128485798918e-03, {-3.101731974045544e-01, +5.411043641351523e-01}},
+       Descriptor{6, 1.951830532879285e-04, {+9.841852549757261e-01, +8.206649948892372e-01}},
+       Descriptor{6, 1.758454840661237e-03, {+8.443872335315854e-01, -1.473766326322086e-01}},
+       Descriptor{6, 3.887320262932200e-03, {+4.875993998852213e-01, +7.674914745033637e-01}},
+       Descriptor{6, 1.887401083675721e-03, {+7.641318497967444e-01, +3.863910365012072e-01}},
+       Descriptor{6, 1.699165183521457e-03, {+7.112537388224306e-01, +9.032012949929131e-01}},
+       Descriptor{6, 7.588378288377641e-04, {+9.551489495939087e-01, +2.226160644570115e-01}},
+       Descriptor{6, 1.455762954103218e-03, {+1.696919803869971e-01, +9.651628675592381e-01}},
+       Descriptor{6, 4.007905075757146e-03, {+6.085335258834057e-01, +9.412726803541549e-02}},
+       Descriptor{6, 1.399186075584291e-03, {+4.265517787687217e-01, +9.784781131096041e-01}},
+       Descriptor{6, 4.099280788926685e-03, {+1.836920702608942e-01, +7.457754226757105e-01}},
+       Descriptor{7, 1.951861257384991e-03, {+6.535330886596633e-01, +9.133523126050157e-01, +2.888629020786040e-01}},
+       Descriptor{7, 9.537937942918817e-04, {+8.475635090984786e-01, +9.748482139426561e-01, +5.534277540813458e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 20:
+  case 21: {
+    constexpr size_t nb_points = 580;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 3.007917561622623e-03, {-6.174957125232848e-01}},
+       Descriptor{2, 6.157551855986320e-03, {-2.913675401437903e-01}},
+       Descriptor{3, 7.451864938264940e-04, {+8.622583182294242e-01}},
+       Descriptor{3, 3.473649875824585e-03, {+5.666798116095041e-01}},
+       Descriptor{5, 5.231065457280251e-04, {+7.962288576876380e-01, +9.999999871114571e-01}},
+       Descriptor{5, 4.721261422431887e-03, {+6.935661772320008e-01, +3.463806774517028e-01}},
+       Descriptor{5, 2.337222373763527e-03, {+8.718362020420203e-01, -6.252215000307171e-01}},
+       Descriptor{6, 3.841754898565957e-03, {-4.164545409594995e-01, +8.053481425029153e-01}},
+       Descriptor{6, 1.507767719634259e-04, {+9.738519593091541e-01, +8.745003988374457e-01}},
+       Descriptor{6, 3.214079409518292e-03, {+7.290234505608810e-01, +2.451675433295293e-01}},
+       Descriptor{6, 2.245446622613030e-03, {+6.496117102813809e-01, +8.441031264282526e-01}},
+       Descriptor{6, 1.041367673151780e-03, {+8.919707516952021e-01, +1.301935885566839e-01}},
+       Descriptor{6, 5.920000965024342e-04, {+7.833271437290619e-01, +9.532187072534166e-01}},
+       Descriptor{6, 4.100172337698149e-04, {+9.699411873288849e-01, +2.710159328732420e-01}},
+       Descriptor{6, 7.465850926621290e-04, {+1.720339772207737e-01, +9.847690037797490e-01}},
+       Descriptor{6, 3.743289683970358e-03, {+5.380985926191851e-01, -2.207631346215846e-01}},
+       Descriptor{6, 4.534357504253366e-04, {+5.536849035893876e-01, +9.989728978231427e-01}},
+       Descriptor{6, 4.182867578178122e-03, {+2.235580139510551e-01, +4.418577456047065e-01}},
+       Descriptor{6, 7.389797381822837e-04, {+8.700625815926416e-01, +4.988147208937697e-01}},
+       Descriptor{7, 1.371282553387760e-03, {+7.501439986013229e-01, +9.439853549264192e-01, +3.972114022576276e-01}},
+       Descriptor{7, 3.675472168789246e-04, {+9.025858122958752e-01, +9.846237882037482e-01, +6.307126444914427e-01}},
+       Descriptor{7, 1.258019483515844e-03, {+2.217794596478607e-01, +9.822849317288322e-02, +8.721988578583585e-01}},
+       Descriptor{7, 1.516565561694638e-03, {+9.568422563542535e-01, +2.081392891496346e-01, +4.958409266028638e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(CubeGaussQuadrature::max_degree) + " on cubes");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/analysis/CubeGaussQuadrature.hpp b/src/analysis/CubeGaussQuadrature.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bd4ed179dd3cf3745e0dc4512eaa543f1ba6e1b9
--- /dev/null
+++ b/src/analysis/CubeGaussQuadrature.hpp
@@ -0,0 +1,39 @@
+#ifndef CUBE_GAUSS_QUADRATURE_HPP
+#define CUBE_GAUSS_QUADRATURE_HPP
+
+#include <analysis/QuadratureFormula.hpp>
+
+/**
+ * Defines Cube Gauss quadrature on the reference element
+ * \f$]-1,1[^3\f$.
+ *
+ * \note formulae are provided by
+ *
+ * "Addendum to the paper 'High-order symmetric cubature rules for
+ * tetrahedra and pyramids'" Jan Jaśkowiec & N. Sukumar (2020).
+ */
+class CubeGaussQuadrature final : public QuadratureFormula<3>
+{
+ public:
+  constexpr static size_t max_degree = 21;
+
+ private:
+  void _buildPointAndWeightLists(const size_t degree);
+
+ public:
+  CubeGaussQuadrature(CubeGaussQuadrature&&)      = default;
+  CubeGaussQuadrature(const CubeGaussQuadrature&) = default;
+
+ private:
+  friend class QuadratureManager;
+
+  explicit CubeGaussQuadrature(const size_t degree) : QuadratureFormula<3>{QuadratureType::Gauss}
+  {
+    this->_buildPointAndWeightLists(degree);
+  }
+
+  CubeGaussQuadrature()  = delete;
+  ~CubeGaussQuadrature() = default;
+};
+
+#endif   // CUBE_GAUSS_QUADRATURE_HPP
diff --git a/src/analysis/GaussLegendreQuadratureDescriptor.hpp b/src/analysis/GaussLegendreQuadratureDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c37122516fa67650307aaab421120da9353f51d0
--- /dev/null
+++ b/src/analysis/GaussLegendreQuadratureDescriptor.hpp
@@ -0,0 +1,50 @@
+#ifndef GAUSS_LEGENDRE_QUADRATURE_DESCRIPTOR_HPP
+#define GAUSS_LEGENDRE_QUADRATURE_DESCRIPTOR_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+
+#include <sstream>
+
+class GaussLegendreQuadratureDescriptor final : public IQuadratureDescriptor
+{
+ private:
+  size_t m_degree;
+
+ public:
+  bool
+  isTensorial() const
+  {
+    return true;
+  }
+
+  QuadratureType
+  type() const
+  {
+    return QuadratureType::GaussLegendre;
+  }
+
+  size_t
+  degree() const
+  {
+    return m_degree;
+  }
+
+  std::string
+  name() const
+  {
+    std::ostringstream os;
+    os << ::name(this->type()) << "(" << m_degree << ")";
+    return os.str();
+  }
+
+  GaussLegendreQuadratureDescriptor(size_t degree) : m_degree{degree} {}
+  GaussLegendreQuadratureDescriptor() noexcept = delete;
+
+  GaussLegendreQuadratureDescriptor(const GaussLegendreQuadratureDescriptor&) = default;
+
+  GaussLegendreQuadratureDescriptor(GaussLegendreQuadratureDescriptor&&) noexcept = default;
+
+  virtual ~GaussLegendreQuadratureDescriptor() noexcept = default;
+};
+
+#endif   // GAUSS_LEGENDRE_QUADRATURE_DESCRIPTOR_HPP
diff --git a/src/analysis/GaussLobattoQuadratureDescriptor.hpp b/src/analysis/GaussLobattoQuadratureDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0cc3a8e6ef108af6708098c0735338621ed880a4
--- /dev/null
+++ b/src/analysis/GaussLobattoQuadratureDescriptor.hpp
@@ -0,0 +1,50 @@
+#ifndef GAUSS_LOBATTO_QUADRATURE_DESCRIPTOR_HPP
+#define GAUSS_LOBATTO_QUADRATURE_DESCRIPTOR_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+
+#include <sstream>
+
+class GaussLobattoQuadratureDescriptor final : public IQuadratureDescriptor
+{
+ private:
+  size_t m_degree;
+
+ public:
+  bool
+  isTensorial() const
+  {
+    return true;
+  }
+
+  QuadratureType
+  type() const
+  {
+    return QuadratureType::GaussLobatto;
+  }
+
+  size_t
+  degree() const
+  {
+    return m_degree;
+  }
+
+  std::string
+  name() const
+  {
+    std::ostringstream os;
+    os << ::name(this->type()) << "(" << m_degree << ")";
+    return os.str();
+  }
+
+  GaussLobattoQuadratureDescriptor(size_t degree) : m_degree{degree} {}
+  GaussLobattoQuadratureDescriptor() noexcept = delete;
+
+  GaussLobattoQuadratureDescriptor(const GaussLobattoQuadratureDescriptor&) = default;
+
+  GaussLobattoQuadratureDescriptor(GaussLobattoQuadratureDescriptor&&) noexcept = default;
+
+  virtual ~GaussLobattoQuadratureDescriptor() noexcept = default;
+};
+
+#endif   // GAUSS_LOBATTO_QUADRATURE_DESCRIPTOR_HPP
diff --git a/src/analysis/GaussQuadratureDescriptor.hpp b/src/analysis/GaussQuadratureDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..752ac40d4fa3b609f848ea82bf10b0576a3e794f
--- /dev/null
+++ b/src/analysis/GaussQuadratureDescriptor.hpp
@@ -0,0 +1,50 @@
+#ifndef GAUSS_QUADRATURE_DESCRIPTOR_HPP
+#define GAUSS_QUADRATURE_DESCRIPTOR_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+
+#include <sstream>
+
+class GaussQuadratureDescriptor final : public IQuadratureDescriptor
+{
+ private:
+  size_t m_degree;
+
+ public:
+  bool
+  isTensorial() const
+  {
+    return false;
+  }
+
+  QuadratureType
+  type() const
+  {
+    return QuadratureType::Gauss;
+  }
+
+  size_t
+  degree() const
+  {
+    return m_degree;
+  }
+
+  std::string
+  name() const
+  {
+    std::ostringstream os;
+    os << ::name(this->type()) << "(" << m_degree << ")";
+    return os.str();
+  }
+
+  GaussQuadratureDescriptor(size_t degree) : m_degree{degree} {}
+  GaussQuadratureDescriptor() noexcept = delete;
+
+  GaussQuadratureDescriptor(const GaussQuadratureDescriptor&) = default;
+
+  GaussQuadratureDescriptor(GaussQuadratureDescriptor&&) noexcept = default;
+
+  virtual ~GaussQuadratureDescriptor() noexcept = default;
+};
+
+#endif   // GAUSS_QUADRATURE_DESCRIPTOR_HPP
diff --git a/src/analysis/IQuadratureDescriptor.hpp b/src/analysis/IQuadratureDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d7e3685a8c7a8a6014ce7d8753d231183ba52aa
--- /dev/null
+++ b/src/analysis/IQuadratureDescriptor.hpp
@@ -0,0 +1,25 @@
+#ifndef I_QUADRATURE_DESCRIPTOR_HPP
+#define I_QUADRATURE_DESCRIPTOR_HPP
+
+#include <analysis/QuadratureType.hpp>
+
+#include <string>
+
+class IQuadratureDescriptor
+{
+ public:
+  virtual QuadratureType type() const = 0;
+  virtual bool isTensorial() const    = 0;
+  virtual size_t degree() const       = 0;
+  virtual std::string name() const    = 0;
+
+  IQuadratureDescriptor() noexcept = default;
+
+  IQuadratureDescriptor(const IQuadratureDescriptor&) = default;
+
+  IQuadratureDescriptor(IQuadratureDescriptor&&) noexcept = default;
+
+  virtual ~IQuadratureDescriptor() noexcept = default;
+};
+
+#endif   // I_QUADRATURE_DESCRIPTOR_HPP
diff --git a/src/analysis/PrismGaussQuadrature.cpp b/src/analysis/PrismGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..623d3f87864b08fc7b65586ebf09453568dabcf1
--- /dev/null
+++ b/src/analysis/PrismGaussQuadrature.cpp
@@ -0,0 +1,806 @@
+#include <analysis/PrismGaussQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+void
+PrismGaussQuadrature::_buildPointAndWeightLists(const size_t degree)
+{
+  using R2 = TinyVector<2>;
+  using R3 = TinyVector<3>;
+
+  struct Descriptor
+  {
+    int id;
+    double weight;
+    std::vector<double> lambda_list;
+  };
+
+  auto fill_quadrature_points = [](auto descriptor_list, auto& point_list, auto& weight_list) {
+    auto to_R3 = [](R2 X, double z) { return R3{X[0], X[1], z}; };
+
+    Assert(point_list.size() == weight_list.size());
+
+    const R2 A{0, 0};
+    const R2 B{1, 0};
+    const R2 C{0, 1};
+
+    size_t k = 0;
+    for (size_t i = 0; i < descriptor_list.size(); ++i) {
+      const auto [id, w, value_list] = descriptor_list[i];
+
+      switch (id) {
+      case 1: {
+        Assert(value_list.size() == 0);
+
+        point_list[k]  = R3{1. / 3, 1. / 3, 0.};
+        weight_list[k] = w;
+
+        k += 1;
+        break;
+      }
+      case 2: {
+        Assert(value_list.size() == 1);
+        const double c    = value_list[0];
+        point_list[k + 0] = R3{1. / 3, 1. / 3, +c};
+        point_list[k + 1] = R3{1. / 3, 1. / 3, -c};
+
+        for (size_t l = 0; l < 2; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 2;
+        break;
+      }
+      case 3: {
+        Assert(value_list.size() == 1);
+        const double l0 = value_list[0];
+        const double l1 = 1 - 2 * l0;
+
+        point_list[k + 0] = to_R3(l0 * A + l0 * B + l1 * C, 0);
+        point_list[k + 1] = to_R3(l0 * A + l1 * B + l0 * C, 0);
+        point_list[k + 2] = to_R3(l1 * A + l0 * B + l0 * C, 0);
+
+        for (size_t l = 0; l < 3; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 3;
+        break;
+      }
+      case 4: {
+        Assert(value_list.size() == 2);
+        const double l0 = value_list[0];
+        const double l1 = 1 - 2 * l0;
+        const double z  = value_list[1];
+
+        point_list[k + 0] = to_R3(l0 * A + l0 * B + l1 * C, +z);
+        point_list[k + 1] = to_R3(l0 * A + l1 * B + l0 * C, +z);
+        point_list[k + 2] = to_R3(l1 * A + l0 * B + l0 * C, +z);
+        point_list[k + 3] = to_R3(l0 * A + l0 * B + l1 * C, -z);
+        point_list[k + 4] = to_R3(l0 * A + l1 * B + l0 * C, -z);
+        point_list[k + 5] = to_R3(l1 * A + l0 * B + l0 * C, -z);
+
+        for (size_t l = 0; l < 6; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 6;
+        break;
+      }
+      case 5: {
+        Assert(value_list.size() == 2);
+        const double l0 = value_list[0];
+        const double l1 = value_list[1];
+        const double l2 = 1 - l0 - l1;
+
+        point_list[k + 0] = to_R3(l0 * A + l1 * B + l2 * C, 0);
+        point_list[k + 1] = to_R3(l0 * A + l2 * B + l1 * C, 0);
+        point_list[k + 2] = to_R3(l1 * A + l0 * B + l2 * C, 0);
+        point_list[k + 3] = to_R3(l1 * A + l2 * B + l0 * C, 0);
+        point_list[k + 4] = to_R3(l2 * A + l0 * B + l1 * C, 0);
+        point_list[k + 5] = to_R3(l2 * A + l1 * B + l0 * C, 0);
+
+        for (size_t l = 0; l < 6; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 6;
+        break;
+      }
+      case 6: {
+        Assert(value_list.size() == 3);
+        const double l0 = value_list[0];
+        const double l1 = value_list[1];
+        const double l2 = 1 - l0 - l1;
+        const double z  = value_list[2];
+
+        point_list[k + 0]  = to_R3(l0 * A + l1 * B + l2 * C, +z);
+        point_list[k + 1]  = to_R3(l0 * A + l2 * B + l1 * C, +z);
+        point_list[k + 2]  = to_R3(l1 * A + l0 * B + l2 * C, +z);
+        point_list[k + 3]  = to_R3(l1 * A + l2 * B + l0 * C, +z);
+        point_list[k + 4]  = to_R3(l2 * A + l0 * B + l1 * C, +z);
+        point_list[k + 5]  = to_R3(l2 * A + l1 * B + l0 * C, +z);
+        point_list[k + 6]  = to_R3(l0 * A + l1 * B + l2 * C, -z);
+        point_list[k + 7]  = to_R3(l0 * A + l2 * B + l1 * C, -z);
+        point_list[k + 8]  = to_R3(l1 * A + l0 * B + l2 * C, -z);
+        point_list[k + 9]  = to_R3(l1 * A + l2 * B + l0 * C, -z);
+        point_list[k + 10] = to_R3(l2 * A + l0 * B + l1 * C, -z);
+        point_list[k + 11] = to_R3(l2 * A + l1 * B + l0 * C, -z);
+
+        for (size_t l = 0; l < 12; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 12;
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("invalid quadrature id");
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+  };
+
+  switch (degree) {
+  case 0:
+  case 1: {
+    constexpr size_t nb_points = 1;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.000000000000000e+00, {}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 2: {
+    constexpr size_t nb_points = 5;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.344355869392759e-01, {-8.431650688665664e-01}},
+       Descriptor{3, 1.770429420404827e-01, {+1.046424703769979e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 3: {
+    constexpr size_t nb_points = 8;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.803034341765672e-01, {-9.614404179888022e-01}},
+       Descriptor{3, 1.134313729984015e-01, {+4.890588576053607e-01}},
+       Descriptor{3, 9.969967088388698e-02, {+7.783177802730620e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 4: {
+    constexpr size_t nb_points = 11;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.079119748155355e-01, {-8.668619740090348e-01}},
+       Descriptor{3, 1.364146126054776e-01, {+4.686558098619952e-01}},
+       Descriptor{4, 6.248870209208267e-02, {+1.007404057989106e-01, +6.756398236822597e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 5: {
+    constexpr size_t nb_points = 16;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 2.071428343483058e-01, {}}, Descriptor{3, 3.807558903099764e-02, {+5.176461782716475e-02}},
+       Descriptor{4, 7.637426139226190e-02, {+1.663967696311171e-01, +8.071634863884439e-01}},
+       Descriptor{4, 3.673080503418831e-02, {+4.976649895838920e-01, -3.972616744496609e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 6: {
+    constexpr size_t nb_points = 28;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 3.614462938209505e-02, {+9.850385924415599e-01}},
+       Descriptor{2, 5.544690202422076e-02, {-5.039978952117941e-01}},
+       Descriptor{3, 1.164296357658436e-02, {+1.750424465865124e-02}},
+       Descriptor{3, 7.688064192655711e-02, {+1.617417813899514e-01}},
+       Descriptor{4, 4.962685514962321e-02, {+4.656535513495914e-01, +4.811426008466984e-01}},
+       Descriptor{6, 2.112374914835039e-02, {+3.459486985245699e-02, +2.025039451729335e-01, -8.094634904091534e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 7: {
+    constexpr size_t nb_points = 35;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.844568111268929e-02, {+9.802280695908916e-01}},
+       Descriptor{3, 6.118242173095039e-03, {+8.337595466021473e-03}},
+       Descriptor{4, 2.155086408622646e-02, {+4.815219753291366e-01, -8.413873542065260e-01}},
+       Descriptor{4, 2.917852490209846e-02, {+9.548324837148936e-02, -7.958490905869831e-01}},
+       Descriptor{5, 2.551485633514925e-02, {+7.429966820728956e-01, +1.214913159837829e-02}},
+       Descriptor{6, 3.894070327620761e-02, {+1.529845984247976e-01, +3.051562164322261e-01, +4.039345605321099e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 8: {
+    constexpr size_t nb_points = 46;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.073493664285546e-02, {+2.244338661059906e-01}},
+       Descriptor{2, 5.108942711218155e-02, {-6.818254415708658e-01}},
+       Descriptor{3, 5.269974490650717e-02, {+4.600889628137106e-01}},
+       Descriptor{3, 1.815224878414972e-02, {+5.341235093690714e-02}},
+       Descriptor{4, 7.145357753538616e-03, {+4.723878583976938e-02, +8.288154430586802e-01}},
+       Descriptor{4, 4.062926265898933e-02, {+1.740616079243704e-01, +4.060842293545903e-01}},
+       Descriptor{4, 1.114302734846531e-02, {+1.597492639425890e-01, +9.658651665406544e-01}},
+       Descriptor{4, 2.108650929358649e-02, {+4.585690687909513e-01, +8.761959100002542e-01}},
+       Descriptor{6, 1.364752909087307e-02, {+8.588127507759013e-03, +7.285980718010000e-01, +5.773502691896257e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 9: {
+    constexpr size_t nb_points = 59;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 3.428347129092372e-02, {-2.750184190077768e-01}},
+       Descriptor{3, 1.639279770445363e-02, {+4.938630638969568e-01}},
+       Descriptor{4, 2.347178069870322e-02, {+2.362540169543293e-01, +8.712387576289576e-01}},
+       Descriptor{4, 5.761848158278895e-03, {+7.223833941638236e-02, +9.551937402361604e-01}},
+       Descriptor{4, 3.492479305778020e-02, {+4.383137607101617e-01, +4.364117817130079e-01}},
+       Descriptor{4, 7.373899623212321e-03, {+3.643401649407788e-02, +5.030056360566418e-01}},
+       Descriptor{4, 5.722460846448911e-03, {+4.828779929693860e-01, -9.817457230015865e-01}},
+       Descriptor{4, 2.433874301444529e-02, {+1.628698857202373e-01, -1.748668736196710e-01}},
+       Descriptor{5, 9.412963663135166e-03, {+8.213377527237301e-01, +1.626087609745086e-01}},
+       Descriptor{6, 1.801797749439730e-02, {+4.249444950639278e-02, +2.561926710584905e-01, +6.836158559766136e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 10: {
+    constexpr size_t nb_points = 84;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 3.219608823836797e-02, {}},
+       Descriptor{2, 1.323959532338741e-02, {+9.650866816169476e-01}},
+       Descriptor{3, 1.663071726413940e-02, {+4.300104731739727e-01}},
+       Descriptor{3, 2.237117375679376e-02, {+1.095696683547513e-01}},
+       Descriptor{3, 2.965144268028371e-03, {+1.581483663138682e-02}},
+       Descriptor{4, 2.041220712674361e-02, {+4.356018236697312e-01, +7.829035904287732e-01}},
+       Descriptor{4, 1.414434969765846e-02, {+1.485976165307029e-01, +7.673257532061416e-01}},
+       Descriptor{4, 3.545708824271026e-03, {+5.196648358223575e-02, +4.892913949246038e-01}},
+       Descriptor{4, 6.058080482992846e-03, {+4.990219694442680e-01, +6.916716292769725e-01}},
+       Descriptor{4, 3.555888039958573e-03, {+4.155250160277021e-02, +8.910010843120678e-01}},
+       Descriptor{4, 3.038102466521747e-02, {+2.343945196820784e-01, +4.651342767282841e-01}},
+       Descriptor{6, 6.211856757092111e-03, {+5.281151684656214e-02, +2.716521744885937e-01, +9.566215690575565e-01}},
+       Descriptor{6, 6.618419166146100e-03, {+1.675738337212976e-01, +1.019125209869291e-02, +5.391797458851759e-01}},
+       Descriptor{6, 1.607306259567184e-02, {+3.291869417398026e-01, +5.090816276695182e-02, -2.642847151350913e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 11: {
+    constexpr size_t nb_points = 99;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.168014406730469e-02, {-5.549549808499665e-01}},
+       Descriptor{2, 1.045760798587228e-02, {+9.704322100480244e-01}},
+       Descriptor{2, 3.493462507713402e-03, {-8.225054517645587e-02}},
+       Descriptor{3, 1.610610418171415e-02, {+4.269221881685347e-01}},
+       Descriptor{4, 2.203310285466430e-02, {+4.396327659106838e-01, +6.759979260851990e-01}},
+       Descriptor{4, 1.274418696180960e-02, {+2.009128761138035e-01, +8.360535436001461e-01}},
+       Descriptor{4, 2.842526101831001e-03, {+2.158509804317693e-02, +5.861905524880673e-01}},
+       Descriptor{4, 1.509857722810566e-03, {+4.941546402080231e-01, +9.747001655491775e-01}},
+       Descriptor{4, 3.650427064786428e-03, {+5.996027726544360e-02, +9.450272677498353e-01}},
+       Descriptor{4, 2.100290671698337e-02, {+2.328102236807494e-01, +2.635224843921006e-01}},
+       Descriptor{4, 1.876195853667291e-02, {+1.213201391549184e-01, +4.439791020520624e-01}},
+       Descriptor{4, 5.194819129164601e-03, {+4.987543911906001e-01, +5.750153420806698e-01}},
+       Descriptor{5, 7.050587938770526e-03, {+1.046140524481813e-01, +1.651849633425114e-02}},
+       Descriptor{6, 5.901269948294771e-03, {+7.196325697558484e-02, +2.979854667459965e-01, +9.371332380619902e-01}},
+       Descriptor{6, 6.212665465530579e-03, {+1.998026706474004e-01, +1.720948255102629e-02, +7.302645437140292e-01}},
+       Descriptor{6, 1.385914960018441e-02, {+3.197359768880742e-01, +4.628523865251271e-02, -2.345966255449601e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 12: {
+    constexpr size_t nb_points = 136;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.122540095264984e-02, {+9.249605281775015e-01}},
+       Descriptor{2, 2.250978701634355e-02, {+3.174565048121913e-01}},
+       Descriptor{3, 5.489424083937044e-03, {+4.853372007888398e-02}},
+       Descriptor{3, 4.964406537953573e-04, {+3.340541016130764e-04}},
+       Descriptor{4, 1.020904001099484e-02, {+4.849074297077183e-01, +5.587032406470354e-01}},
+       Descriptor{4, 2.100649550809441e-03, {+1.497566246841529e-01, -9.990255354639939e-01}},
+       Descriptor{4, 1.520942066705075e-02, {+2.344536517846724e-01, +3.308248202440709e-01}},
+       Descriptor{4, 1.365828920018060e-02, {+4.409030162469282e-01, -7.263193646044468e-02}},
+       Descriptor{4, 2.788739322048965e-03, {+4.870412467653055e-01, +9.373420953931805e-01}},
+       Descriptor{4, 2.252095085593022e-03, {+2.486381930021292e-02, +6.814367870790086e-01}},
+       Descriptor{4, 9.917448258967848e-03, {+1.118542147928236e-01, +7.546508799087877e-01}},
+       Descriptor{5, 9.524594103832613e-03, {+1.207867185816364e-01, +7.097236881695401e-01}},
+       Descriptor{5, 6.688135429090100e-03, {+6.481099336610571e-01, +3.419306029008594e-01}},
+       Descriptor{6, 5.667322471331417e-03, {+2.009036502771764e-02, +1.335404714654308e-01, +3.889378443355619e-01}},
+       Descriptor{6, 1.258664545850983e-02, {+3.214917379706315e-01, +1.648190492804087e-01, +7.118012620024268e-01}},
+       Descriptor{6, 1.143701180003211e-02, {+6.899565054914573e-02, +6.636404691861656e-01, -4.223089212637487e-01}},
+       Descriptor{6, 4.495597556144068e-03, {+1.996263664334138e-02, +2.615529990296567e-01, +8.442893497066150e-01}},
+       Descriptor{6, 1.189774020910150e-03, {+9.002977238916547e-02, +1.552185633457250e-02, +9.525314828501794e-01}},
+       Descriptor{6, 4.663778699523011e-03, {+1.172214513692467e-01, +5.641016399337317e-01, -9.443120180277191e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 13: {
+    constexpr size_t nb_points = 162;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{4, 2.629059706793623e-03, {+4.898439392870481e-01, +9.257498273505042e-01}},
+       Descriptor{4, 5.973423749312596e-03, {+4.652402431593082e-01, -6.807809122309588e-01}},
+       Descriptor{4, 8.093549424155690e-03, {+1.189161740974999e-01, +8.113054084779335e-01}},
+       Descriptor{4, 6.819116259035672e-03, {+4.065656818638698e-01, +1.925054913726048e-02}},
+       Descriptor{4, 4.933451368773049e-03, {+3.731243598486834e-01, +9.244211981221383e-01}},
+       Descriptor{4, 2.014708013288427e-03, {+2.222577118367550e-02, +2.903696330212439e-01}},
+       Descriptor{4, 1.233577073707530e-02, {+4.252710267490136e-01, +6.716647847389562e-01}},
+       Descriptor{4, 1.136479433767108e-02, {+2.896929731593648e-01, +3.940165471189255e-01}},
+       Descriptor{4, 6.666864960682452e-03, {+1.072868932263340e-01, +1.612419567102727e-01}},
+       Descriptor{4, 1.194522013639762e-02, {+2.080125480772502e-01, +2.621034383425459e-01}},
+       Descriptor{4, 1.068788094061122e-02, {+2.230030950549982e-01, +7.517293034088793e-01}},
+       Descriptor{4, 1.666344327463221e-03, {+2.617439160849268e-02, +8.530545934139608e-01}},
+       Descriptor{5, 4.512051617065152e-03, {+6.914170159250816e-03, +2.742659465495736e-01}},
+       Descriptor{5, 3.488706839443290e-03, {+1.210652398684976e-01, +2.342377584496339e-02}},
+       Descriptor{5, 1.118035069441576e-02, {+9.353601775463583e-02, +5.415308571572887e-01}},
+       Descriptor{6, 4.205260232450528e-03, {+1.908095378196189e-02, +4.425451813256520e-01, +3.662111224258147e-01}},
+       Descriptor{6, 4.376791749742736e-03, {+2.923396969545124e-01, +1.817765226028588e-02, +7.893982266689565e-01}},
+       Descriptor{6, 4.406755041936957e-03, {+2.123093816715971e-02, +8.615902483468726e-01, +5.566323460176222e-01}},
+       Descriptor{6, 1.203503267726805e-02, {+2.541452627735283e-01, +7.525507194012332e-02, -4.323213872909485e-01}},
+       Descriptor{6, 1.398692188464922e-03, {+1.414034499801619e-01, +2.485309321657718e-02, +9.754978010051167e-01}},
+       Descriptor{6, 4.755154887378063e-03, {+5.920519581168684e-01, +1.079442302776815e-01, +9.602045342477669e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 14: {
+    constexpr size_t nb_points = 194;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.177233712035789e-02, {-5.431508197665302e-01}},
+       Descriptor{3, 1.353619130586776e-02, {+1.531409494078462e-01}},
+       Descriptor{3, 2.863624459018961e-03, {+1.094642266754876e-01}},
+       Descriptor{4, 2.831710195314422e-03, {+4.852139749601931e-01, +9.499413855584002e-01}},
+       Descriptor{4, 4.354649559352045e-03, {+4.811857851081537e-01, -3.416804188824072e-01}},
+       Descriptor{4, 9.145278212110571e-04, {+8.542525491697950e-02, +9.993425247803482e-01}},
+       Descriptor{4, 1.640665865351787e-03, {+4.949498254464672e-01, +4.043764192878801e-01}},
+       Descriptor{4, 1.217329578934041e-02, {+2.006722689888837e-01, +6.704323691216523e-01}},
+       Descriptor{4, 1.062128023171532e-03, {+1.346314023891985e-02, +4.402219142928553e-01}},
+       Descriptor{4, 8.146219031353574e-03, {+3.888073656098609e-01, +8.664942125245655e-01}},
+       Descriptor{4, 1.188198250865211e-02, {+2.589597694248802e-01, -3.805332165701859e-01}},
+       Descriptor{4, 4.510606073543659e-03, {+6.566746391585167e-02, +3.180500426640964e-01}},
+       Descriptor{4, 1.201058503487853e-02, {+4.462504387663027e-01, +6.737378781532631e-01}},
+       Descriptor{4, 4.766438302156675e-03, {+1.155506883468244e-01, +8.527706525272908e-01}},
+       Descriptor{4, 9.777024948606015e-04, {+2.579397983791752e-02, +9.172594869558284e-01}},
+       Descriptor{5, 5.111880702605887e-03, {+2.313133650799548e-02, +3.737411598567404e-01}},
+       Descriptor{5, 2.171762107022176e-03, {+7.808376567245785e-02, +8.716783743732383e-03}},
+       Descriptor{5, 1.040227457387526e-02, {+1.932090397525293e-01, +4.408189862889988e-01}},
+       Descriptor{6, 3.590362689299532e-03, {+1.106956102435793e-02, +3.495104935335414e-01, +6.888113400872247e-01}},
+       Descriptor{6, 5.210848278113965e-03, {+2.813786819004687e-01, +7.252407174904094e-02, +8.818281117320524e-01}},
+       Descriptor{6, 2.902421092721445e-03, {+1.360674597305564e-02, +7.694543610610796e-01, +2.082229797704095e-01}},
+       Descriptor{6, 1.093709739870311e-02, {+3.160642637515643e-01, +9.806129213500263e-02, -2.960531739682007e-01}},
+       Descriptor{6, 1.070623154200375e-03, {+2.010219519914552e-01, +1.015338057469439e-02, +9.567691001326991e-01}},
+       Descriptor{6, 2.366054231962321e-03, {+5.404862370418329e-01, +1.675040220811526e-01, +9.843823434581401e-01}},
+       Descriptor{6, 2.619672090010859e-03, {+1.016331118955575e-01, +1.682509189588544e-02, +7.176031930976530e-01}},
+       Descriptor{6, 7.096030229028862e-03, {+7.553391402298588e-01, +5.528506804337090e-02, -4.978572721822902e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 15: {
+    constexpr size_t nb_points = 238;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.097348853586182e-02, {+6.802218229609156e-01}},
+       Descriptor{2, 3.261395830734888e-03, {+9.049666192679098e-01}},
+       Descriptor{3, 9.394140546438022e-03, {+2.180179116942069e-01}},
+       Descriptor{3, 7.472192538440717e-03, {+1.819996803717089e-01}},
+       Descriptor{3, 1.125148661004542e-03, {+1.740140116312610e-02}},
+       Descriptor{3, 4.027894259445648e-03, {+5.427115018599665e-02}},
+       Descriptor{4, 4.698740527412616e-03, {+3.702896314191779e-01, +1.017726699824298e-01}},
+       Descriptor{4, 4.423106873185891e-03, {+6.837923242190364e-02, +7.328812049735380e-01}},
+       Descriptor{4, 1.190871191679164e-02, {+4.041029865296590e-01, -3.417820508269636e-01}},
+       Descriptor{4, 3.419058355265174e-03, {+4.924009437532922e-01, -1.425907114174295e-01}},
+       Descriptor{4, 7.607869583069166e-03, {+4.679592294726652e-01, +6.612792268372575e-01}},
+       Descriptor{4, 3.487721814344312e-03, {+2.538613877625208e-01, +9.653983029752667e-01}},
+       Descriptor{4, 4.126571123768199e-03, {+1.490233678882002e-01, +9.207186791179688e-01}},
+       Descriptor{4, 2.055229043939243e-03, {+4.943016520035221e-01, +8.528797591642616e-01}},
+       Descriptor{5, 5.333436981117544e-03, {+2.380227290434666e-01, +1.919120975700847e-02}},
+       Descriptor{6, 4.565836294062908e-03, {+2.658819381343187e-01, +9.470242762478344e-02, +8.051851008291565e-01}},
+       Descriptor{6, 5.459132755230557e-04, {+2.824880313716481e-02, +6.797646253429619e-03, +5.550126100536413e-01}},
+       Descriptor{6, 4.319194829106329e-03, {+6.410939576871652e-01, +1.511219453234863e-02, +4.965601017444452e-01}},
+       Descriptor{6, 5.377414350253174e-03, {+1.570345607673937e-01, +7.920488512684705e-02, +2.807402393928705e-01}},
+       Descriptor{6, 7.161694599840893e-03, {+2.618681572601245e-01, +1.978262294209248e-01, +5.539260799071125e-01}},
+       Descriptor{6, 5.425741614460213e-04, {+6.532458874680514e-03, +4.868056225266830e-02, +9.130997730502041e-01}},
+       Descriptor{6, 1.437669184909563e-03, {+3.702624031957258e-01, +9.129771330289427e-02, +9.857586619210927e-01}},
+       Descriptor{6, 1.116039038725027e-03, {+1.377079768509805e-01, +3.730083695338125e-02, +9.761262490766817e-01}},
+       Descriptor{6, 4.855981285749960e-03, {+4.784950101267365e-01, +1.635654688089934e-01, +8.308622970412985e-01}},
+       Descriptor{6, 1.797153116855535e-03, {+3.278240979071815e-01, +2.496158166292168e-02, +9.410500001533124e-01}},
+       Descriptor{6, 9.605058598669360e-03, {+3.475735063324100e-01, +8.713022883913982e-02, -2.293965609348112e-01}},
+       Descriptor{6, 2.702127731284175e-03, {+1.096327891311607e-01, +1.283956773909193e-02, +3.801848607953903e-01}},
+       Descriptor{6, 5.504446034785920e-03, {+2.078784801536998e-01, +8.490456755292054e-02, +5.633005891537184e-01}},
+       Descriptor{6, 2.394682993576167e-03, {+1.953330193360589e-01, +1.430457871757475e-02, +7.767703733759026e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 16: {
+    constexpr size_t nb_points = 287;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.260828009387497e-02, {+6.806235853917343e-01}},
+       Descriptor{3, 5.804416586274102e-04, {+8.954084031214650e-03}},
+       Descriptor{3, 1.451527527703537e-03, {+5.539081004402164e-02}},
+       Descriptor{3, 1.103925194575912e-02, {+3.916317627335951e-01}},
+       Descriptor{4, 2.389660788445647e-03, {+5.610007788583466e-02, +7.685450672389825e-01}},
+       Descriptor{4, 1.118529592275642e-02, {+2.669967593271997e-01, +3.187166719009041e-01}},
+       Descriptor{4, 4.452710626148950e-03, {+1.619375672046858e-01, +7.425213340738127e-01}},
+       Descriptor{4, 9.931042920523256e-03, {+4.259891438745869e-01, -5.391540839796620e-01}},
+       Descriptor{4, 2.798331509865767e-03, {+4.943682456985981e-01, +2.910077027212657e-01}},
+       Descriptor{4, 4.595079144000768e-03, {+4.745817664118026e-01, +7.702843820193086e-01}},
+       Descriptor{4, 2.649741982735800e-03, {+2.785739189962955e-01, +9.637659513314421e-01}},
+       Descriptor{4, 2.379174945004374e-03, {+1.564426776295505e-01, +9.700236378009609e-01}},
+       Descriptor{4, 1.290211537994051e-03, {+4.933597406415689e-01, +9.402630634429575e-01}},
+       Descriptor{5, 7.174970445767767e-03, {+5.254267651000835e-01, +9.931494549031464e-02}},
+       Descriptor{5, 3.904196912906489e-03, {+3.482743570815052e-01, +2.127155614722863e-02}},
+       Descriptor{5, 5.896847921349438e-03, {+1.654756461617898e-01, +8.608509799836524e-02}},
+       Descriptor{6, 2.434940232310643e-03, {+1.639763763638563e-01, +5.517222908705197e-02, +8.775190479419079e-01}},
+       Descriptor{6, 7.867926281754163e-04, {+3.703825228193449e-02, +1.050366659141256e-02, +5.241361844419883e-01}},
+       Descriptor{6, 1.973345895093291e-03, {+6.350080118967869e-01, +5.563959422105249e-03, +6.546900713490693e-01}},
+       Descriptor{6, 3.902804476486658e-03, {+1.297894012222233e-01, +7.248681972075718e-02, +4.803772456633624e-01}},
+       Descriptor{6, 2.865194848993297e-03, {+3.059321030072774e-01, +6.212018826128094e-02, +8.420714610259229e-01}},
+       Descriptor{6, 4.265862973729131e-03, {+1.677554820715698e-01, +2.612303617350463e-01, +6.466653853149219e-01}},
+       Descriptor{6, 2.763846307136280e-04, {+1.529621929633006e-03, +3.376869229909243e-02, +8.957708274465785e-01}},
+       Descriptor{6, 1.594720494514084e-03, {+3.532796606132861e-01, +9.492422215886979e-02, +9.832783024215900e-01}},
+       Descriptor{6, 5.967018601525356e-04, {+9.592623259204197e-02, +2.488171702893576e-02, +9.797358781454657e-01}},
+       Descriptor{6, 5.089010857368133e-03, {+4.963305263335406e-01, +1.757029526928022e-01, +8.611424584617942e-01}},
+       Descriptor{6, 1.023252728565292e-03, {+2.738501035919119e-01, +1.407861220922605e-02, +9.516023826285763e-01}},
+       Descriptor{6, 4.833611837936114e-03, {+3.828844998895772e-01, +5.651716232003427e-02, -3.926210681927463e-01}},
+       Descriptor{6, 2.256570449498587e-03, {+8.993653239530038e-02, +1.924125702016291e-02, +2.277368351329287e-01}},
+       Descriptor{6, 4.394217441895532e-03, {+2.516074150213084e-01, +6.113701845424014e-02, +5.771276473820092e-01}},
+       Descriptor{6, 1.441280420852857e-03, {+1.524244609819593e-01, +5.018269956194812e-03, +7.029237083896506e-01}},
+       Descriptor{6, 7.638903866482450e-03, {+2.475011786624472e-01, +1.365910574355268e-01, -2.555004528315441e-01}},
+       Descriptor{6, 3.266920063147978e-03, {+7.591852986956938e-01, +1.735437181095786e-02, +2.659716737332686e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 17: {
+    constexpr size_t nb_points = 338;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 7.521652592973654e-03, {+7.332980977058475e-01}},
+       Descriptor{4, 3.952340142081542e-03, {+4.665556153120432e-01, +7.407048594232563e-01}},
+       Descriptor{4, 2.658797102389008e-03, {+4.923809674098066e-01, +1.925784688493710e-01}},
+       Descriptor{4, 2.303261680132425e-03, {+4.466963397682847e-01, +9.724914617208265e-01}},
+       Descriptor{4, 4.300609620564292e-03, {+2.058899494758731e-01, +2.386810048778012e-01}},
+       Descriptor{4, 3.022106015301819e-03, {+2.305779360323416e-01, +3.012229853956593e-01}},
+       Descriptor{4, 3.076949950084475e-04, {+7.072650234259644e-03, +6.517254037629554e-01}},
+       Descriptor{4, 3.073440319150984e-03, {+5.248385455049231e-02, -5.044383522897413e-01}},
+       Descriptor{4, 4.905529550079285e-03, {+2.780743897936196e-01, -4.157283981367805e-01}},
+       Descriptor{4, 2.706669648409243e-03, {+3.893995813144771e-01, -8.036253483433273e-01}},
+       Descriptor{4, 3.457774927613496e-03, {+1.132999955833483e-01, +4.250397278118490e-01}},
+       Descriptor{4, 5.170517454197127e-03, {+4.687774749283292e-01, -2.967208107336694e-01}},
+       Descriptor{4, 1.402258311530629e-03, {+4.942337889964747e-01, +8.919484290177192e-01}},
+       Descriptor{4, 4.240102604381064e-03, {+2.906999098818323e-01, +1.317224598994015e-01}},
+       Descriptor{4, 3.284148741170156e-03, {+9.376930399184973e-02, +8.547449509711846e-01}},
+       Descriptor{4, 1.134225985312425e-03, {+1.632812980104028e-01, +9.979247130558654e-01}},
+       Descriptor{4, 7.470854816360809e-03, {+1.774626418569745e-01, -6.791385858496140e-01}},
+       Descriptor{4, 3.434038461352071e-03, {+3.930786561208929e-01, +2.690582708305222e-01}},
+       Descriptor{4, 3.146260969412623e-03, {+2.711153947647855e-01, +9.623986887607461e-01}},
+       Descriptor{4, 5.752613942102343e-04, {+2.430622100156484e-02, +9.425587161003264e-01}},
+       Descriptor{5, 6.555279553032400e-03, {+1.273859447314594e-01, +2.765687474993688e-01}},
+       Descriptor{6, 2.945409561987205e-03, {+2.450510833431427e-01, +8.524685018280553e-02, +9.106045966534763e-01}},
+       Descriptor{6, 2.453063041563170e-03, {+3.192911229101512e-01, +2.764498521125525e-02, +5.755372475087557e-02}},
+       Descriptor{6, 2.870396640498966e-03, {+4.577741357714497e-01, +1.385428807092584e-01, -9.235677741171985e-02}},
+       Descriptor{6, 1.826827790065983e-03, {+2.421105196372634e-01, +1.245683910267135e-02, -2.750816491221573e-01}},
+       Descriptor{6, 4.684287720797846e-03, {+7.269945541994681e-01, +8.880930240992388e-02, +2.353034707366664e-01}},
+       Descriptor{6, 6.198783236777368e-03, {+4.870517086755036e-01, +1.725779658472295e-01, -5.739332375158235e-01}},
+       Descriptor{6, 4.197863049050683e-03, {+1.582123188291182e-01, +3.108975273704328e-01, +8.562321460031186e-01}},
+       Descriptor{6, 2.552814201463011e-03, {+5.973833093262283e-01, +4.329678563553625e-02, +7.520938435967152e-01}},
+       Descriptor{6, 2.090115170917154e-03, {+1.111929938785087e-01, +3.682608320557917e-02, +2.115412627288322e-02}},
+       Descriptor{6, 7.568671403396213e-04, {+3.878650062905668e-02, +7.045410253068108e-03, +2.249484087437713e-01}},
+       Descriptor{6, 1.300065728839461e-03, {+1.417441449927015e-01, +6.656135005864013e-03, +3.824194976983090e-01}},
+       Descriptor{6, 8.479873802967710e-04, {+1.343472140817544e-01, +2.698141838221961e-02, +9.741848074979857e-01}},
+       Descriptor{6, 6.404613040101877e-03, {+3.195031824862086e-01, +8.134405613722046e-02, +4.381759877407309e-01}},
+       Descriptor{6, 1.132352094196614e-03, {+8.263084385883397e-02, +1.026354650397643e-02, +7.684631026560178e-01}},
+       Descriptor{6, 2.064410253144382e-03, {+3.827807429336583e-01, +6.749013415056980e-03, +5.465728592769658e-01}},
+       Descriptor{6, 1.048193458332381e-03, {+2.446543644502634e-01, +5.650199208693200e-03, +8.553667006980621e-01}},
+       Descriptor{6, 9.390285812952761e-04, {+2.637071367985937e-02, +6.264598409479015e-01, +9.755523282064171e-01}},
+       Descriptor{6, 4.216060332324916e-03, {+4.340745420159236e-02, +1.947680793017931e-01, +6.465743617002622e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 18: {
+    constexpr size_t nb_points = 396;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{4, 2.257692848096014e-03, {+4.728631477307805e-01, +9.068774432765224e-01}},
+       Descriptor{4, 3.522000993611865e-03, {+1.090318158369727e-01, +5.191310054808369e-01}},
+       Descriptor{4, 4.461723497786998e-03, {+2.141954002982068e-01, +6.975267033041567e-01}},
+       Descriptor{4, 3.707999359392404e-04, {+8.415088753342596e-03, +4.704963597796460e-01}},
+       Descriptor{4, 2.423661142721756e-03, {+4.941564402940962e-02, -3.771046294090744e-01}},
+       Descriptor{4, 2.814996700016239e-03, {+4.574258170355346e-01, -2.709290831593626e-01}},
+       Descriptor{4, 5.337642166957648e-03, {+2.945570490820424e-01, -1.757797989950063e-01}},
+       Descriptor{4, 8.574266664464696e-03, {+4.148583469418042e-01, -2.903119874076979e-01}},
+       Descriptor{4, 2.265725871486650e-03, {+1.801567120271234e-01, -2.996313713953939e-01}},
+       Descriptor{4, 2.955157959707394e-03, {+4.092592655375933e-01, -8.687914069087144e-01}},
+       Descriptor{4, 3.919466496845992e-03, {+4.106876572727113e-01, -7.102916194693503e-01}},
+       Descriptor{4, 6.030674708196229e-04, {+4.942616172602019e-01, +9.613370670800333e-01}},
+       Descriptor{4, 4.577980994542588e-03, {+1.455821883151533e-01, +2.580797174139677e-01}},
+       Descriptor{4, 2.430663642304682e-03, {+7.759286964629900e-02, +8.574527537341975e-01}},
+       Descriptor{4, 2.159969925839523e-03, {+1.688466480848478e-01, +9.442832226314590e-01}},
+       Descriptor{4, 7.901051089233524e-03, {+2.737821968608845e-01, -5.261705496722796e-01}},
+       Descriptor{4, 2.173845391531516e-03, {+3.193703110432156e-01, +7.838756187326278e-01}},
+       Descriptor{4, 2.248646315174777e-03, {+2.789278090499041e-01, +9.684120338438452e-01}},
+       Descriptor{4, 3.442548195872391e-04, {+1.473433086781356e-02, +9.055493494371784e-01}},
+       Descriptor{5, 8.069935581612784e-03, {+2.803931989784251e-01, +1.730708483947652e-01}},
+       Descriptor{6, 2.387723163590078e-03, {+3.067402740370590e-01, +4.725833161993727e-02, +8.686462758628247e-01}},
+       Descriptor{6, 3.430528221040949e-03, {+5.587472044782793e-01, +6.374864935475613e-02, +1.027445357789138e-01}},
+       Descriptor{6, 6.029545367216524e-03, {+1.295467219204834e-01, +2.834002542935580e-01, -4.512744270430775e-01}},
+       Descriptor{6, 3.276823879158102e-03, {+2.943165499688927e-01, +3.236061609245209e-02, -4.364849670272578e-01}},
+       Descriptor{6, 3.663549210540714e-03, {+7.045618883179275e-01, +6.827271159006545e-02, +1.782321853488997e-01}},
+       Descriptor{6, 4.424488262807732e-03, {+5.336092397180819e-01, +8.232278479338200e-02, -6.571528038169470e-01}},
+       Descriptor{6, 2.760063523214631e-03, {+1.659343236267790e-01, +2.958566157742775e-01, +8.788603720074640e-01}},
+       Descriptor{6, 1.392109889443924e-03, {+5.873912217089957e-01, +9.153855225433397e-03, +7.488835368067833e-01}},
+       Descriptor{6, 2.138435872642536e-03, {+1.216404492275597e-01, +4.924556835776799e-02, -8.479900295560096e-02}},
+       Descriptor{6, 5.166067073488927e-04, {+4.208095332260363e-02, +7.753734519664009e-03, +9.396055358943239e-02}},
+       Descriptor{6, 1.953953141133318e-03, {+4.516351625773616e-01, +2.282191697171249e-02, +4.454084028264259e-01}},
+       Descriptor{6, 1.255008598363820e-03, {+1.252580641579170e-01, +7.396327404727600e-03, +3.172925739593950e-01}},
+       Descriptor{6, 1.042334114181810e-03, {+1.765722195894258e-01, +6.530021157253776e-02, +9.730112939053892e-01}},
+       Descriptor{6, 1.279342041886178e-03, {+2.404267015692479e-01, +8.587901550681881e-03, +1.654571949278815e-01}},
+       Descriptor{6, 1.087772234137440e-03, {+6.440585522631566e-02, +1.226780343665597e-02, +7.058192837170146e-01}},
+       Descriptor{6, 8.506443220554228e-04, {+2.681069831840066e-01, +4.479326561367245e-03, +6.566582555690533e-01}},
+       Descriptor{6, 1.071738436276040e-03, {+1.727205252244992e-01, +1.173345352446697e-02, +8.740347679939118e-01}},
+       Descriptor{6, 1.222389838372311e-03, {+1.118329953359960e-01, +5.446775610503505e-01, +9.879516615930261e-01}},
+       Descriptor{6, 1.426482164900413e-03, {+5.899536365475773e-01, +8.017587417702520e-03, +1.572003893989445e-01}},
+       Descriptor{6, 3.834177899773436e-03, {+9.615405978617643e-02, +2.067498834662211e-01, +7.510430318557424e-01}},
+       Descriptor{6, 4.758021594541443e-04, {+3.068968355307726e-01, +1.177465532391100e-02, +9.786522981024399e-01}},
+       Descriptor{6, 3.711298716255058e-04, {+7.984864478527595e-02, +1.740633561957181e-02, +9.787643170060012e-01}},
+       Descriptor{6, 2.736409660029034e-03, {+3.607777906277682e-02, +1.618562004775664e-01, +5.932565552690645e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 19: {
+    constexpr size_t nb_points = 420;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 6.766510972429800e-03, {+5.244830821459505e-01}},
+       Descriptor{2, 2.666997686915707e-03, {+7.453333881549420e-02}},
+       Descriptor{2, 2.198296683612197e-03, {+9.566097292149883e-01}},
+       Descriptor{3, 1.803557661024045e-03, {+4.935398626809867e-01}},
+       Descriptor{3, 6.354430508374619e-03, {+2.767659367008514e-01}},
+       Descriptor{3, 1.636834721720784e-03, {+5.457779485028427e-02}},
+       Descriptor{3, 4.893654007837226e-04, {+1.399697352188861e-02}},
+       Descriptor{4, 1.846418513040312e-03, {+1.418740786751734e-01, +9.552594993861951e-01}},
+       Descriptor{4, 2.773402798745861e-04, {+8.395599010104145e-03, +6.731987542402844e-01}},
+       Descriptor{4, 2.823634437215422e-03, {+1.035973359771996e-01, -2.005717856238193e-01}},
+       Descriptor{4, 7.726075998933063e-03, {+3.956080996828434e-01, +3.080298478277679e-01}},
+       Descriptor{4, 2.193983773241012e-04, {+1.400193119774357e-02, +2.354401887617481e-01}},
+       Descriptor{4, 4.366650366888420e-03, {+4.801897764603656e-01, +4.828040748109914e-01}},
+       Descriptor{4, 4.029293922819735e-03, {+1.623484102426076e-01, -2.002124012363609e-01}},
+       Descriptor{4, 1.130768692395794e-03, {+4.972236736522475e-01, +7.211608708255114e-01}},
+       Descriptor{4, 4.287863606686208e-03, {+1.990216655966096e-01, +7.769851170153003e-01}},
+       Descriptor{4, 1.592256072404633e-03, {+1.202889101503504e-01, -6.997989522584932e-01}},
+       Descriptor{4, 2.052668995583038e-03, {+5.321915260988313e-02, +6.322324604037224e-01}},
+       Descriptor{4, 3.894795325529755e-03, {+4.012796612843944e-01, +8.985968033126313e-01}},
+       Descriptor{4, 1.803402089659100e-03, {+1.089883669604236e-01, +6.414118273921905e-01}},
+       Descriptor{4, 4.906673814096024e-03, {+4.472748897677901e-01, +1.418065900083249e-01}},
+       Descriptor{4, 8.385245625735418e-04, {+6.923354485715784e-02, +9.592516295656434e-01}},
+       Descriptor{4, 1.344422701677694e-03, {+2.475145234973103e-01, +9.911400740550351e-01}},
+       Descriptor{4, 1.212351202429953e-03, {+4.533377451558121e-01, +9.881823361248698e-01}},
+       Descriptor{4, 2.704967330298844e-04, {+1.808374112681072e-02, +9.537067614804079e-01}},
+       Descriptor{5, 1.194495738162939e-03, {+1.297897131077726e-01, +6.917496569505294e-03}},
+       Descriptor{5, 3.103142225008695e-03, {+3.779418721990778e-01, +3.100617423599103e-02}},
+       Descriptor{5, 5.131660654552337e-03, {+6.446313119552312e-02, +2.203251202566757e-01}},
+       Descriptor{6, 4.803074338815450e-03, {+1.625899734623090e-01, +2.824754920851798e-01, +1.568351483023571e-01}},
+       Descriptor{6, 8.150376242849885e-04, {+8.118414809439785e-03, +5.478835112834492e-02, -3.967511256013889e-01}},
+       Descriptor{6, 4.498489872233412e-03, {+6.582273496659641e-01, +1.178005364736766e-01, -4.885151647510187e-01}},
+       Descriptor{6, 2.731777351387361e-03, {+2.876874815978630e-01, +3.552685414544682e-02, +5.867894747260645e-01}},
+       Descriptor{6, 3.917995492165165e-03, {+2.839484400308330e-01, +2.073917255967352e-01, +4.831782153742011e-01}},
+       Descriptor{6, 8.946420147168489e-04, {+2.824570038349533e-02, +8.622300269568527e-02, -2.019344195912693e-01}},
+       Descriptor{6, 1.272619581981183e-03, {+3.915043117238656e-01, +5.619651603686126e-03, +3.317594047785773e-01}},
+       Descriptor{6, 4.539117516816274e-03, {+7.376676137243446e-02, +3.389265718024346e-01, -3.045944670757652e-01}},
+       Descriptor{6, 1.228077139941574e-03, {+1.582419524500696e-01, +8.020518897879757e-03, +6.242276968562098e-01}},
+       Descriptor{6, 2.481352686408294e-03, {+2.270465393157224e-01, +7.611830639869993e-02, +7.779863298475371e-01}},
+       Descriptor{6, 6.742435916065792e-04, {+1.074023202728770e-02, +6.748601969235382e-02, +8.305354371679037e-01}},
+       Descriptor{6, 3.085688768414200e-03, {+4.506297544312898e-02, +1.495289831435912e-01, +3.936943128499052e-01}},
+       Descriptor{6, 2.827744882828859e-03, {+3.899263834124326e-01, +5.365999012062311e-02, +8.362140373651296e-01}},
+       Descriptor{6, 7.880156766981593e-04, {+1.426168210785968e-02, +4.039638109964547e-01, +9.545849701585185e-01}},
+       Descriptor{6, 9.734356783437631e-04, {+3.314062155351160e-01, +8.833083116611448e-03, +7.096795149078843e-01}},
+       Descriptor{6, 2.344473397349397e-04, {+1.223644156202090e-01, +1.039237935936368e-02, +9.900280695189960e-01}},
+       Descriptor{6, 2.754928134962740e-03, {+2.929652829983945e-01, +1.324448044285512e-01, +9.178743530773790e-01}},
+       Descriptor{6, 8.530955496579550e-04, {+2.534748953911949e-01, +5.351470915991541e-02, +9.845698380380822e-01}},
+       Descriptor{6, 1.720791066604888e-03, {+4.407858720850005e-02, +1.461188761758480e-01, +8.569909006217998e-01}},
+       Descriptor{6, 5.238060160822010e-03, {+5.031457633505478e-01, +1.264604222462178e-01, +6.487185269932558e-01}},
+       Descriptor{6, 7.780035531900878e-04, {+8.749997984117155e-03, +2.433623618774898e-01, +9.028600070589583e-01}},
+       Descriptor{6, 2.799719934133974e-03, {+4.433279475253553e-01, +2.569581578007861e-01, +7.536500284803950e-01}},
+       Descriptor{6, 1.887126925839933e-03, {+2.485412372956145e-01, +1.127715403373798e-02, +2.418109616381699e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 20: {
+    constexpr size_t nb_points = 518;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 4.747044232976959e-03, {+8.420166009462728e-01}},
+       Descriptor{3, 6.640322548036050e-04, {+1.441108736989831e-02}},
+       Descriptor{3, 9.620914054915759e-04, {+5.949741342252451e-02}},
+       Descriptor{4, 2.742173179580797e-03, {+4.716249531173324e-01, +8.152882860873994e-01}},
+       Descriptor{4, 2.814324102526247e-03, {+4.084460043805324e-01, +5.308959921730525e-01}},
+       Descriptor{4, 3.864759174126348e-03, {+2.544280410411358e-01, +7.145164808360277e-01}},
+       Descriptor{4, 3.774538054936480e-03, {+4.774877095488475e-01, +3.302023087843083e-01}},
+       Descriptor{4, 2.695893315348362e-04, {+8.029263668218678e-03, +5.253966144526213e-01}},
+       Descriptor{4, 1.117541442536487e-03, {+4.968613511179721e-01, +1.678823330502635e-01}},
+       Descriptor{4, 1.168183967320753e-03, {+4.952615769053836e-01, +7.059801252317832e-01}},
+       Descriptor{4, 9.039109611475474e-04, {+3.535780971713121e-02, +3.380124871900346e-01}},
+       Descriptor{4, 3.369833201547859e-03, {+2.045970282680664e-01, -9.824414827677468e-02}},
+       Descriptor{4, 2.639764171119988e-03, {+1.406131665045265e-01, +6.840978690759630e-01}},
+       Descriptor{4, 2.599301978924752e-03, {+9.825610533777711e-02, -2.793583988322257e-01}},
+       Descriptor{4, 1.282233014437331e-03, {+5.059516645881403e-02, +5.440523359665514e-01}},
+       Descriptor{4, 1.290196339749456e-03, {+4.494558038329708e-01, +9.661789228499832e-01}},
+       Descriptor{4, 3.634034328176076e-03, {+2.188296334498393e-01, +5.161030390440184e-01}},
+       Descriptor{4, 3.865555241109362e-03, {+3.839280551265914e-01, +6.414153617240698e-01}},
+       Descriptor{4, 1.054906754865126e-03, {+5.997065917075951e-02, +8.943918441529238e-01}},
+       Descriptor{4, 1.562311721738531e-03, {+2.735494152095392e-01, +9.793184961065893e-01}},
+       Descriptor{4, 4.729101647325770e-04, {+4.907393281824849e-01, +9.886251338305795e-01}},
+       Descriptor{4, 1.740638131578173e-04, {+1.149301083025444e-02, +9.267723967676791e-01}},
+       Descriptor{4, 2.895959965319792e-03, {+4.048678509173179e-01, +9.164077711364235e-01}},
+       Descriptor{4, 5.234744278809832e-03, {+2.917111538437177e-01, -3.721772106793935e-01}},
+       Descriptor{4, 3.176639444983119e-03, {+4.459340787080981e-01, +3.782961145575000e-02}},
+       Descriptor{5, 2.716223123318332e-03, {+2.107382124681030e-01, +3.137124037930802e-02}},
+       Descriptor{5, 3.281825797738248e-03, {+2.031438036613264e-01, +1.114400380412596e-01}},
+       Descriptor{5, 3.187894698489262e-03, {+3.734168270486775e-01, +3.629012071225871e-02}},
+       Descriptor{6, 3.590341132235904e-03, {+5.331515253263366e-01, +1.335223252058013e-01, +7.802503826507077e-01}},
+       Descriptor{6, 5.184823629819776e-04, {+3.105220296426577e-03, +6.506617704521390e-02, -2.884770252636346e-01}},
+       Descriptor{6, 2.044053800301851e-03, {+7.387825606686489e-01, +9.320636776575365e-02, -2.442855072953742e-01}},
+       Descriptor{6, 2.454384702584994e-03, {+2.402209181076414e-01, +3.428169043211691e-02, +4.260690982676672e-01}},
+       Descriptor{6, 4.119406732850509e-03, {+8.161298386181808e-02, +3.087274419444517e-01, +2.420309868035705e-01}},
+       Descriptor{6, 5.762946980041836e-03, {+1.728159430622389e-01, +3.228866908185781e-01, -2.377845513467044e-01}},
+       Descriptor{6, 3.254649507017313e-03, {+2.247127800382396e-01, +1.327427633967232e-01, +4.683664775890787e-01}},
+       Descriptor{6, 1.376952861188114e-03, {+2.850103512086270e-02, +1.058138309132070e-01, -1.189934946219634e-01}},
+       Descriptor{6, 1.390144266230514e-03, {+3.896100790636586e-01, +1.097122964947396e-02, +4.812562527542947e-01}},
+       Descriptor{6, 1.973482620122281e-03, {+2.493335646225524e-01, +3.397868885786173e-01, -3.494229691424887e-02}},
+       Descriptor{6, 1.112029053786251e-03, {+7.042023600039281e-03, +3.177207979690921e-01, +2.078247495083118e-01}},
+       Descriptor{6, 1.196110133680204e-03, {+1.229469656661441e-01, +1.566757860788512e-02, +6.094751351099792e-01}},
+       Descriptor{6, 1.888982141486211e-03, {+2.259160262165568e-01, +5.215188464418734e-02, +8.360830877198899e-01}},
+       Descriptor{6, 5.659037184544154e-04, {+9.471493326637022e-03, +4.964414152642235e-02, +7.613841735998289e-01}},
+       Descriptor{6, 1.832188797223636e-03, {+5.243114241272343e-02, +1.452721772236918e-01, +4.996792895702993e-01}},
+       Descriptor{6, 8.460253945737610e-04, {+8.872202555510618e-03, +3.816302433543118e-01, +8.877001109429070e-01}},
+       Descriptor{6, 1.597513197343773e-03, {+2.592518129005248e-01, +5.551397100853696e-01, +8.790486279875863e-01}},
+       Descriptor{6, 2.678157870344808e-03, {+3.527097683072426e-01, +4.546097143409199e-02, +6.369991526576181e-01}},
+       Descriptor{6, 7.373212126545897e-04, {+1.450709676542589e-01, +1.181702543851609e-02, +8.952124765779996e-01}},
+       Descriptor{6, 1.390059413378896e-03, {+1.874143309040665e-01, +1.371981166173774e-01, +9.134247047008914e-01}},
+       Descriptor{6, 7.964285504550415e-04, {+6.814016086596048e-02, +1.498311852045099e-01, +9.751784156687211e-01}},
+       Descriptor{6, 1.460116442574025e-03, {+3.340585216441213e-01, +5.496962861346901e-02, +9.356631230196827e-01}},
+       Descriptor{6, 1.348373684024378e-03, {+6.785157976700519e-02, +1.205147270278029e-01, +7.655747942493478e-01}},
+       Descriptor{6, 3.286050878128491e-03, {+5.023136055537912e-01, +1.076311573233394e-01, +5.182173603742516e-01}},
+       Descriptor{6, 1.124720118777690e-03, {+7.819399743424233e-03, +2.556542053755758e-01, +7.166820851155357e-01}},
+       Descriptor{6, 2.127107274890790e-03, {+6.609230621965876e-01, +9.839569585073644e-02, +6.803594853699388e-01}},
+       Descriptor{6, 7.135219223491392e-04, {+1.784736485903538e-01, +1.796539994312394e-03, +2.866535225594761e-01}},
+       Descriptor{6, 3.719588400835986e-04, {+2.532227359651285e-01, +1.312175908589084e-02, +9.829888431964284e-01}},
+       Descriptor{6, 2.151829924432428e-04, {+1.443505367133109e-02, +6.867387590589011e-02, +9.834928544568246e-01}},
+       Descriptor{6, 8.163226512583331e-04, {+2.794904874495157e-01, +1.274003485132245e-01, +9.907920620663775e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(PrismGaussQuadrature::max_degree) + "on prisms");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/analysis/PrismGaussQuadrature.hpp b/src/analysis/PrismGaussQuadrature.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2750d76c29894fd960b89afbfd29ec908c54d03b
--- /dev/null
+++ b/src/analysis/PrismGaussQuadrature.hpp
@@ -0,0 +1,38 @@
+#ifndef PRISM_GAUSS_QUADRATURE_HPP
+#define PRISM_GAUSS_QUADRATURE_HPP
+
+#include <analysis/QuadratureFormula.hpp>
+
+/**
+ * Defines Gauss quadrature on the reference prism element
+ *
+ * \note formulae are provided by
+ *
+ * 'High-order symmetric cubature rules for tetrahedra and pyramids'
+ * Jan Jaśkowiec & N. Sukumar (2020).
+ */
+class PrismGaussQuadrature final : public QuadratureFormula<3>
+{
+ public:
+  constexpr static size_t max_degree = 20;
+
+ private:
+  void _buildPointAndWeightLists(const size_t degree);
+
+ public:
+  PrismGaussQuadrature(PrismGaussQuadrature&&)      = default;
+  PrismGaussQuadrature(const PrismGaussQuadrature&) = default;
+
+ private:
+  friend class QuadratureManager;
+
+  explicit PrismGaussQuadrature(const size_t degree) : QuadratureFormula<3>(QuadratureType::Gauss)
+  {
+    this->_buildPointAndWeightLists(degree);
+  }
+
+  PrismGaussQuadrature()  = delete;
+  ~PrismGaussQuadrature() = default;
+};
+
+#endif   // PRISM_GAUSS_QUADRATURE_HPP
diff --git a/src/analysis/PyramidGaussQuadrature.cpp b/src/analysis/PyramidGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..48eb4ec3421f1f6498420c9c851994520dc1b486
--- /dev/null
+++ b/src/analysis/PyramidGaussQuadrature.cpp
@@ -0,0 +1,978 @@
+#include <analysis/PyramidGaussQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+void
+PyramidGaussQuadrature::_buildPointAndWeightLists(const size_t degree)
+{
+  using R3 = TinyVector<3>;
+
+  struct Descriptor
+  {
+    int id;
+    double weight;
+    std::vector<double> lambda_list;
+  };
+
+  auto fill_quadrature_points = [](auto descriptor_list, auto& point_list, auto& weight_list) {
+    Assert(point_list.size() == weight_list.size());
+
+    size_t k = 0;
+    for (size_t i = 0; i < descriptor_list.size(); ++i) {
+      const auto [id, unit_weight, value_list] = descriptor_list[i];
+
+      const double w = (4. / 3) * unit_weight;
+
+      switch (id) {
+      case 1: {
+        Assert(value_list.size() == 1);
+        const double z = value_list[0];
+
+        point_list[k]  = R3{0, 0, z};
+        weight_list[k] = w;
+        ++k;
+        break;
+      }
+      case 2: {
+        Assert(value_list.size() == 2);
+        const double a = value_list[0];
+        const double z = value_list[1];
+
+        point_list[k + 0] = R3{+a, 0, z};
+        point_list[k + 1] = R3{-a, 0, z};
+        point_list[k + 2] = R3{0, +a, z};
+        point_list[k + 3] = R3{0, -a, z};
+
+        for (size_t l = 0; l < 4; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 4;
+        break;
+      }
+      case 3: {
+        Assert(value_list.size() == 2);
+        const double a = value_list[0];
+        const double z = value_list[1];
+
+        point_list[k + 0] = R3{+a, +a, z};
+        point_list[k + 1] = R3{+a, -a, z};
+        point_list[k + 2] = R3{-a, +a, z};
+        point_list[k + 3] = R3{-a, -a, z};
+
+        for (size_t l = 0; l < 4; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 4;
+        break;
+      }
+      case 4: {
+        Assert(value_list.size() == 3);
+        const double a = value_list[0];
+        const double b = value_list[1];
+        const double z = value_list[2];
+
+        point_list[k + 0] = R3{+a, +b, z};
+        point_list[k + 1] = R3{+a, -b, z};
+        point_list[k + 2] = R3{-a, +b, z};
+        point_list[k + 3] = R3{-a, -b, z};
+        point_list[k + 4] = R3{+b, +a, z};
+        point_list[k + 5] = R3{-b, +a, z};
+        point_list[k + 6] = R3{+b, -a, z};
+        point_list[k + 7] = R3{-b, -a, z};
+
+        for (size_t l = 0; l < 8; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 8;
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("invalid quadrature id");
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+  };
+
+  switch (degree) {
+  case 0:
+  case 1: {
+    constexpr size_t nb_points = 1;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.000000000000000e+00, {+2.500000000000000e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 2: {
+    constexpr size_t nb_points = 5;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 2.798666789016337e-01, {+5.606322125356171e-01}},
+       Descriptor{3, 1.800333302745916e-01, {+5.269974873671749e-01, +1.292784570090256e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 3: {
+    constexpr size_t nb_points = 6;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.534506474854593e-01, {+3.032132711145601e-02}},
+       Descriptor{1, 2.613312220748051e-01, {+5.656071879789744e-01}},
+       Descriptor{3, 1.463045326099339e-01, {+5.845963663947116e-01, +1.666666666666667e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 4: {
+    constexpr size_t nb_points = 10;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 2.068834025895523e-01, {+1.251369531087465e-01}},
+       Descriptor{1, 1.137418831706419e-01, {+6.772327888861374e-01}},
+       Descriptor{2, 1.063245878893255e-01, {+6.505815563982326e-01, +3.223841495782137e-01}},
+       Descriptor{3, 6.351909067062594e-02, {+6.579669971216900e-01, +3.924828389881535e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 5: {
+    constexpr size_t nb_points = 15;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 6.773442693037113e-02, {+7.307094695547904e-01}},
+       Descriptor{1, 6.470893518150579e-02, {+6.197232858190588e-03}},
+       Descriptor{1, 1.772715490151452e-01, {+2.684458095343137e-01}},
+       Descriptor{2, 5.910777216655192e-02, {+7.534406130793294e-01, +1.250000000000000e-01}},
+       Descriptor{3, 6.537546219121122e-02, {+4.171520024257513e-01, +4.218217110028595e-01}},
+       Descriptor{3, 4.808803786048134e-02, {+6.740225164778704e-01, +6.579572180745927e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 6: {
+    constexpr size_t nb_points = 23;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.023699419233705e-01, {+1.335312170632148e-01}},
+       Descriptor{1, 2.544552509057920e-02, {+8.083918187874604e-01}},
+       Descriptor{1, 1.074435834226933e-01, {+3.784035206635531e-01}},
+       Descriptor{2, 3.715744178992644e-02, {+4.210459518278233e-01, +5.563577402280808e-01}},
+       Descriptor{2, 3.663269740345384e-02, {+8.358409250652439e-01, +9.682668434012107e-02}},
+       Descriptor{3, 7.134885171305939e-02, {+5.134178134130217e-01, +2.554780750374050e-01}},
+       Descriptor{3, 8.659461394440064e-03, {+8.719795336426682e-01, +3.348911098405843e-02}},
+       Descriptor{3, 3.738678508995950e-02, {+4.773315577677307e-01, +2.776222122928558e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 7: {
+    constexpr size_t nb_points = 31;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.005139817749384e-01, {+3.936504852592841e-01}},
+       Descriptor{1, 1.571901760701542e-02, {+8.386341427229903e-01}},
+       Descriptor{1, 2.499658963028166e-02, {+1.985131073852604e-05}},
+       Descriptor{2, 2.871093750000000e-02, {+6.172133998483676e-01, +3.333333333333333e-01}},
+       Descriptor{2, 2.669175929300292e-02, {+8.640987597877147e-01, +6.666666666666667e-02}},
+       Descriptor{3, 3.572750182264944e-02, {+5.248875603037457e-01, +2.904549108425410e-01}},
+       Descriptor{3, 2.951904528668866e-02, {+2.541968221946381e-01, +6.054783556814159e-01}},
+       Descriptor{3, 6.594160872648229e-02, {+3.540511188101694e-01, +1.293188463105600e-01}},
+       Descriptor{3, 1.333274388639106e-02, {+6.142719454511971e-01, +1.008633926811357e-04}},
+       Descriptor{3, 1.476900623172677e-02, {+8.028224862699490e-01, +8.012951317750569e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 8: {
+    constexpr size_t nb_points = 47;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 5.595524252285600e-02, {+7.395194949759915e-02}},
+       Descriptor{1, 6.694668391641566e-02, {+4.806418077857804e-01}},
+       Descriptor{1, 4.752523069833954e-03, {+8.978770012649402e-01}},
+       Descriptor{2, 1.488586682102477e-02, {+2.536785615782182e-01, +7.039949439220020e-01}},
+       Descriptor{2, 2.455698624881831e-02, {+7.102737577690728e-01, +1.599976229101533e-01}},
+       Descriptor{2, 1.608138988371910e-02, {+6.364336235983890e-01, +3.474084640816018e-01}},
+       Descriptor{2, 1.442915622061931e-02, {+6.233792819622643e-01, +1.127682420195144e-02}},
+       Descriptor{3, 2.488836268558412e-02, {+5.122596817100590e-01, +6.351022006373874e-02}},
+       Descriptor{3, 3.016542061786949e-02, {+3.796590137942965e-01, +4.628422688700697e-01}},
+       Descriptor{3, 1.825943823062004e-02, {+6.914008694052951e-01, +1.917713050993898e-01}},
+       Descriptor{3, 4.052705114084869e-03, {+8.674219079854986e-01, +1.630913438364360e-02}},
+       Descriptor{3, 5.109969513593878e-02, {+3.171232262910623e-01, +2.368198703013063e-01}},
+       Descriptor{4, 9.833683332222407e-03, {+8.937479716183564e-01, +4.052832634656467e-01, +5.005997974535450e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 9: {
+    constexpr size_t nb_points = 62;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 3.517982458221282e-03, {+9.033806033428579e-01}},
+       Descriptor{1, 4.216520928063209e-02, {+5.649644435959712e-01}},
+       Descriptor{2, 8.521043257455639e-03, {+2.199358452633800e-01, +7.485612463519468e-01}},
+       Descriptor{2, 2.087072229798968e-02, {+2.558015573793007e-01, +1.346997662955544e-01}},
+       Descriptor{2, 1.073969585101612e-02, {+8.739242228416501e-01, +5.467616134251216e-02}},
+       Descriptor{2, 2.521321637389669e-02, {+6.206970648203557e-01, +1.894626948405005e-01}},
+       Descriptor{2, 1.961258547003659e-02, {+4.808872023980156e-01, +2.248423582708249e-02}},
+       Descriptor{2, 1.643323197880765e-02, {+5.476633755989256e-01, +4.134082258927628e-01}},
+       Descriptor{3, 1.830589291063876e-02, {+3.171678212066174e-01, +5.507723363912053e-01}},
+       Descriptor{3, 1.130841811263377e-02, {+5.811421463327766e-01, +3.191414755591298e-01}},
+       Descriptor{3, 2.507245299443831e-02, {+5.230813443611307e-01, +8.935238781868592e-02}},
+       Descriptor{3, 4.419409904347655e-03, {+8.727886385585986e-01, +6.151583405729108e-02}},
+       Descriptor{3, 4.065272607298719e-02, {+2.744783026131462e-01, +3.264219851596609e-01}},
+       Descriptor{4, 1.219793270502279e-02, {+4.578740192946059e-01, +7.687455968654898e-01, +1.755724204367565e-01}},
+       Descriptor{4, 6.516970715496498e-03, {+5.294592086619503e-01, +8.651632950329239e-01, +1.570840570495663e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 10: {
+    constexpr size_t nb_points = 80;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 2.226887246752692e-03, {+9.176665489678424e-01}},
+       Descriptor{1, 1.676862119517167e-02, {+7.090093740468727e-01}},
+       Descriptor{1, 5.218524659101940e-02, {+2.271396740104223e-01}},
+       Descriptor{1, 2.912684113372536e-02, {+6.874011069480644e-02}},
+       Descriptor{2, 2.061250827144200e-02, {+6.542017017611934e-01, +1.239254499739905e-01}},
+       Descriptor{2, 1.001318317045432e-02, {+7.663392799174616e-01, +2.096299791450872e-02}},
+       Descriptor{2, 3.805894719789539e-03, {+8.042105610181555e-01, +1.657731354676645e-01}},
+       Descriptor{2, 1.603356460939568e-02, {+2.171975788362678e-01, +4.934052093395356e-01}},
+       Descriptor{2, 4.210573985824022e-03, {+2.093734735103665e-01, +7.896976713483934e-01}},
+       Descriptor{2, 8.129732309410973e-03, {+5.283070905328926e-01, +4.716928890485879e-01}},
+       Descriptor{3, 1.345646795638387e-02, {+3.468149019049753e-01, +2.011394567580291e-02}},
+       Descriptor{3, 1.016376299548378e-02, {+5.423477423323926e-01, +3.682210199892779e-01}},
+       Descriptor{3, 2.981645762492043e-02, {+4.296576954455671e-01, +1.266159486770112e-01}},
+       Descriptor{3, 8.657127834237414e-03, {+7.187141460172495e-01, +2.433819982878694e-02}},
+       Descriptor{3, 9.531999164931240e-03, {+7.624400857874878e-01, +1.270144250256904e-01}},
+       Descriptor{3, 7.139582674758052e-04, {+9.642735008631930e-01, +1.067692573406055e-02}},
+       Descriptor{3, 1.519293397619959e-02, {+2.789738869874377e-01, +5.970429731303039e-01}},
+       Descriptor{4, 1.008533639437561e-02, {+7.312452983523516e-01, +3.711506789000326e-01, +2.175616631549504e-01}},
+       Descriptor{4, 5.859338229960730e-03, {+9.219480705797174e-01, +4.196652283339353e-01, +4.137857207280887e-02}},
+       Descriptor{4, 2.134779341185569e-02, {+1.995426151915722e-01, +4.434009744724249e-01, +3.261536572399062e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 11: {
+    constexpr size_t nb_points = 103;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.286363653695535e-02, {+6.929563964750883e-01}},
+       Descriptor{1, 2.595721225427735e-03, {+9.110152344627592e-01}},
+       Descriptor{1, 8.176385700475526e-03, {+2.503292907254847e-01}},
+       Descriptor{2, 5.119414398042891e-03, {+3.095914411121506e-01, +5.040798390564714e-02}},
+       Descriptor{2, 6.846996666462350e-03, {+7.699392208102979e-01, +1.178348626907169e-02}},
+       Descriptor{2, 2.021405278036776e-02, {+7.002703811744613e-01, +1.010394090255615e-01}},
+       Descriptor{2, 1.240017518258633e-02, {+4.609503542907282e-01, +3.427275760580460e-01}},
+       Descriptor{2, 1.050060859922616e-02, {+2.992893821695951e-01, +6.286989821677651e-01}},
+       Descriptor{2, 1.207523991498537e-02, {+5.911494375715570e-01, +2.582284852308509e-01}},
+       Descriptor{2, 7.945494399371682e-03, {+1.861308147704910e-02, +4.358344954832800e-01}},
+       Descriptor{2, 8.979308019639792e-03, {+1.603429097546466e-01, +2.192831941429789e-01}},
+       Descriptor{3, 4.328610077172669e-03, {+1.445075355444747e-01, +7.879743305346877e-01}},
+       Descriptor{3, 9.591505938503333e-03, {+5.936945097967551e-01, +3.089295886910713e-01}},
+       Descriptor{3, 8.458896075424903e-03, {+2.749014180295009e-01, +1.781185684646454e-02}},
+       Descriptor{3, 2.093917618186587e-02, {+2.968007724724139e-01, +1.112703360898260e-01}},
+       Descriptor{3, 1.007335618421271e-02, {+5.984926370656903e-01, +2.444716310077481e-02}},
+       Descriptor{3, 1.474935950898293e-02, {+6.103141530732866e-01, +1.204187551930976e-01}},
+       Descriptor{3, 4.054320200259096e-03, {+3.734502308185636e-01, +5.775253255524150e-01}},
+       Descriptor{3, 1.768012056788626e-02, {+2.624599146327740e-01, +4.916980190881633e-01}},
+       Descriptor{3, 2.349367280688882e-02, {+3.686063571979057e-01, +2.631132571277226e-01}},
+       Descriptor{3, 3.003553459527201e-03, {+8.702629344236114e-01, +1.908681361607608e-02}},
+       Descriptor{3, 4.330579718905655e-03, {+8.102574500242316e-01, +1.169261992676211e-01}},
+       Descriptor{4, 7.882977643914898e-03, {+7.896765746747297e-01, +3.552721383504759e-01, +1.796809539017722e-01}},
+       Descriptor{4, 5.094911855326565e-03, {+9.280503494413060e-01, +4.148421023475579e-01, +3.535559961728909e-02}},
+       Descriptor{4, 6.675422227745319e-03, {+2.331378908359926e-01, +5.562877919132911e-01, +4.146440202422456e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 12: {
+    constexpr size_t nb_points = 127;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 2.715854620871203e-02, {+4.570070042407191e-01}},
+       Descriptor{1, 4.353142424489953e-04, {+9.624848010480895e-01}},
+       Descriptor{1, 1.391222140872406e-02, {+2.656531684537741e-02}},
+       Descriptor{2, 3.872881466492529e-03, {+6.175240822879710e-01, +5.374720825773919e-03}},
+       Descriptor{2, 2.179679715936184e-03, {+9.538341781347592e-01, +3.942123626649530e-02}},
+       Descriptor{2, 1.267133485574918e-02, {+5.950300580138582e-01, +7.619184421748394e-02}},
+       Descriptor{2, 8.734641630353807e-03, {+4.353935982023290e-01, +5.246187553362018e-01}},
+       Descriptor{2, 2.891467316158506e-03, {+1.329135556120804e-01, +8.387199403114260e-01}},
+       Descriptor{2, 3.366868485115227e-03, {+7.971436348244696e-01, +2.007518977789794e-01}},
+       Descriptor{2, 1.476038031344119e-03, {+3.107829504939940e-01, +6.826546910531470e-01}},
+       Descriptor{2, 1.998444773599242e-02, {+4.689689670336921e-01, +3.171338246752144e-01}},
+       Descriptor{2, 6.189271706444372e-03, {+6.329314929065794e-02, +1.270962903467187e-01}},
+       Descriptor{3, 8.200787054788633e-03, {+1.169412447366524e-01, +6.712417429492287e-01}},
+       Descriptor{3, 5.096515464581623e-03, {+4.771085065521420e-01, +4.586280271864225e-01}},
+       Descriptor{3, 1.739298850473944e-02, {+3.479673540088846e-01, +1.193889061370197e-01}},
+       Descriptor{3, 1.948797782027694e-02, {+2.174809398989127e-01, +2.574178486652196e-01}},
+       Descriptor{3, 5.388962434670257e-03, {+6.742620754102585e-01, +2.260502558938976e-02}},
+       Descriptor{3, 6.769117314513475e-03, {+6.541531257781691e-01, +8.746307755558261e-02}},
+       Descriptor{3, 4.675876093665083e-03, {+2.604386899595714e-01, +6.714016412797261e-01}},
+       Descriptor{3, 1.838172037009153e-02, {+2.744210786837723e-01, +4.760425504887897e-01}},
+       Descriptor{3, 1.407964506457295e-02, {+5.125902273710998e-01, +2.277795581554027e-01}},
+       Descriptor{3, 2.833700634607033e-03, {+8.687887353904908e-01, +5.850272176027390e-02}},
+       Descriptor{3, 4.791372228177735e-03, {+7.118168180030836e-01, +2.205399573194206e-01}},
+       Descriptor{3, 9.906806338191463e-03, {+3.675456953238826e-01, +2.548724290196442e-02}},
+       Descriptor{4, 5.355743630233014e-03, {+8.489693748245684e-01, +5.120364631720311e-01, +1.114583653780480e-01}},
+       Descriptor{4, 8.168185107182221e-04, {+9.738540313599703e-01, +7.467034094055778e-01, +3.392208246799127e-03}},
+       Descriptor{4, 8.541753721892767e-03, {+3.230240551801264e-01, +6.255561325380413e-01, +3.272515164405167e-01}},
+       Descriptor{4, 1.018769584471249e-02, {+1.945007634528372e-01, +6.980595936427326e-01, +1.480516686189832e-01}},
+       Descriptor{4, 5.723677926726618e-03, {+3.289488538718914e-01, +8.538315289662523e-01, +2.487563023274973e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 13: {
+    constexpr size_t nb_points = 152;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.203669565577547e-03, {+9.297400586425600e-01}},
+       Descriptor{1, 1.890734566258640e-02, {+5.838703604447901e-01}},
+       Descriptor{1, 2.412883548353303e-02, {+1.094245815067315e-01}},
+       Descriptor{1, 1.430958560741041e-02, {+3.004359812964663e-01}},
+       Descriptor{2, 1.462233467606358e-02, {+4.122389208832898e-01, +4.035176514416959e-01}},
+       Descriptor{2, 1.310471335067461e-03, {+9.448240879795888e-01, +5.541396470307911e-03}},
+       Descriptor{2, 1.315348594597115e-03, {+1.501230655029512e-01, +8.497929440328683e-01}},
+       Descriptor{2, 6.041729387793333e-03, {+3.633828357756222e-01, +6.071950436217343e-01}},
+       Descriptor{2, 8.289343691958597e-03, {+3.289712237345070e-01, +2.033792783786767e-02}},
+       Descriptor{2, 1.293341373858936e-02, {+5.570493589160862e-01, +1.060119987373025e-01}},
+       Descriptor{2, 5.638854191543832e-03, {+7.106035443742125e-01, +2.491041991532026e-01}},
+       Descriptor{2, 4.504991398813604e-03, {+8.853174870868126e-01, +8.107956240970675e-02}},
+       Descriptor{3, 3.636184206108946e-03, {+4.888475450384162e-01, +9.508151681060791e-03}},
+       Descriptor{3, 3.888421168021828e-03, {+4.503177245667275e-01, +4.881936050921409e-01}},
+       Descriptor{3, 4.023653208263601e-03, {+6.587295691554786e-01, +2.769083115660864e-01}},
+       Descriptor{3, 9.559019492448712e-03, {+6.258827191179884e-01, +1.224809382042812e-01}},
+       Descriptor{3, 1.938656716858041e-02, {+2.517896003319362e-01, +2.232305727986451e-01}},
+       Descriptor{3, 9.687548837381962e-04, {+9.133819128044913e-01, +1.207165002118426e-02}},
+       Descriptor{3, 5.022143502861996e-03, {+1.111786164172054e-01, +7.551872471669051e-01}},
+       Descriptor{3, 1.448769412031482e-02, {+3.781492498737534e-01, +8.424825204838618e-02}},
+       Descriptor{3, 4.599764313830424e-03, {+2.314619569157776e-01, +6.318857723177680e-01}},
+       Descriptor{3, 1.107636061349879e-02, {+1.859866728257666e-01, +3.989297765923411e-01}},
+       Descriptor{3, 4.692602004327276e-03, {+7.294149054289296e-01, +2.502635713295603e-02}},
+       Descriptor{3, 2.851949824498801e-03, {+8.041924177792682e-01, +1.203800076513554e-01}},
+       Descriptor{3, 1.323033835935081e-02, {+4.703608283841747e-01, +2.927035935703978e-01}},
+       Descriptor{3, 1.119723439728825e-03, {+2.753565752001155e-01, +7.074274892155881e-01}},
+       Descriptor{3, 9.182464621381196e-03, {+2.584044366257879e-01, +5.283274650941000e-01}},
+       Descriptor{4, 4.564906537010502e-03, {+7.745630168641912e-01, +4.397893897194206e-01, +1.953840901290468e-01}},
+       Descriptor{4, 5.475034834428800e-03, {+7.201414655134084e-01, +2.217479044308266e-01, +2.260035188498128e-02}},
+       Descriptor{4, 9.922612076457428e-03, {+1.750984955638136e-01, +5.856079751486080e-01, +2.160852053363709e-01}},
+       Descriptor{4, 1.103829121564573e-03, {+9.376254731805550e-01, +7.398267532513132e-01, +6.232437306564607e-02}},
+       Descriptor{4, 2.362282939586705e-03, {+9.258882242137035e-01, +5.000209314552113e-01, +1.929314718254384e-02}},
+       Descriptor{4, 6.306665849348717e-03, {+2.682073921041632e-01, +5.618519868919964e-01, +4.010246655048774e-01}},
+       Descriptor{4, 6.754925131024099e-03, {+3.620795943973790e-01, +7.924562807098640e-01, +9.200529105614789e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 14: {
+    constexpr size_t nb_points = 184;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 6.009828410914801e-04, {+9.455096078380157e-01}},
+       Descriptor{1, 1.255487583666288e-02, {+6.138243564839928e-01}},
+       Descriptor{1, 2.319377258130698e-02, {+2.454049718059621e-01}},
+       Descriptor{1, 1.905556551345681e-02, {+4.183282539085985e-01}},
+       Descriptor{2, 1.191795956205729e-02, {+4.218261367348597e-01, +4.017536316056771e-01}},
+       Descriptor{2, 3.867184412332806e-03, {+8.465613435886886e-02, +8.958821481037450e-02}},
+       Descriptor{2, 1.923763095174742e-03, {+9.516713196592316e-01, +3.128290548598175e-02}},
+       Descriptor{2, 1.234179804493060e-03, {+1.221288178114563e-01, +8.656263522240111e-01}},
+       Descriptor{2, 4.716648039115899e-03, {+3.459755786873036e-01, +5.615491563602975e-01}},
+       Descriptor{2, 5.134044973384534e-03, {+3.250020921247040e-01, +1.398939848661482e-02}},
+       Descriptor{2, 1.154564746408180e-03, {+8.527465617518474e-01, +1.655718296284047e-03}},
+       Descriptor{2, 1.181264118995304e-02, {+6.382775647715264e-01, +6.493816225859560e-02}},
+       Descriptor{2, 2.436937834631011e-03, {+6.723114741458501e-01, +3.226524499585241e-01}},
+       Descriptor{2, 2.442180393951459e-03, {+8.537496059510902e-01, +1.428460235322913e-01}},
+       Descriptor{2, 2.760272065188771e-03, {+3.235182852814910e-01, +6.650982181901243e-01}},
+       Descriptor{2, 1.491875937488001e-02, {+4.969751483237657e-01, +1.927846766563645e-01}},
+       Descriptor{3, 8.607885480520022e-03, {+5.041706295584495e-01, +5.324772752904031e-02}},
+       Descriptor{3, 9.201054026224215e-04, {+6.385320288130391e-01, +3.499922479913266e-01}},
+       Descriptor{3, 3.506580713358855e-03, {+7.643224482486065e-01, +1.609611231580418e-01}},
+       Descriptor{3, 1.184517921997258e-02, {+5.613330619383329e-01, +1.603998892409423e-01}},
+       Descriptor{3, 1.517200950119656e-02, {+2.878680539515507e-01, +1.373276390573706e-01}},
+       Descriptor{3, 7.988570964665479e-04, {+9.016308382757633e-01, +1.032358209500777e-02}},
+       Descriptor{3, 3.465322056721376e-03, {+9.459747575878658e-02, +7.677917941640774e-01}},
+       Descriptor{3, 5.649017528916505e-03, {+2.627574405731676e-01, +4.943554173327801e-02}},
+       Descriptor{3, 3.144992576543379e-03, {+3.864091536381419e-01, +5.560266141216673e-01}},
+       Descriptor{3, 1.042467052306092e-02, {+2.282300577985542e-01, +4.962791997555249e-01}},
+       Descriptor{3, 1.353353344199375e-03, {+7.057872269283543e-01, +2.008807154101613e-03}},
+       Descriptor{3, 6.863946049178268e-04, {+9.147875524427646e-01, +4.956231677804342e-02}},
+       Descriptor{3, 8.488466463152199e-03, {+5.088306451368273e-01, +3.463558696873174e-01}},
+       Descriptor{3, 1.389764713997917e-03, {+2.183390551054974e-01, +7.454288363105724e-01}},
+       Descriptor{3, 5.646871721661347e-03, {+2.052421004307298e-01, +6.415316471289261e-01}},
+       Descriptor{3, 1.822286920356053e-02, {+2.910901390165372e-01, +3.062273833527194e-01}},
+       Descriptor{3, 4.755535844463758e-03, {+7.486540248046429e-01, +4.936880562940795e-02}},
+       Descriptor{4, 3.308393786732543e-03, {+7.378246466058306e-01, +4.928495648091606e-01, +2.369940069990296e-01}},
+       Descriptor{4, 4.070045103160022e-03, {+8.589060620098202e-01, +3.738196822630493e-01, +2.776225858911670e-02}},
+       Descriptor{4, 8.268489304502566e-03, {+2.243615064922758e-01, +6.317949456704877e-01, +2.592830550287851e-01}},
+       Descriptor{4, 2.221378811027735e-03, {+9.001757758936474e-01, +6.261867885017652e-01, +7.806621986847780e-02}},
+       Descriptor{4, 5.341491722009947e-04, {+9.789501849840820e-01, +6.256578399323836e-01, +4.430014519257858e-03}},
+       Descriptor{4, 4.879462347378159e-03, {+2.560758373363789e-01, +5.119481066576588e-01, +4.542857276885983e-01}},
+       Descriptor{4, 7.897297568864630e-03, {+2.974176206871381e-01, +7.755395865083232e-01, +1.176110467333258e-01}},
+       Descriptor{4, 2.696628566117115e-03, {+2.907965956609688e-01, +6.192615227882655e-01, +7.834837395674860e-03}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 15: {
+    constexpr size_t nb_points = 234;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 4.253301554860054e-04, {+9.516784060639266e-01}},
+       Descriptor{1, 1.525748792328103e-02, {+2.989009214424757e-01}},
+       Descriptor{2, 5.280176625857056e-03, {+7.001403574239718e-01, +1.037252232620472e-01}},
+       Descriptor{2, 2.446394589904036e-03, {+9.193631484833984e-01, +6.052669044962753e-02}},
+       Descriptor{2, 9.737060312972353e-04, {+1.101334889442361e-01, +8.776707213450818e-01}},
+       Descriptor{2, 7.776124953823158e-03, {+4.465008567015498e-01, +3.306143190884451e-01}},
+       Descriptor{2, 2.676970927679192e-03, {+8.020538575801487e-01, +1.891305716440682e-01}},
+       Descriptor{2, 6.190812842666219e-03, {+5.301512452732464e-01, +1.554979319555147e-01}},
+       Descriptor{2, 6.561865616728806e-04, {+9.732273442892753e-01, +4.905110329399738e-03}},
+       Descriptor{2, 6.569940566182691e-03, {+5.055718663406419e-01, +6.238803484862593e-02}},
+       Descriptor{2, 2.673087432145158e-03, {+4.618938260051262e-02, +5.328414641791853e-01}},
+       Descriptor{2, 1.759495302160485e-03, {+6.230136406382800e-01, +3.769559380708412e-01}},
+       Descriptor{2, 3.312857608561831e-03, {+2.950931045827234e-01, +6.814825140552475e-01}},
+       Descriptor{2, 7.510345065720940e-03, {+4.776005857611240e-01, +2.309753566333424e-01}},
+       Descriptor{2, 8.931665290893454e-03, {+3.689801580562112e-01, +5.033566690504305e-01}},
+       Descriptor{3, 5.568418259966719e-03, {+3.723782904830595e-01, +4.235367529321636e-01}},
+       Descriptor{3, 2.649457392781329e-03, {+5.341902002288372e-01, +4.129667979379346e-01}},
+       Descriptor{3, 1.970289990554734e-03, {+6.063808134784291e-02, +7.864057957899899e-01}},
+       Descriptor{3, 5.134839039660163e-03, {+4.777068770658753e-01, +3.265377521300765e-01}},
+       Descriptor{3, 7.421925481559295e-03, {+1.987543120573730e-01, +5.291423306214975e-02}},
+       Descriptor{3, 1.644857216298594e-03, {+7.599695611037520e-01, +6.930996189193400e-03}},
+       Descriptor{3, 5.534536939100995e-03, {+5.055458420266429e-01, +1.799129130112892e-02}},
+       Descriptor{3, 3.256281564205464e-03, {+2.206482423626575e-01, +8.304773708384203e-03}},
+       Descriptor{3, 1.515060824261142e-03, {+3.440503418178477e-01, +6.178873991598769e-01}},
+       Descriptor{3, 5.911265567277567e-03, {+1.962412964100753e-01, +1.926549836686368e-01}},
+       Descriptor{3, 8.392379781416604e-04, {+9.194790158373803e-01, +1.617257265834036e-02}},
+       Descriptor{3, 4.022404010252260e-03, {+2.620925839988805e-01, +5.954881060323367e-01}},
+       Descriptor{3, 2.231695755198927e-03, {+1.744887942814471e-01, +7.673520458998934e-01}},
+       Descriptor{3, 6.156882008161937e-03, {+1.325632428580384e-01, +6.445714883912202e-01}},
+       Descriptor{3, 8.642320589201948e-03, {+3.077710707446309e-01, +2.682338927247724e-01}},
+       Descriptor{3, 7.452703387505539e-03, {+4.543788145456805e-01, +1.924966376990235e-01}},
+       Descriptor{3, 5.028803835070171e-03, {+1.820389321231959e-01, +3.545120556149344e-01}},
+       Descriptor{3, 8.796385122255454e-03, {+1.880443006828807e-01, +1.378439700992989e-01}},
+       Descriptor{3, 1.747812460887048e-03, {+8.528586603669460e-01, +8.819642588395045e-02}},
+       Descriptor{3, 9.189893838229260e-03, {+1.939535843143651e-01, +4.412634170405248e-01}},
+       Descriptor{3, 6.208073919367888e-03, {+6.362056944924487e-01, +1.812670653663255e-01}},
+       Descriptor{3, 5.683980552459499e-03, {+7.160962950323663e-01, +6.273138420630826e-02}},
+       Descriptor{3, 1.836585716232118e-03, {+7.297058862326863e-01, +2.223880047949926e-01}},
+       Descriptor{4, 2.218692647777552e-03, {+8.582850877054248e-01, +5.768403835499213e-01, +1.231132989726892e-01}},
+       Descriptor{4, 2.609887405864026e-03, {+7.633676619034022e-01, +2.116780932527140e-01, +2.653658688975024e-02}},
+       Descriptor{4, 6.168756108593601e-03, {+2.161303509635070e-01, +5.839223522583539e-01, +3.294970252944507e-01}},
+       Descriptor{4, 1.229957612267995e-03, {+9.561584299674409e-01, +6.424182794168720e-01, +2.375108271645151e-02}},
+       Descriptor{4, 1.954938784233806e-03, {+8.759221925259401e-01, +3.779756583128506e-01, +1.156156746353602e-02}},
+       Descriptor{4, 3.467639337694073e-03, {+2.445394062188940e-01, +4.671941605403201e-01, +5.047812448827769e-01}},
+       Descriptor{4, 6.770266043344008e-03, {+3.706058970009432e-01, +5.485269061480423e-01, +9.182493970820699e-02}},
+       Descriptor{4, 1.678294163556885e-03, {+6.156798284057371e-01, +7.140171587890908e-03, +1.061641469954417e-02}},
+       Descriptor{4, 7.130797590166880e-03, {+7.101327396551219e-01, +2.791894198719184e-01, +1.869924830554508e-01}},
+       Descriptor{4, 4.675472898850534e-03, {+8.258711059677443e-01, +3.568302621331758e-01, +7.551964039133613e-02}},
+       Descriptor{4, 2.534207524207739e-03, {+4.755766339064903e-01, +6.928649578239733e-01, +2.863100113252361e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 16: {
+    constexpr size_t nb_points = 285;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 2.767212020768290e-04, {+9.586035463620837e-01}},
+       Descriptor{2, 7.349442577079450e-03, {+5.187038802297479e-01, +1.406555941365744e-01}},
+       Descriptor{2, 1.487404370728813e-03, {+9.153129245676680e-01, +4.316491102619298e-02}},
+       Descriptor{2, 3.876936384211876e-03, {+1.655472656731889e-02, +3.952283264601336e-01}},
+       Descriptor{2, 1.030614824039442e-02, {+3.691213557507810e-01, +2.989012556159393e-01}},
+       Descriptor{2, 2.726731988438958e-03, {+2.060330211667108e-02, +6.870461262955434e-02}},
+       Descriptor{2, 2.446292134085587e-03, {+8.719419371010426e-01, +1.119349596786030e-01}},
+       Descriptor{2, 5.549243997617444e-03, {+3.153842078793646e-01, +5.666543504043285e-01}},
+       Descriptor{2, 5.022624174664197e-03, {+6.139955556315307e-01, +1.595773278635465e-01}},
+       Descriptor{2, 6.711117008479383e-04, {+9.765007634893953e-02, +8.922021492300448e-01}},
+       Descriptor{2, 3.168735186737549e-03, {+7.317646448078422e-01, +2.475499337151233e-01}},
+       Descriptor{2, 2.307072686856537e-03, {+8.405422094352291e-01, +1.014565417563986e-02}},
+       Descriptor{2, 2.233554340355750e-03, {+5.557460950562904e-01, +4.288551242077440e-01}},
+       Descriptor{2, 2.286856931321262e-03, {+2.643802171735182e-01, +7.164979649536070e-01}},
+       Descriptor{3, 5.838886999649284e-03, {+1.375503146864574e-01, +1.787752340257923e-01}},
+       Descriptor{3, 4.120387561185287e-03, {+5.559992997537619e-01, +2.929550108496573e-01}},
+       Descriptor{3, 1.730379481687403e-03, {+6.803760252907728e-01, +2.744752357294187e-01}},
+       Descriptor{3, 4.106809066021676e-03, {+6.898707466220132e-01, +1.571412723940531e-01}},
+       Descriptor{3, 6.354389480672592e-03, {+4.665601661638106e-01, +2.503592476644397e-01}},
+       Descriptor{3, 4.326210184433355e-04, {+9.365742707871645e-01, +2.983913639912041e-02}},
+       Descriptor{3, 3.452632929818020e-03, {+1.066846406851088e-01, +7.018965704311338e-01}},
+       Descriptor{3, 4.240714557365053e-03, {+5.636286631903044e-01, +5.534643995880605e-02}},
+       Descriptor{3, 3.405266316789942e-03, {+8.721470315267314e-02, +5.880318078594904e-01}},
+       Descriptor{3, 7.009429293023371e-03, {+2.882648291123767e-01, +3.576004004129416e-01}},
+       Descriptor{3, 3.711845099382991e-03, {+2.350324970540493e-01, +6.380849983494681e-01}},
+       Descriptor{3, 6.557699894218194e-03, {+2.450858218595137e-01, +1.118004676798527e-01}},
+       Descriptor{3, 1.699962978331957e-03, {+5.003064978621564e-01, +4.579960420164317e-01}},
+       Descriptor{3, 1.347400048357294e-03, {+5.973130190403576e-02, +8.160093965927975e-01}},
+       Descriptor{3, 3.633969504612614e-03, {+3.628596393971073e-01, +5.597294995869231e-02}},
+       Descriptor{3, 1.216194629008639e-03, {+8.316301192301562e-01, +1.257310660131593e-01}},
+       Descriptor{3, 5.495797083167567e-03, {+5.981515120789380e-01, +1.068019913435455e-01}},
+       Descriptor{3, 9.733027356279389e-04, {+3.245246862338501e-01, +6.465862007088805e-01}},
+       Descriptor{3, 7.253930032328553e-03, {+1.840043782869460e-01, +2.540941713633634e-01}},
+       Descriptor{3, 2.616000670590770e-03, {+8.068558507667803e-01, +5.465918013108818e-02}},
+       Descriptor{3, 5.483336923370839e-03, {+3.903817283942642e-01, +4.534593946231034e-01}},
+       Descriptor{3, 1.501870645161511e-03, {+1.620565672046460e-01, +7.891766078049036e-01}},
+       Descriptor{4, 2.242112763518706e-03, {+7.955779101854147e-01, +5.265131942175433e-01, +1.868978854107283e-01}},
+       Descriptor{4, 2.543570121402526e-03, {+8.200116575745190e-01, +5.876389794788515e-01, +1.494308906905272e-02}},
+       Descriptor{4, 5.361444814068662e-03, {+4.810677594905461e-01, +1.491370161533038e-01, +3.977024606563388e-01}},
+       Descriptor{4, 1.505662384876206e-03, {+9.169801723597639e-01, +6.303394356928483e-01, +6.620609897561705e-02}},
+       Descriptor{4, 1.126519980001580e-03, {+9.578199602162939e-01, +3.327783186859149e-01, +1.432824105432797e-02}},
+       Descriptor{4, 2.836972153671841e-03, {+2.122679281909628e-01, +4.214367174540187e-01, +5.509963820368152e-01}},
+       Descriptor{4, 4.234433041605569e-03, {+2.485957380035636e-01, +6.262321767993255e-01, +2.800030049831467e-01}},
+       Descriptor{4, 3.184147857413263e-03, {+5.534790138573062e-01, +1.881580592010290e-01, +2.436330465584662e-01}},
+       Descriptor{4, 4.029779987731148e-03, {+8.242282785245648e-01, +3.637620573617559e-01, +7.506931441576017e-02}},
+       Descriptor{4, 3.669736816875404e-03, {+1.460654272875972e-01, +4.426831022376114e-01, +5.795059845476017e-02}},
+       Descriptor{4, 2.488887287575317e-03, {+3.881072073502287e-01, +6.235512572087863e-01, +3.563115863132650e-01}},
+       Descriptor{4, 4.888892260572901e-04, {+9.567448428329013e-01, +7.984451750366893e-01, +5.975255115121650e-03}},
+       Descriptor{4, 3.252645155099476e-03, {+6.098484948586518e-01, +3.120515076281816e-01, +1.006650964023973e-02}},
+       Descriptor{4, 4.955059091457157e-03, {+2.484742721540007e-01, +1.340653178754635e-01, +4.761968373134181e-01}},
+       Descriptor{4, 4.570242180251667e-03, {+6.991894353535666e-01, +1.776787645087477e-01, +5.836635447459246e-02}},
+       Descriptor{4, 4.810617642413540e-03, {+3.601541528165914e-01, +4.301177602083764e-01, +1.649556291845448e-01}},
+       Descriptor{4, 5.387917947217286e-03, {+7.366874205051257e-01, +3.029873517367453e-01, +1.596398472135355e-01}},
+       Descriptor{4, 2.469280567426453e-03, {+3.142480630211401e-01, +2.522065148266495e-02, +1.331460692424767e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 17: {
+    constexpr size_t nb_points = 319;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.221682107890185e-04, {+9.722032338703692e-01}},
+       Descriptor{1, 5.639436295815340e-03, {+6.387035292668141e-01}},
+       Descriptor{1, 1.871168380432278e-03, {+6.802004320805534e-01}},
+       Descriptor{2, 8.790055406139772e-04, {+9.524140858124460e-01, +4.194980824924494e-02}},
+       Descriptor{2, 6.397368832390104e-04, {+8.275692125030436e-02, +9.014118320415641e-01}},
+       Descriptor{2, 1.919424779584283e-03, {+2.657854504312773e-01, +6.843252932276896e-01}},
+       Descriptor{2, 1.088568001837918e-03, {+8.115594002847951e-01, +8.245324867689100e-08}},
+       Descriptor{2, 5.791783484828203e-03, {+3.523279995401680e-01, +5.541739087364920e-01}},
+       Descriptor{2, 3.335679832473916e-03, {+6.030377121897242e-01, +3.625880598876809e-01}},
+       Descriptor{2, 4.839934014148432e-03, {+7.840260420215905e-01, +1.387570850346267e-01}},
+       Descriptor{2, 1.171223275540323e-02, {+4.934647219864639e-01, +2.426016748645620e-01}},
+       Descriptor{2, 7.818032837412321e-03, {+3.503875164890664e-01, +4.013077580688794e-01}},
+       Descriptor{2, 3.163898916366554e-03, {+3.364043121074268e-03, +3.815297326937233e-01}},
+       Descriptor{2, 8.136278822739700e-03, {+4.307451267906745e-01, +1.190145691544862e-01}},
+       Descriptor{2, 4.719826157526003e-03, {+5.925480128294769e-01, +4.928012549325394e-02}},
+       Descriptor{2, 9.623713493802133e-04, {+2.255068603658533e-01, +7.620639151935824e-01}},
+       Descriptor{3, 1.623787740135037e-03, {+6.397586374033618e-01, +3.115346949712161e-01}},
+       Descriptor{3, 8.713459275997466e-03, {+1.646347487963126e-01, +2.565099172621796e-01}},
+       Descriptor{3, 4.120086184663263e-03, {+4.930356331962961e-01, +7.449733603189460e-02}},
+       Descriptor{3, 5.220647228935927e-04, {+7.479128039630975e-01, +1.307885480845669e-03}},
+       Descriptor{3, 3.897164563433518e-03, {+2.625131786428597e-01, +5.285858021846196e-01}},
+       Descriptor{3, 2.229921875344444e-03, {+1.748421558986807e-01, +9.552210724735053e-03}},
+       Descriptor{3, 9.418067622583964e-03, {+2.760246878567057e-01, +3.414298990596975e-01}},
+       Descriptor{3, 6.615832230310311e-04, {+4.351255240898762e-01, +2.702292596780164e-04}},
+       Descriptor{3, 1.073942218989596e-04, {+9.530769415773807e-01, +4.692242455320396e-02}},
+       Descriptor{3, 3.031478525882112e-03, {+4.842559790519491e-01, +3.444971328350538e-01}},
+       Descriptor{3, 8.193264106692307e-04, {+2.938106184332757e-01, +6.681192301420841e-01}},
+       Descriptor{3, 4.651214490706081e-03, {+1.605720050369202e-01, +6.739128715712450e-01}},
+       Descriptor{3, 1.676067168115160e-03, {+8.101452921420736e-01, +3.437584591482228e-02}},
+       Descriptor{3, 8.257689574656562e-03, {+1.800932575491559e-01, +1.554287257829705e-01}},
+       Descriptor{3, 3.975458595443641e-03, {+5.013679940745993e-01, +2.697182873385593e-01}},
+       Descriptor{3, 5.172252054798609e-04, {+9.228440785543659e-01, +1.181428825288093e-02}},
+       Descriptor{3, 6.241363480381797e-03, {+3.207724957060760e-01, +6.366317285532444e-02}},
+       Descriptor{3, 2.499541224162367e-03, {+2.145401504974506e-02, +6.762724457801919e-02}},
+       Descriptor{3, 3.441345824053468e-03, {+6.529307874193767e-01, +2.014966460540852e-01}},
+       Descriptor{3, 3.730857641897916e-03, {+3.810920673681604e-01, +4.858674466671989e-01}},
+       Descriptor{3, 9.853795017806486e-03, {+4.141312471403079e-01, +1.907105246425098e-01}},
+       Descriptor{3, 7.367579520284493e-03, {+1.431188794593187e-01, +5.103737585320696e-01}},
+       Descriptor{3, 1.780487305606260e-03, {+6.816745760360433e-02, +8.035124551324651e-01}},
+       Descriptor{3, 2.423131685196213e-03, {+2.297724242201254e-01, +4.121355156423638e-02}},
+       Descriptor{3, 1.057340089483043e-03, {+7.821120027007296e-01, +1.776001617612250e-01}},
+       Descriptor{3, 1.450363434917984e-03, {+4.764642149060935e-01, +4.843901243932818e-01}},
+       Descriptor{3, 2.710579935745260e-03, {+6.655005676200287e-01, +4.796979480806327e-02}},
+       Descriptor{3, 1.120145668844513e-03, {+1.575537160038546e-01, +8.010812345834409e-01}},
+       Descriptor{4, 1.209171043596973e-03, {+7.476025549089913e-01, +1.335733555833585e-01, +2.410142270350212e-01}},
+       Descriptor{4, 1.624713950736176e-03, {+6.402449723756475e-01, +3.697899872598127e-01, +1.069891762813818e-02}},
+       Descriptor{4, 6.208589669146640e-03, {+2.772500272074544e-01, +6.588035927979838e-01, +2.398863138338967e-01}},
+       Descriptor{4, 1.357245778023027e-03, {+8.843596956349281e-01, +7.690144551026831e-01, +8.514410320936129e-02}},
+       Descriptor{4, 7.768823018224295e-04, {+9.543208345441746e-01, +2.756529256636570e-01, +8.471105348916165e-03}},
+       Descriptor{4, 1.905388500388057e-03, {+1.961068637617923e-01, +4.881593428940726e-01, +4.956224506553615e-01}},
+       Descriptor{4, 1.836355855674232e-03, {+5.253928688656228e-01, +9.036540229831336e-01, +4.642983560786509e-02}},
+       Descriptor{4, 1.733122655911971e-03, {+7.871050645706265e-01, +5.305666908542513e-01, +1.965615185289643e-01}},
+       Descriptor{4, 8.755868144016887e-04, {+8.730512492238405e-01, +5.824961459878540e-01, +6.875723092085683e-03}},
+       Descriptor{4, 5.299199352810601e-03, {+4.953985501065977e-01, +2.191018460614218e-01, +3.867051301301994e-01}},
+       Descriptor{4, 1.909231403394242e-03, {+8.695725428246946e-01, +3.055722326033879e-01, +1.089610679849025e-01}},
+       Descriptor{4, 3.332703296489462e-03, {+8.255682153916121e-01, +1.820166521926553e-01, +4.416884971526350e-02}},
+       Descriptor{4, 1.372582739425581e-03, {+2.263367583189702e-01, +3.553849916667940e-01, +6.248925484789940e-01}},
+       Descriptor{4, 4.405628951728082e-03, {+5.513678656050841e-01, +7.486875952236592e-01, +1.172732959999338e-01}},
+       Descriptor{4, 6.902657704569894e-03, {+6.416804127385760e-01, +2.589654916989230e-01, +1.195843625945489e-01}},
+       Descriptor{4, 2.027344695276262e-03, {+4.862393601034299e-01, +1.128685901856588e-01, +1.562091696416078e-02}},
+       Descriptor{4, 2.046435953340190e-03, {+4.272869183745079e-01, +6.283616414580556e-01, +3.513262055700426e-01}},
+       Descriptor{4, 3.453264166213793e-04, {+9.787077639713484e-01, +7.341055271589447e-01, +1.323405399603627e-02}},
+       Descriptor{4, 2.425089503329792e-03, {+7.058505258664157e-01, +4.135891617947305e-01, +2.986233869137579e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 18: {
+    constexpr size_t nb_points = 357;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.340052013254401e-04, {+9.689131877314755e-01}},
+       Descriptor{2, 6.532829236534353e-03, {+7.386927583195001e-01, +1.121808531538677e-01}},
+       Descriptor{2, 2.462677829860450e-03, {+7.680665730295287e-01, +2.095203731422156e-01}},
+       Descriptor{2, 2.362663198775904e-03, {+3.167394480989801e-01, +4.504333920940983e-01}},
+       Descriptor{2, 2.412846260195942e-03, {+6.187757221807219e-01, +3.671380924150245e-01}},
+       Descriptor{2, 5.639399189884141e-03, {+3.723529440295544e-01, +5.149694555513888e-01}},
+       Descriptor{2, 5.308794645373922e-03, {+5.038719357078286e-01, +2.465874064866041e-01}},
+       Descriptor{2, 3.303209677609701e-03, {+7.220783650726965e-01, +3.347288009925681e-02}},
+       Descriptor{2, 7.157096872930287e-03, {+3.940311335958268e-01, +1.760491594601057e-01}},
+       Descriptor{2, 3.560209000799229e-03, {+1.181947980612448e-01, +1.262767090030246e-01}},
+       Descriptor{2, 2.869089106153775e-03, {+5.117986204426422e-01, +1.146186842907397e-02}},
+       Descriptor{2, 1.192387955426962e-03, {+9.344056049843052e-02, +8.286557181156362e-01}},
+       Descriptor{2, 2.357089773027461e-04, {+9.778630528838397e-01, +4.114776635706134e-03}},
+       Descriptor{2, 3.324152026523092e-03, {+2.689311802882549e-01, +6.643176008593026e-01}},
+       Descriptor{2, 6.442449838790662e-04, {+1.858887671719376e-01, +8.071453164684427e-01}},
+       Descriptor{3, 9.304082839731633e-04, {+8.803206212322914e-01, +6.759778909905437e-02}},
+       Descriptor{3, 6.153126385517773e-04, {+6.814521213676156e-01, +2.940343113268007e-01}},
+       Descriptor{3, 2.890748228612777e-04, {+4.969903643427416e-01, +4.966550809316037e-01}},
+       Descriptor{3, 9.579280577883828e-03, {+3.063401808222882e-01, +2.791132054901511e-01}},
+       Descriptor{3, 2.562433782912423e-03, {+5.853996645538505e-01, +7.876704807666435e-02}},
+       Descriptor{3, 1.970097108482327e-03, {+7.769439547694348e-01, +4.825635484814583e-02}},
+       Descriptor{3, 2.248538016231859e-03, {+1.869536840196541e-01, +8.812474861667117e-03}},
+       Descriptor{3, 1.783785036670776e-03, {+6.440284406129158e-01, +2.005872107121198e-02}},
+       Descriptor{3, 4.615488477875886e-04, {+1.993968440939729e-01, +7.563909496805638e-01}},
+       Descriptor{3, 3.835673525848779e-03, {+4.915166272295902e-01, +1.515550593825411e-01}},
+       Descriptor{3, 4.243233892689520e-03, {+2.197188373659789e-01, +6.084275881937196e-01}},
+       Descriptor{3, 2.977098509258866e-03, {+3.818186027529240e-01, +5.256954545937250e-01}},
+       Descriptor{3, 7.500976584211740e-03, {+1.323357443649693e-01, +4.028122600425206e-01}},
+       Descriptor{3, 2.080642319051259e-03, {+2.643490668003158e-01, +4.148931197347632e-02}},
+       Descriptor{3, 6.762965500915199e-03, {+2.625901672258577e-01, +1.466170076483845e-01}},
+       Descriptor{3, 3.005874847678541e-03, {+6.368304996885981e-01, +2.566159742055003e-01}},
+       Descriptor{3, 5.694731752763933e-03, {+5.033323003573335e-01, +2.555236724339244e-01}},
+       Descriptor{3, 3.293452280749932e-03, {+4.305306648769874e-01, +3.698185671948173e-01}},
+       Descriptor{3, 3.193430746935616e-03, {+4.412117799623165e-01, +1.554708555805914e-02}},
+       Descriptor{3, 3.007859639579343e-03, {+1.233852653491083e-01, +4.951092940950103e-02}},
+       Descriptor{3, 6.210180814260240e-03, {+2.819646674678744e-01, +4.572992412719536e-01}},
+       Descriptor{3, 6.903801293936010e-03, {+1.230206594250918e-01, +2.586539281154879e-01}},
+       Descriptor{3, 1.006183863763346e-04, {+9.699534085616927e-01, +1.471561063742728e-02}},
+       Descriptor{3, 1.356307828233825e-04, {+1.536759742301584e-01, +8.430293850954313e-01}},
+       Descriptor{3, 7.789376721955394e-04, {+3.232491600294373e-01, +6.422131631619533e-01}},
+       Descriptor{3, 2.674414703364759e-03, {+7.979061002341173e-02, +7.081546894975584e-01}},
+       Descriptor{3, 4.775459413065313e-04, {+5.572751587573097e-02, +9.075661676644768e-01}},
+       Descriptor{3, 7.406610401180713e-04, {+8.040718007941732e-01, +1.580109891260832e-01}},
+       Descriptor{3, 5.291370439452658e-03, {+1.063079492932114e-01, +5.586401442375718e-01}},
+       Descriptor{3, 1.500664611055249e-03, {+1.496811655200803e-01, +7.655341121253637e-01}},
+       Descriptor{3, 2.626273999362915e-03, {+7.217980235897704e-01, +1.203862817704082e-01}},
+       Descriptor{4, 1.065436899176937e-03, {+8.179717082248776e-01, +6.432021548505883e-01, +1.654004245581700e-01}},
+       Descriptor{4, 6.080722561497974e-03, {+4.547777230252971e-01, +1.663641899799587e-01, +7.191647566286845e-02}},
+       Descriptor{4, 5.310023010585634e-03, {+2.207750902147102e-01, +6.491941928630117e-01, +2.436936622868156e-01}},
+       Descriptor{4, 2.020033424832363e-03, {+8.584914437706522e-01, +2.644232091625772e-01, +3.997911405124788e-02}},
+       Descriptor{4, 2.164703930837454e-03, {+1.908724705162439e-01, +4.565598584631854e-01, +5.240352269645050e-01}},
+       Descriptor{4, 4.353306989152711e-03, {+3.778781661386005e-01, +6.640629325475310e-01, +6.332251756914624e-02}},
+       Descriptor{4, 6.863403477821615e-04, {+8.939555664639546e-01, +1.728332917834724e-01, +6.271673653011652e-03}},
+       Descriptor{4, 4.019067831153739e-03, {+4.099141505393519e-01, +5.003413197772252e-02, +3.461255047044217e-01}},
+       Descriptor{4, 7.912728473970523e-04, {+9.319810865442065e-01, +1.066217398606188e-01, +4.934944134498662e-02}},
+       Descriptor{4, 2.446710528967660e-03, {+2.801822060720651e-01, +8.499319598680132e-01, +1.205025919278488e-01}},
+       Descriptor{4, 1.950381902056960e-03, {+5.772816541361394e-01, +4.620142974473129e-01, +3.942812057164277e-01}},
+       Descriptor{4, 1.105915938820858e-03, {+8.445158080852770e-01, +6.175679175506062e-01, +8.380194537906345e-03}},
+       Descriptor{4, 5.030114823747663e-03, {+5.294157289381186e-01, +2.334853358468202e-01, +3.799152175151670e-01}},
+       Descriptor{4, 6.596708157003132e-04, {+9.282752180189467e-01, +6.794178627981728e-01, +6.658426856019066e-02}},
+       Descriptor{4, 6.132163978139013e-04, {+9.683651638409505e-01, +4.614425143846638e-01, +1.360996700708025e-02}},
+       Descriptor{4, 8.965714244660760e-04, {+1.674457184003009e-01, +3.143041479415947e-01, +6.737791163005959e-01}},
+       Descriptor{4, 3.639835604771190e-03, {+4.923271845449609e-01, +7.398884292540342e-01, +1.512239686451382e-01}},
+       Descriptor{4, 2.111289190280422e-03, {+8.565997422827011e-01, +5.406106561720058e-01, +5.960704457999711e-02}},
+       Descriptor{4, 5.700622245430812e-03, {+2.670227736126091e-01, +5.753139352915381e-01, +1.555725209349524e-01}},
+       Descriptor{4, 1.955327766010427e-03, {+4.150645766534223e-01, +7.173101873515257e-01, +2.646731283533919e-01}},
+       Descriptor{4, 6.238495350617601e-04, {+9.446623957916176e-01, +8.161908018659551e-01, +1.193065095103094e-02}},
+       Descriptor{4, 1.517921654020750e-03, {+7.145052262408169e-01, +3.117978322705600e-01, +7.294130256492850e-03}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 19: {
+    constexpr size_t nb_points = 418;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.287002239747240e-04, {+9.673913352370350e-01}},
+       Descriptor{1, 5.390761254300309e-03, {+5.436653246463873e-01}},
+       Descriptor{2, 6.666871057805598e-03, {+3.736572163452879e-01, +4.188756062275946e-01}},
+       Descriptor{2, 6.198011213798800e-03, {+4.375282703042845e-01, +2.977489444568584e-01}},
+       Descriptor{2, 1.196903942172590e-03, {+5.578275629324269e-01, +4.363255890435926e-01}},
+       Descriptor{2, 9.000351976243133e-03, {+2.727587922914985e-01, +1.312347586523022e-01}},
+       Descriptor{2, 4.164637105384020e-03, {+2.887241175382593e-01, +5.688216954681572e-01}},
+       Descriptor{2, 1.824497208727276e-03, {+1.127746562325076e-01, +6.623057363188606e-01}},
+       Descriptor{2, 4.523718683330509e-03, {+6.547023041522421e-01, +2.196378912459858e-01}},
+       Descriptor{2, 8.161116064814124e-03, {+2.229510128266209e-01, +2.273269633283456e-01}},
+       Descriptor{2, 1.648326828637079e-03, {+3.517668912115330e-01, +5.417515126253033e-03}},
+       Descriptor{2, 5.571492309500324e-04, {+2.069773541366567e-01, +7.929490613648335e-01}},
+       Descriptor{2, 1.118215252194827e-03, {+9.311118902772667e-01, +4.180662980331772e-02}},
+       Descriptor{2, 1.885810158761153e-03, {+8.430335844595135e-01, +1.361551806457953e-01}},
+       Descriptor{2, 2.828101226263676e-04, {+7.303948013428374e-02, +9.187547560280617e-01}},
+       Descriptor{2, 1.815863546152488e-03, {+6.780735133862854e-01, +3.019190562178010e-01}},
+       Descriptor{2, 1.247082448220326e-03, {+8.819848262866421e-01, +9.387499849089011e-03}},
+       Descriptor{2, 1.074162553027278e-03, {+3.858781168610365e-01, +6.065859845746238e-01}},
+       Descriptor{2, 2.579770777378314e-03, {+1.370888473751836e-01, +7.535348220210553e-01}},
+       Descriptor{3, 9.170154497260322e-04, {+5.255852110826873e-01, +4.408685369060898e-01}},
+       Descriptor{3, 3.068778721173566e-03, {+4.530164257058980e-01, +4.188977508155631e-01}},
+       Descriptor{3, 1.871058171917288e-03, {+6.712511632017596e-01, +1.251810432139165e-02}},
+       Descriptor{3, 1.536909390806274e-04, {+3.695141864916882e-01, +6.246014838381103e-01}},
+       Descriptor{3, 3.845290732110408e-03, {+2.883822343558625e-01, +5.439973273857342e-02}},
+       Descriptor{3, 2.243116164244068e-04, {+8.285723838946580e-01, +1.619186219316668e-01}},
+       Descriptor{3, 8.402087626133961e-04, {+6.724607008544720e-01, +2.930033138342824e-01}},
+       Descriptor{3, 3.041957674540431e-03, {+4.873668933896256e-01, +2.634265889387019e-01}},
+       Descriptor{3, 2.294841766987932e-04, {+9.503664431507984e-01, +1.127614473867753e-02}},
+       Descriptor{3, 4.182420591869638e-03, {+9.132353058612172e-02, +3.580798461499961e-01}},
+       Descriptor{3, 6.327091081505453e-03, {+1.552069555141659e-01, +4.714188671670859e-01}},
+       Descriptor{3, 7.592834005951271e-04, {+5.052806321356114e-02, +8.544640711209540e-01}},
+       Descriptor{3, 4.596836662276725e-03, {+3.978139280416619e-01, +1.293031169145601e-01}},
+       Descriptor{3, 2.766364274235799e-03, {+3.760791314064199e-01, +7.899233073306565e-02}},
+       Descriptor{3, 2.144850801510551e-03, {+1.643638267982826e-01, +1.672886065733771e-02}},
+       Descriptor{3, 5.283144486182838e-03, {+3.468892555686117e-01, +3.885050437237611e-01}},
+       Descriptor{3, 4.881052322614178e-04, {+1.191332579371944e-01, +8.475473975749326e-01}},
+       Descriptor{3, 2.120695458348499e-03, {+7.454583814188197e-01, +1.626360280018163e-01}},
+       Descriptor{3, 1.615060199482018e-03, {+7.610495209730348e-01, +7.862268330374027e-02}},
+       Descriptor{3, 6.942257092948444e-03, {+2.420812058877188e-01, +3.202806437619411e-01}},
+       Descriptor{3, 1.002683020081214e-03, {+8.445798381566891e-01, +1.382133423647079e-02}},
+       Descriptor{3, 9.207753167491697e-04, {+1.645646578166248e-01, +7.687752250435795e-01}},
+       Descriptor{3, 3.556169477445426e-03, {+2.747842347238860e-01, +5.301402408730526e-01}},
+       Descriptor{3, 2.186178713296525e-03, {+5.849919450560005e-01, +3.008923824441204e-01}},
+       Descriptor{3, 2.691651662032098e-03, {+1.489738124603387e-01, +6.167319270078593e-01}},
+       Descriptor{3, 3.016740104757321e-03, {+4.389290397617306e-01, +1.376377254881576e-02}},
+       Descriptor{3, 7.466645241550527e-04, {+8.798197120003726e-01, +7.210305012229555e-02}},
+       Descriptor{3, 2.997482336902333e-03, {+1.168533193079235e-01, +6.238118616203953e-02}},
+       Descriptor{3, 1.447847026063246e-03, {+3.428716485856084e-01, +5.855795349165888e-01}},
+       Descriptor{4, 1.072809629353361e-03, {+8.643327984186001e-01, +6.422236743536752e-01, +1.179192064779849e-01}},
+       Descriptor{4, 4.839037348378018e-03, {+5.784047959665971e-01, +1.570037147921136e-01, +1.114252341292851e-01}},
+       Descriptor{4, 2.747099059820038e-03, {+3.108799114935293e-01, +5.391600980992765e-01, +2.750267281218793e-01}},
+       Descriptor{4, 4.335016889029408e-03, {+2.047256512125053e-01, +5.650506986268603e-01, +3.472846972655921e-01}},
+       Descriptor{4, 2.718100159849547e-03, {+1.547714831454746e-01, +8.008730085913268e-01, +7.549551730236130e-02}},
+       Descriptor{4, 9.168547398610547e-04, {+9.112222485018308e-01, +3.619692710244549e-01, +3.464296962031117e-02}},
+       Descriptor{4, 6.808951019688473e-03, {+2.376393444235864e-01, +4.605447477525737e-01, +2.044152977923567e-01}},
+       Descriptor{4, 4.013856896610910e-04, {+9.302667501209977e-01, +3.215980764268125e-01, +6.972695561067982e-02}},
+       Descriptor{4, 2.155002895452285e-03, {+2.733067099141755e-01, +7.541757880282322e-01, +2.199898046581558e-01}},
+       Descriptor{4, 1.611542573023053e-03, {+6.197790831123378e-01, +4.009499952384629e-01, +3.646225494795918e-01}},
+       Descriptor{4, 2.743455266744436e-04, {+9.673179731072893e-01, +7.175030678238746e-01, +4.586334961223785e-03}},
+       Descriptor{4, 2.300082975062185e-03, {+2.810759624172646e-01, +1.446964075338812e-01, +6.664230048871306e-01}},
+       Descriptor{4, 2.945835759051771e-04, {+9.572349478971794e-01, +8.054431343183734e-01, +3.975302308577006e-02}},
+       Descriptor{4, 6.007312216772520e-04, {+9.341504389265958e-01, +5.642632183544610e-01, +3.073598825835916e-02}},
+       Descriptor{4, 2.620175037577330e-04, {+2.212443839381749e-01, +2.763723337715620e-01, +7.207735178967332e-01}},
+       Descriptor{4, 4.192832498709637e-03, {+5.144086015486090e-01, +6.711096936374128e-01, +1.403609469612069e-01}},
+       Descriptor{4, 1.736294308688135e-03, {+8.466968176319485e-01, +6.476309524361428e-01, +5.264191965632582e-02}},
+       Descriptor{4, 9.439101669434532e-04, {+5.837234015734821e-01, +7.587849158481068e-01, +2.270233248942167e-01}},
+       Descriptor{4, 2.175513608503740e-03, {+2.312548427820351e-01, +7.579578663965916e-01, +2.881326163838643e-02}},
+       Descriptor{4, 1.283394690183355e-03, {+6.535198164687628e-01, +2.118649081243290e-01, +5.321389368184961e-03}},
+       Descriptor{4, 2.726150499605700e-03, {+6.953450152489690e-01, +1.860513163719893e-01, +1.591709313067350e-01}},
+       Descriptor{4, 3.185339603850620e-03, {+8.127234831798691e-02, +5.241033074238067e-01, +4.286780869626788e-02}},
+       Descriptor{4, 2.663092866644570e-03, {+6.731624418813449e-01, +4.707459336103425e-01, +2.281921560389168e-01}},
+       Descriptor{4, 2.326668465288244e-03, {+8.297715217071255e-01, +3.845634575264398e-01, +1.149758475625305e-01}},
+       Descriptor{4, 9.734631151970315e-04, {+8.530842873329166e-01, +4.855129905435616e-01, +6.546603573639168e-03}},
+       Descriptor{4, 3.483456851399480e-03, {+4.467073185069126e-01, +1.775709599102257e-01, +4.889658061311413e-01}},
+       Descriptor{4, 4.061954216963042e-03, {+6.700967212030146e-01, +4.619994161131860e-01, +5.843691849737929e-02}},
+       Descriptor{4, 3.324559356884226e-04, {+9.767336037107472e-01, +2.679001927626974e-01, +6.104985413274207e-03}},
+       Descriptor{4, 9.232817417531915e-04, {+3.188790007212526e-01, +4.538176463136764e-01, +5.340866401557672e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 20: {
+    constexpr size_t nb_points = 489;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 5.374309731479196e-03, {+6.041261328156059e-01}},
+       Descriptor{2, 5.671734293778256e-03, {+4.816935434431252e-01, +1.283254121370188e-01}},
+       Descriptor{2, 3.248860881724051e-03, {+6.985803886987003e-01, +2.363645279387677e-01}},
+       Descriptor{2, 3.452094526682737e-03, {+5.346865795177097e-01, +3.694814396873626e-01}},
+       Descriptor{2, 6.666033444769521e-04, {+4.222838087844267e-01, +5.741352229223836e-01}},
+       Descriptor{2, 3.995792460381601e-03, {+3.068045188877912e-01, +5.709702769195857e-01}},
+       Descriptor{2, 4.992778959530078e-03, {+2.287749849768982e-01, +4.453272633438413e-01}},
+       Descriptor{2, 2.469622976420997e-03, {+5.538968741280718e-01, +1.850719926469714e-01}},
+       Descriptor{2, 7.066155754130616e-03, {+3.848922804233133e-01, +2.828189872249547e-01}},
+       Descriptor{2, 1.916980035371976e-03, {+2.571105687384414e-01, +7.453813550512145e-03}},
+       Descriptor{2, 2.387541558193644e-04, {+9.495420833471157e-01, +2.730608888531540e-03}},
+       Descriptor{2, 1.680239670130047e-04, {+9.635090648066924e-02, +8.947468307319109e-01}},
+       Descriptor{2, 9.944327373233232e-04, {+9.104493126181845e-01, +5.489260378459437e-02}},
+       Descriptor{2, 3.454941677138827e-03, {+2.683082435742811e-01, +1.789054761711333e-01}},
+       Descriptor{2, 1.377780657771690e-03, {+8.274465668629972e-01, +1.443872792953364e-01}},
+       Descriptor{2, 6.135678341459227e-05, {+3.789270083108932e-02, +9.600532902975129e-01}},
+       Descriptor{2, 9.961596076112076e-04, {+5.735205240815813e-01, +4.226237395657491e-01}},
+       Descriptor{2, 1.734688844684138e-03, {+7.423115722700304e-01, +9.373411784322374e-03}},
+       Descriptor{2, 6.408905290179002e-04, {+2.846909641320057e-01, +7.051164480862681e-01}},
+       Descriptor{2, 1.747701749929037e-03, {+1.128162795069089e-01, +8.097603763752680e-01}},
+       Descriptor{3, 5.356183098283481e-04, {+4.535676239967043e-01, +5.210700225779646e-01}},
+       Descriptor{3, 3.744128155669667e-03, {+3.336110385261160e-01, +4.762219126204001e-01}},
+       Descriptor{3, 3.648498543938937e-03, {+3.773337527822211e-01, +4.553198949867700e-02}},
+       Descriptor{3, 2.722104630492699e-04, {+3.061117491692780e-01, +6.823651677845297e-01}},
+       Descriptor{3, 4.038058627934289e-03, {+2.904460076524542e-01, +3.676651830752369e-01}},
+       Descriptor{3, 2.849455013152634e-03, {+5.914106884092367e-01, +3.140155892114704e-02}},
+       Descriptor{3, 4.946318515118939e-04, {+7.660549649135295e-01, +2.085476317987768e-01}},
+       Descriptor{3, 3.435750243515581e-03, {+4.496216926317984e-01, +3.209594399518608e-01}},
+       Descriptor{3, 7.654793642697566e-04, {+6.125691075458759e-01, +3.559054105435636e-01}},
+       Descriptor{3, 9.270297025443059e-05, {+9.651777563788078e-01, +1.963481046583392e-02}},
+       Descriptor{3, 4.440447400598151e-03, {+1.850944308523854e-01, +5.811084832537615e-01}},
+       Descriptor{3, 4.995284180274901e-03, {+1.305339914072845e-01, +2.203018355115161e-01}},
+       Descriptor{3, 2.327605643441156e-03, {+8.713330755302490e-02, +7.074337461014996e-01}},
+       Descriptor{3, 3.058461110576954e-03, {+1.162657955161678e-01, +4.975461839293467e-01}},
+       Descriptor{3, 2.716703334283396e-04, {+3.353132384174681e-02, +9.019799487990967e-01}},
+       Descriptor{3, 3.592040414552888e-03, {+6.300704464975707e-01, +1.583017481238254e-01}},
+       Descriptor{3, 4.506232324626833e-03, {+1.008155351127816e-01, +3.470348929613336e-01}},
+       Descriptor{3, 2.662059522271117e-04, {+9.712721271803038e-02, +8.772759077750505e-01}},
+       Descriptor{3, 1.722422251895809e-03, {+6.503674461202499e-01, +2.509808878874181e-01}},
+       Descriptor{3, 1.529902163082495e-03, {+7.881670555390969e-01, +1.228748706112481e-01}},
+       Descriptor{3, 4.331723455652033e-03, {+4.632610253517699e-01, +2.156955721524682e-01}},
+       Descriptor{3, 8.698495590594387e-04, {+8.742622562545352e-01, +4.316688220319717e-02}},
+       Descriptor{3, 3.166557615941483e-03, {+9.693365579654889e-02, +1.072427543090557e-01}},
+       Descriptor{3, 1.455167758465003e-03, {+5.265123436991546e-01, +3.296427782470148e-01}},
+       Descriptor{3, 1.137616034931676e-03, {+2.105801835055998e-01, +7.160068077591689e-01}},
+       Descriptor{3, 1.884904388466294e-03, {+4.834178601814259e-01, +4.197363107268692e-01}},
+       Descriptor{3, 6.049803062524013e-03, {+2.723239805399290e-01, +1.077197094023458e-01}},
+       Descriptor{3, 2.785053160390842e-04, {+8.874839070114657e-01, +9.137076099343852e-02}},
+       Descriptor{3, 1.644290914957632e-03, {+3.400434017708192e-01, +5.743322455524006e-01}},
+       Descriptor{3, 4.384669874639433e-03, {+2.465713452115888e-01, +2.965278956806727e-01}},
+       Descriptor{3, 2.508217360325574e-03, {+7.203261185246220e-01, +6.861567128774998e-02}},
+       Descriptor{4, 6.403192997163759e-04, {+8.693467247184354e-01, +6.605377674376229e-01, +1.211268750412950e-01}},
+       Descriptor{4, 2.743609819133677e-03, {+5.165700364414527e-01, +1.268114432243165e-01, +6.232922095164157e-02}},
+       Descriptor{4, 4.425132157331708e-03, {+2.141949283591463e-01, +5.351420520651734e-01, +2.887837560282615e-01}},
+       Descriptor{4, 3.793533547619605e-03, {+3.860615525036007e-01, +1.430987462436435e-01, +4.198199632798237e-01}},
+       Descriptor{4, 2.598731880891428e-03, {+3.148265223116505e-01, +5.788734905442336e-01, +3.671971633512633e-01}},
+       Descriptor{4, 1.100562071404283e-03, {+1.333776220074420e-01, +8.685734808776638e-01, +3.539982573087990e-02}},
+       Descriptor{4, 7.559451274546276e-04, {+8.933451426565783e-01, +2.970093961186454e-01, +6.894095497062928e-03}},
+       Descriptor{4, 4.164432957175231e-03, {+6.420732128180285e-01, +2.185904390936289e-01, +1.803409782254840e-01}},
+       Descriptor{4, 7.986472006319258e-04, {+9.015566925902675e-01, +2.850450105795807e-01, +8.701177937660884e-02}},
+       Descriptor{4, 1.274772831709237e-03, {+1.795078173861985e-01, +6.796606097350029e-01, +3.018403418988506e-01}},
+       Descriptor{4, 7.742072015194865e-04, {+5.287716454511575e-01, +3.752537914501156e-01, +4.637531647615307e-01}},
+       Descriptor{4, 1.079867868775378e-03, {+8.568477683810934e-01, +7.061426122653948e-01, +1.259447070049874e-02}},
+       Descriptor{4, 1.550935782403934e-03, {+2.374464971047839e-01, +9.039264484495425e-02, +6.846455484276207e-01}},
+       Descriptor{4, 4.993786208816673e-04, {+9.406598770106565e-01, +7.437511960991476e-01, +4.669618994654997e-02}},
+       Descriptor{4, 4.262366420646442e-04, {+9.654498557948427e-01, +5.581648667244832e-01, +9.979099797204329e-03}},
+       Descriptor{4, 5.244528523523384e-04, {+1.185997715939458e-01, +1.973055345489109e-01, +7.917076093806301e-01}},
+       Descriptor{4, 2.954838737142077e-03, {+4.383682874673118e-01, +6.801053445689546e-01, +2.323974680712023e-01}},
+       Descriptor{4, 1.540648448261571e-03, {+8.501501583895467e-01, +5.776785987998531e-01, +6.212941698545271e-02}},
+       Descriptor{4, 4.849861236258662e-03, {+4.097788353774976e-01, +5.992649167860331e-01, +1.025000747100843e-01}},
+       Descriptor{4, 7.495372210984812e-04, {+6.254025057545211e-01, +7.793811396398747e-01, +1.963810536401145e-01}},
+       Descriptor{4, 1.677329035907760e-03, {+1.658794771155705e-01, +6.513788165859978e-01, +3.807225396126873e-02}},
+       Descriptor{4, 2.242040914713952e-03, {+5.062559144366789e-01, +2.470826877566723e-01, +9.699938299695787e-03}},
+       Descriptor{4, 3.054238951987089e-03, {+7.429293632807329e-01, +1.682359345705867e-01, +8.712730173221997e-02}},
+       Descriptor{4, 2.363709150448725e-03, {+1.867609649526593e-02, +2.536204599781817e-01, +4.169093747605618e-02}},
+       Descriptor{4, 4.124773767916695e-03, {+2.608600527928172e-01, +4.094605558894180e-01, +1.898222236268910e-01}},
+       Descriptor{4, 2.522814373870828e-03, {+7.938808312983506e-01, +4.691881899824492e-01, +1.276217397945529e-01}},
+       Descriptor{4, 1.143364037200234e-03, {+7.930116554406030e-01, +2.986457776503003e-01, +1.955063271970715e-01}},
+       Descriptor{4, 2.785004192577059e-03, {+4.492999606983316e-01, +1.696796572740249e-01, +4.935677375430703e-01}},
+       Descriptor{4, 2.222625782566179e-03, {+7.687383145879386e-01, +3.967745055048160e-01, +3.472481178210238e-02}},
+       Descriptor{4, 2.609946044201681e-04, {+9.785997597757148e-01, +1.909470577592247e-01, +1.518094278770999e-02}},
+       Descriptor{4, 1.252174210689572e-03, {+2.135890927684384e-01, +3.604161529615568e-01, +6.154203152262100e-01}},
+       Descriptor{4, 1.279670338642788e-03, {+7.962525773875013e-01, +1.562852697636821e-01, +1.257520475253165e-01}},
+       Descriptor{4, 7.990495656729902e-04, {+9.193985656269752e-01, +4.148607451061945e-01, +4.457888016467764e-02}},
+       Descriptor{4, 7.812219859741840e-04, {+5.150733025160915e-01, +6.896293007082364e-01, +3.017078195063375e-01}},
+       Descriptor{4, 2.172588051809931e-04, {+9.636121389116856e-01, +8.595767942792535e-01, +5.771284380553639e-03}},
+       Descriptor{4, 7.595577614438183e-04, {+7.214071149093607e-01, +4.920954370020814e-01, +2.395309192181843e-03}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(PyramidGaussQuadrature::max_degree) + " on pyramids");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/analysis/PyramidGaussQuadrature.hpp b/src/analysis/PyramidGaussQuadrature.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b68c518040e39611a0f1670758f40563271abb11
--- /dev/null
+++ b/src/analysis/PyramidGaussQuadrature.hpp
@@ -0,0 +1,38 @@
+#ifndef PYRAMID_GAUSS_QUADRATURE_HPP
+#define PYRAMID_GAUSS_QUADRATURE_HPP
+
+#include <analysis/QuadratureFormula.hpp>
+
+/**
+ * Defines Gauss quadrature on the reference pyramid element
+ *
+ * \note formulae are provided by
+ *
+ * 'High-order symmetric cubature rules for tetrahedra and pyramids'
+ * Jan Jaśkowiec & N. Sukumar (2020).
+ */
+class PyramidGaussQuadrature final : public QuadratureFormula<3>
+{
+ public:
+  constexpr static size_t max_degree = 20;
+
+ private:
+  void _buildPointAndWeightLists(const size_t degree);
+
+ public:
+  PyramidGaussQuadrature(PyramidGaussQuadrature&&)      = default;
+  PyramidGaussQuadrature(const PyramidGaussQuadrature&) = default;
+
+ private:
+  friend class QuadratureManager;
+
+  explicit PyramidGaussQuadrature(const size_t degree) : QuadratureFormula<3>(QuadratureType::Gauss)
+  {
+    this->_buildPointAndWeightLists(degree);
+  }
+
+  PyramidGaussQuadrature()  = delete;
+  ~PyramidGaussQuadrature() = default;
+};
+
+#endif   // PYRAMID_GAUSS_QUADRATURE_HPP
diff --git a/src/analysis/QuadratureFormula.hpp b/src/analysis/QuadratureFormula.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c472e3556a298d14df73d1cb45a8d32c9fe6dc28
--- /dev/null
+++ b/src/analysis/QuadratureFormula.hpp
@@ -0,0 +1,63 @@
+#ifndef QUADRATURE_FORMULA_HPP
+#define QUADRATURE_FORMULA_HPP
+
+#include <algebra/TinyVector.hpp>
+#include <analysis/QuadratureType.hpp>
+#include <utils/PugsAssert.hpp>
+#include <utils/PugsMacros.hpp>
+#include <utils/SmallArray.hpp>
+
+template <size_t Dimension>
+class QuadratureFormula
+{
+ public:
+ protected:
+  QuadratureType m_type;
+  SmallArray<const TinyVector<Dimension>> m_point_list;
+  SmallArray<const double> m_weight_list;
+
+ public:
+  PUGS_INLINE size_t
+  numberOfPoints() const
+  {
+    Assert(m_point_list.size() == m_weight_list.size());
+    return m_point_list.size();
+  }
+
+  PUGS_INLINE
+  const SmallArray<const TinyVector<Dimension>>&
+  pointList() const
+  {
+    return m_point_list;
+  }
+
+  PUGS_INLINE
+  const TinyVector<Dimension>&
+  point(const size_t i) const
+  {
+    return m_point_list[i];
+  }
+
+  PUGS_INLINE
+  const SmallArray<const double>&
+  weightList() const
+  {
+    return m_weight_list;
+  }
+
+  PUGS_INLINE
+  const double&
+  weight(const size_t i) const
+  {
+    return m_weight_list[i];
+  }
+
+ protected:
+  explicit QuadratureFormula(const QuadratureType type) : m_type{type} {}
+
+ public:
+  QuadratureFormula()          = default;
+  virtual ~QuadratureFormula() = default;
+};
+
+#endif   // QUADRATURE_FORMULA_HPP
diff --git a/src/analysis/QuadratureManager.cpp b/src/analysis/QuadratureManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ca63299d184197d3a63168b87111646aec9bf5b7
--- /dev/null
+++ b/src/analysis/QuadratureManager.cpp
@@ -0,0 +1,194 @@
+#include <analysis/QuadratureManager.hpp>
+
+#include <analysis/CubeGaussQuadrature.hpp>
+#include <analysis/PrismGaussQuadrature.hpp>
+#include <analysis/PyramidGaussQuadrature.hpp>
+#include <analysis/SquareGaussQuadrature.hpp>
+#include <analysis/TensorialGaussLegendreQuadrature.hpp>
+#include <analysis/TensorialGaussLobattoQuadrature.hpp>
+#include <analysis/TetrahedronGaussQuadrature.hpp>
+#include <analysis/TriangleGaussQuadrature.hpp>
+
+QuadratureManager* QuadratureManager::s_instance{nullptr};
+
+void
+QuadratureManager::create()
+{
+  Assert(s_instance == nullptr, "QuadratureManager is already created");
+  s_instance = new QuadratureManager;
+}
+
+void
+QuadratureManager::destroy()
+{
+  Assert(s_instance != nullptr, "QuadratureManager was not created!");
+
+  delete s_instance;
+  s_instance = nullptr;
+}
+
+Array<const QuadratureFormula<1>>
+QuadratureManager::_buildLineGaussLegendreFormulaList()
+{
+  Array<QuadratureFormula<1>> line_gauss_legendre_formula_list(TensorialGaussLegendreQuadrature<1>::max_degree / 2 + 1);
+  for (size_t i = 0; i < TensorialGaussLegendreQuadrature<1>::max_degree / 2 + 1; ++i) {
+    line_gauss_legendre_formula_list[i] = TensorialGaussLegendreQuadrature<1>(2 * i + 1);
+  }
+
+  return line_gauss_legendre_formula_list;
+}
+
+Array<const QuadratureFormula<1>>
+QuadratureManager::_buildLineGaussLobattoFormulaList()
+{
+  Array<QuadratureFormula<1>> line_gauss_lobatto_formula_list(TensorialGaussLobattoQuadrature<1>::max_degree / 2 + 1);
+  for (size_t i = 0; i < TensorialGaussLobattoQuadrature<1>::max_degree / 2 + 1; ++i) {
+    line_gauss_lobatto_formula_list[i] = TensorialGaussLobattoQuadrature<1>(2 * i + 1);
+  }
+
+  return line_gauss_lobatto_formula_list;
+}
+
+Array<const QuadratureFormula<2>>
+QuadratureManager::_buildSquareGaussFormulaList()
+{
+  Array<QuadratureFormula<2>> square_gauss_formula_list(SquareGaussQuadrature::max_degree / 2 + 1);
+  for (size_t i = 0; i < SquareGaussQuadrature::max_degree / 2 + 1; ++i) {
+    square_gauss_formula_list[i] = SquareGaussQuadrature(2 * i + 1);
+  }
+
+  return square_gauss_formula_list;
+}
+
+Array<const QuadratureFormula<2>>
+QuadratureManager::_buildSquareGaussLegendreFormulaList()
+{
+  Array<QuadratureFormula<2>> square_gauss_legendre_formula_list(TensorialGaussLegendreQuadrature<2>::max_degree / 2 +
+                                                                 1);
+  for (size_t i = 0; i < TensorialGaussLegendreQuadrature<2>::max_degree / 2 + 1; ++i) {
+    square_gauss_legendre_formula_list[i] = TensorialGaussLegendreQuadrature<2>(2 * i + 1);
+  }
+
+  return square_gauss_legendre_formula_list;
+}
+
+Array<const QuadratureFormula<2>>
+QuadratureManager::_buildSquareGaussLobattoFormulaList()
+{
+  Array<QuadratureFormula<2>> square_gauss_lobatto_formula_list(TensorialGaussLobattoQuadrature<2>::max_degree / 2 + 1);
+  for (size_t i = 0; i < TensorialGaussLobattoQuadrature<2>::max_degree / 2 + 1; ++i) {
+    square_gauss_lobatto_formula_list[i] = TensorialGaussLobattoQuadrature<2>(2 * i + 1);
+  }
+
+  return square_gauss_lobatto_formula_list;
+}
+
+Array<const QuadratureFormula<2>>
+QuadratureManager::_buildTriangleGaussFormulaList()
+{
+  Array<QuadratureFormula<2>> triangle_gauss_formula_list(TriangleGaussQuadrature::max_degree);
+  for (size_t i = 0; i < TriangleGaussQuadrature::max_degree; ++i) {
+    triangle_gauss_formula_list[i] = TriangleGaussQuadrature(i + 1);
+  }
+
+  return triangle_gauss_formula_list;
+}
+
+Array<const QuadratureFormula<3>>
+QuadratureManager::_buildCubeGaussFormulaList()
+{
+  Array<QuadratureFormula<3>> cube_gauss_formula_list(CubeGaussQuadrature::max_degree / 2 + 1);
+  for (size_t i = 0; i < CubeGaussQuadrature::max_degree / 2 + 1; ++i) {
+    cube_gauss_formula_list[i] = CubeGaussQuadrature(2 * i + 1);
+  }
+
+  return cube_gauss_formula_list;
+}
+
+Array<const QuadratureFormula<3>>
+QuadratureManager::_buildCubeGaussLegendreFormulaList()
+{
+  Array<QuadratureFormula<3>> cube_gauss_legendre_formula_list(TensorialGaussLegendreQuadrature<3>::max_degree / 2 + 1);
+  for (size_t i = 0; i < TensorialGaussLegendreQuadrature<3>::max_degree / 2 + 1; ++i) {
+    cube_gauss_legendre_formula_list[i] = TensorialGaussLegendreQuadrature<3>(2 * i + 1);
+  }
+
+  return cube_gauss_legendre_formula_list;
+}
+
+Array<const QuadratureFormula<3>>
+QuadratureManager::_buildCubeGaussLobattoFormulaList()
+{
+  Array<QuadratureFormula<3>> cube_gauss_lobatto_formula_list(TensorialGaussLobattoQuadrature<3>::max_degree / 2 + 1);
+  for (size_t i = 0; i < TensorialGaussLobattoQuadrature<3>::max_degree / 2 + 1; ++i) {
+    cube_gauss_lobatto_formula_list[i] = TensorialGaussLobattoQuadrature<3>(2 * i + 1);
+  }
+
+  return cube_gauss_lobatto_formula_list;
+}
+
+Array<const QuadratureFormula<3>>
+QuadratureManager::_buildPrismGaussFormulaList()
+{
+  Array<QuadratureFormula<3>> prism_gauss_formula_list(PrismGaussQuadrature::max_degree);
+  for (size_t i = 0; i < PrismGaussQuadrature::max_degree; ++i) {
+    prism_gauss_formula_list[i] = PrismGaussQuadrature(i + 1);
+  }
+
+  return prism_gauss_formula_list;
+}
+
+Array<const QuadratureFormula<3>>
+QuadratureManager::_buildPyramidGaussFormulaList()
+{
+  Array<QuadratureFormula<3>> pyramid_gauss_formula_list(PyramidGaussQuadrature::max_degree);
+  for (size_t i = 0; i < PyramidGaussQuadrature::max_degree; ++i) {
+    pyramid_gauss_formula_list[i] = PyramidGaussQuadrature(i + 1);
+  }
+
+  return pyramid_gauss_formula_list;
+}
+
+Array<const QuadratureFormula<3>>
+QuadratureManager::_buildTetrahedronGaussFormulaList()
+{
+  Array<QuadratureFormula<3>> tetrahedron_gauss_formula_list(TetrahedronGaussQuadrature::max_degree);
+  for (size_t i = 0; i < TetrahedronGaussQuadrature::max_degree; ++i) {
+    tetrahedron_gauss_formula_list[i] = TetrahedronGaussQuadrature(i + 1);
+  }
+
+  return tetrahedron_gauss_formula_list;
+}
+
+QuadratureManager::QuadratureManager()
+  : m_line_gauss_legendre_formula_list{this->_buildLineGaussLegendreFormulaList()},
+    m_line_gauss_lobatto_formula_list{this->_buildLineGaussLobattoFormulaList()},
+    //
+    m_square_gauss_formula_list{this->_buildSquareGaussFormulaList()},
+    m_square_gauss_legendre_formula_list{this->_buildSquareGaussLegendreFormulaList()},
+    m_square_gauss_lobatto_formula_list{this->_buildSquareGaussLobattoFormulaList()},
+    m_triangle_gauss_formula_list{this->_buildTriangleGaussFormulaList()},
+    //
+    m_cube_gauss_formula_list{this->_buildCubeGaussFormulaList()},
+    m_cube_gauss_legendre_formula_list{this->_buildCubeGaussLegendreFormulaList()},
+    m_cube_gauss_lobatto_formula_list{this->_buildCubeGaussLobattoFormulaList()},
+    m_prism_gauss_formula_list{this->_buildPrismGaussFormulaList()},
+    m_pyramid_gauss_formula_list{this->_buildPyramidGaussFormulaList()},
+    m_tetrahedron_gauss_formula_list{this->_buildTetrahedronGaussFormulaList()}
+{
+  Assert(m_line_gauss_legendre_formula_list.size() * 2 - 1 == TensorialGaussLegendreQuadrature<1>::max_degree);
+  Assert(m_square_gauss_legendre_formula_list.size() * 2 - 1 == TensorialGaussLegendreQuadrature<2>::max_degree);
+  Assert(m_cube_gauss_legendre_formula_list.size() * 2 - 1 == TensorialGaussLegendreQuadrature<3>::max_degree);
+
+  Assert(m_line_gauss_lobatto_formula_list.size() * 2 - 1 == TensorialGaussLobattoQuadrature<1>::max_degree);
+  Assert(m_square_gauss_lobatto_formula_list.size() * 2 - 1 == TensorialGaussLobattoQuadrature<2>::max_degree);
+  Assert(m_cube_gauss_lobatto_formula_list.size() * 2 - 1 == TensorialGaussLobattoQuadrature<3>::max_degree);
+
+  Assert(m_square_gauss_formula_list.size() * 2 - 1 == SquareGaussQuadrature::max_degree);
+  Assert(m_triangle_gauss_formula_list.size() == TriangleGaussQuadrature::max_degree);
+
+  Assert(m_cube_gauss_formula_list.size() * 2 - 1 == CubeGaussQuadrature::max_degree);
+  Assert(m_prism_gauss_formula_list.size() == PrismGaussQuadrature::max_degree);
+  Assert(m_pyramid_gauss_formula_list.size() == PyramidGaussQuadrature::max_degree);
+  Assert(m_tetrahedron_gauss_formula_list.size() == TetrahedronGaussQuadrature::max_degree);
+}
diff --git a/src/analysis/QuadratureManager.hpp b/src/analysis/QuadratureManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..04655f291f8ff75e8c1207c4b6171a7b7f461a05
--- /dev/null
+++ b/src/analysis/QuadratureManager.hpp
@@ -0,0 +1,338 @@
+#ifndef QUADRATURE_MANAGER_HPP
+#define QUADRATURE_MANAGER_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+#include <analysis/QuadratureFormula.hpp>
+#include <utils/Array.hpp>
+#include <utils/Exceptions.hpp>
+#include <utils/PugsAssert.hpp>
+#include <utils/PugsMacros.hpp>
+
+class QuadratureManager
+{
+ private:
+  static QuadratureManager* s_instance;
+
+  Array<const QuadratureFormula<1>> m_line_gauss_legendre_formula_list;
+  Array<const QuadratureFormula<1>> m_line_gauss_lobatto_formula_list;
+
+  Array<const QuadratureFormula<2>> m_square_gauss_formula_list;
+  Array<const QuadratureFormula<2>> m_square_gauss_legendre_formula_list;
+  Array<const QuadratureFormula<2>> m_square_gauss_lobatto_formula_list;
+  Array<const QuadratureFormula<2>> m_triangle_gauss_formula_list;
+
+  Array<const QuadratureFormula<3>> m_cube_gauss_formula_list;
+  Array<const QuadratureFormula<3>> m_cube_gauss_legendre_formula_list;
+  Array<const QuadratureFormula<3>> m_cube_gauss_lobatto_formula_list;
+  Array<const QuadratureFormula<3>> m_prism_gauss_formula_list;
+  Array<const QuadratureFormula<3>> m_pyramid_gauss_formula_list;
+  Array<const QuadratureFormula<3>> m_tetrahedron_gauss_formula_list;
+
+  Array<const QuadratureFormula<1>> _buildLineGaussLobattoFormulaList();
+  Array<const QuadratureFormula<1>> _buildLineGaussLegendreFormulaList();
+
+  Array<const QuadratureFormula<2>> _buildSquareGaussFormulaList();
+  Array<const QuadratureFormula<2>> _buildSquareGaussLegendreFormulaList();
+  Array<const QuadratureFormula<2>> _buildSquareGaussLobattoFormulaList();
+  Array<const QuadratureFormula<2>> _buildTriangleGaussFormulaList();
+
+  Array<const QuadratureFormula<3>> _buildCubeGaussFormulaList();
+  Array<const QuadratureFormula<3>> _buildCubeGaussLegendreFormulaList();
+  Array<const QuadratureFormula<3>> _buildCubeGaussLobattoFormulaList();
+  Array<const QuadratureFormula<3>> _buildPrismGaussFormulaList();
+  Array<const QuadratureFormula<3>> _buildPyramidGaussFormulaList();
+  Array<const QuadratureFormula<3>> _buildTetrahedronGaussFormulaList();
+
+ public:
+  const QuadratureFormula<1>&
+  getLineFormula(const IQuadratureDescriptor& quadrature_descriptor) const
+  {
+    if (quadrature_descriptor.degree() > maxLineDegree(quadrature_descriptor.type())) {
+      throw NormalError(::name(quadrature_descriptor.type()) + " quadrature formulae handle degrees up to " +
+                        std::to_string(maxLineDegree(quadrature_descriptor.type())) + " on lines");
+    }
+    switch (quadrature_descriptor.type()) {
+    case QuadratureType::Gauss:
+    case QuadratureType::GaussLegendre: {
+      return m_line_gauss_legendre_formula_list[quadrature_descriptor.degree() / 2];
+      break;
+    }
+    case QuadratureType::GaussLobatto: {
+      return m_line_gauss_lobatto_formula_list[quadrature_descriptor.degree() / 2];
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(quadrature_descriptor.type()) + " is not defined on lines");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  const QuadratureFormula<2>&
+  getTriangleFormula(const IQuadratureDescriptor& quadrature_descriptor) const
+  {
+    if (quadrature_descriptor.degree() > maxTriangleDegree(quadrature_descriptor.type())) {
+      throw NormalError(::name(quadrature_descriptor.type()) + " quadrature formulae handle degrees up to " +
+                        std::to_string(maxTriangleDegree(quadrature_descriptor.type())) + " on triangles");
+    }
+    switch (quadrature_descriptor.type()) {
+    case QuadratureType::Gauss: {
+      return m_triangle_gauss_formula_list[quadrature_descriptor.degree() - 1];
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(quadrature_descriptor.type()) + " is not defined on triangles");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  const QuadratureFormula<2>&
+  getSquareFormula(const IQuadratureDescriptor& quadrature_descriptor) const
+  {
+    if (quadrature_descriptor.degree() > maxSquareDegree(quadrature_descriptor.type())) {
+      throw NormalError(::name(quadrature_descriptor.type()) + " quadrature formulae handle degrees up to " +
+                        std::to_string(maxSquareDegree(quadrature_descriptor.type())) + " on squares");
+    }
+    switch (quadrature_descriptor.type()) {
+    case QuadratureType::Gauss: {
+      return m_square_gauss_formula_list[quadrature_descriptor.degree() / 2];
+    }
+    case QuadratureType::GaussLegendre: {
+      return m_square_gauss_legendre_formula_list[quadrature_descriptor.degree() / 2];
+    }
+    case QuadratureType::GaussLobatto: {
+      return m_square_gauss_lobatto_formula_list[quadrature_descriptor.degree() / 2];
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(quadrature_descriptor.type()) + " is not defined on squares");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  const QuadratureFormula<3>&
+  getTetrahedronFormula(const IQuadratureDescriptor& quadrature_descriptor) const
+  {
+    if (quadrature_descriptor.degree() > maxTetrahedronDegree(quadrature_descriptor.type())) {
+      throw NormalError(::name(quadrature_descriptor.type()) + " quadrature formulae handle degrees up to " +
+                        std::to_string(maxTetrahedronDegree(quadrature_descriptor.type())) + " on tetrahedra");
+    }
+    switch (quadrature_descriptor.type()) {
+    case QuadratureType::Gauss: {
+      return m_tetrahedron_gauss_formula_list[quadrature_descriptor.degree() - 1];
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(quadrature_descriptor.type()) + " is not defined on tetrahedra");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  const QuadratureFormula<3>&
+  getPrismFormula(const IQuadratureDescriptor& quadrature_descriptor) const
+  {
+    if (quadrature_descriptor.degree() > maxPrismDegree(quadrature_descriptor.type())) {
+      throw NormalError(::name(quadrature_descriptor.type()) + " quadrature formulae handle degrees up to " +
+                        std::to_string(maxPrismDegree(quadrature_descriptor.type())) + " on prisms");
+    }
+    switch (quadrature_descriptor.type()) {
+    case QuadratureType::Gauss: {
+      return m_prism_gauss_formula_list[quadrature_descriptor.degree() - 1];
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(quadrature_descriptor.type()) + " is not defined on prisms");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  const QuadratureFormula<3>&
+  getPyramidFormula(const IQuadratureDescriptor& quadrature_descriptor) const
+  {
+    if (quadrature_descriptor.degree() > maxPyramidDegree(quadrature_descriptor.type())) {
+      throw NormalError(::name(quadrature_descriptor.type()) + " quadrature formulae handle degrees up to " +
+                        std::to_string(maxPyramidDegree(quadrature_descriptor.type())) + " on pyramids");
+    }
+    switch (quadrature_descriptor.type()) {
+    case QuadratureType::Gauss: {
+      return m_pyramid_gauss_formula_list[quadrature_descriptor.degree() - 1];
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(quadrature_descriptor.type()) + " is not defined on pyramid");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  const QuadratureFormula<3>&
+  getCubeFormula(const IQuadratureDescriptor& quadrature_descriptor) const
+  {
+    if (quadrature_descriptor.degree() > maxCubeDegree(quadrature_descriptor.type())) {
+      throw NormalError(::name(quadrature_descriptor.type()) + " quadrature formulae handle degrees up to " +
+                        std::to_string(maxCubeDegree(quadrature_descriptor.type())) + " on cubes");
+    }
+    switch (quadrature_descriptor.type()) {
+    case QuadratureType::Gauss: {
+      return m_cube_gauss_formula_list[quadrature_descriptor.degree() / 2];
+    }
+    case QuadratureType::GaussLegendre: {
+      return m_cube_gauss_legendre_formula_list[quadrature_descriptor.degree() / 2];
+      break;
+    }
+    case QuadratureType::GaussLobatto: {
+      return m_cube_gauss_lobatto_formula_list[quadrature_descriptor.degree() / 2];
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(quadrature_descriptor.type()) + " is not defined on cubes");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  size_t
+  maxLineDegree(const QuadratureType type) const
+  {
+    switch (type) {
+    case QuadratureType::Gauss:
+    case QuadratureType::GaussLegendre: {
+      return m_line_gauss_legendre_formula_list.size() * 2 - 1;
+    }
+    case QuadratureType::GaussLobatto: {
+      return m_line_gauss_lobatto_formula_list.size() * 2 - 1;
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid quadrature type");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  size_t
+  maxSquareDegree(const QuadratureType type) const
+  {
+    switch (type) {
+    case QuadratureType::Gauss: {
+      return m_square_gauss_formula_list.size() * 2 - 1;
+    }
+    case QuadratureType::GaussLegendre: {
+      return m_square_gauss_legendre_formula_list.size() * 2 - 1;
+    }
+    case QuadratureType::GaussLobatto: {
+      return m_square_gauss_lobatto_formula_list.size() * 2 - 1;
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid quadrature type");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  size_t
+  maxTriangleDegree(const QuadratureType type) const
+  {
+    switch (type) {
+    case QuadratureType::Gauss: {
+      return m_triangle_gauss_formula_list.size();
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(type) + " is not defined on triangle");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  size_t
+  maxCubeDegree(const QuadratureType type) const
+  {
+    switch (type) {
+    case QuadratureType::Gauss: {
+      return m_cube_gauss_formula_list.size() * 2 - 1;
+    }
+    case QuadratureType::GaussLegendre: {
+      return m_cube_gauss_legendre_formula_list.size() * 2 - 1;
+    }
+    case QuadratureType::GaussLobatto: {
+      return m_cube_gauss_lobatto_formula_list.size() * 2 - 1;
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid quadrature type");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  size_t
+  maxPrismDegree(const QuadratureType type) const
+  {
+    switch (type) {
+    case QuadratureType::Gauss: {
+      return m_prism_gauss_formula_list.size();
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(type) + " is not defined on prism");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  size_t
+  maxPyramidDegree(const QuadratureType type) const
+  {
+    switch (type) {
+    case QuadratureType::Gauss: {
+      return m_pyramid_gauss_formula_list.size();
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(type) + " is not defined on pyramid");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  size_t
+  maxTetrahedronDegree(const QuadratureType type) const
+  {
+    switch (type) {
+    case QuadratureType::Gauss: {
+      return m_tetrahedron_gauss_formula_list.size();
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError(::name(type) + " is not defined on tetrahedron");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  static void create();
+  static void destroy();
+
+  PUGS_INLINE
+  static const QuadratureManager&
+  instance()
+  {
+    Assert(s_instance != nullptr, "QuadratureManager was not created!");
+    return *s_instance;
+  }
+
+ private:
+  QuadratureManager(const QuadratureManager&) = delete;
+  QuadratureManager(QuadratureManager&&)      = delete;
+
+  QuadratureManager();
+  ~QuadratureManager() = default;
+};
+
+#endif   // QUADRATURE_MANAGER_HPP
diff --git a/src/analysis/QuadratureType.hpp b/src/analysis/QuadratureType.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..232a5fda0376852e9e9667ba1bd6bfd6c12287ac
--- /dev/null
+++ b/src/analysis/QuadratureType.hpp
@@ -0,0 +1,38 @@
+#ifndef QUADRATURE_TYPE_HPP
+#define QUADRATURE_TYPE_HPP
+
+#include <utils/Exceptions.hpp>
+#include <utils/PugsMacros.hpp>
+
+#include <string>
+
+enum class QuadratureType
+{
+  Gauss         = 0,
+  GaussLegendre = 1,
+  GaussLobatto  = 2,
+};
+
+PUGS_INLINE
+std::string
+name(QuadratureType type)
+{
+  switch (type) {
+  case QuadratureType::Gauss: {
+    return "Gauss";
+  }
+  case QuadratureType::GaussLegendre: {
+    return "Gauss-Legendre";
+  }
+  case QuadratureType::GaussLobatto: {
+    return "Gauss-Lobatto";
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("unknown quadrature type name");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
+
+#endif   // QUADRATURE_TYPE_HPP
diff --git a/src/analysis/SquareGaussQuadrature.cpp b/src/analysis/SquareGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2597e013b1b4f7ede3b69981fb43bfd4e901aff7
--- /dev/null
+++ b/src/analysis/SquareGaussQuadrature.cpp
@@ -0,0 +1,329 @@
+#include <analysis/SquareGaussQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+void
+SquareGaussQuadrature::_buildPointAndWeightLists(const size_t degree)
+{
+  using R2 = TinyVector<2>;
+
+  struct Descriptor
+  {
+    int id;
+    double weight;
+    std::vector<double> lambda_list;
+  };
+
+  auto fill_quadrature_points = [](auto descriptor_list, auto& point_list, auto& weight_list) {
+    Assert(point_list.size() == weight_list.size());
+
+    size_t k = 0;
+    for (size_t i = 0; i < descriptor_list.size(); ++i) {
+      const auto [id, w, position_list] = descriptor_list[i];
+
+      switch (id) {
+      case 1: {
+        Assert(position_list.size() == 0);
+
+        point_list[k]  = R2{0, 0};
+        weight_list[k] = w;
+
+        k += 1;
+        break;
+      }
+      case 2: {
+        Assert(position_list.size() == 1);
+        const double& a = position_list[0];
+
+        point_list[k + 0] = R2{+a, 0};
+        point_list[k + 1] = R2{-a, 0};
+        point_list[k + 2] = R2{0, +a};
+        point_list[k + 3] = R2{0, -a};
+
+        for (size_t l = 0; l < 4; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 4;
+        break;
+      }
+      case 3: {
+        Assert(position_list.size() == 1);
+        const double& a = position_list[0];
+
+        point_list[k + 0] = R2{+a, +a};
+        point_list[k + 1] = R2{+a, -a};
+        point_list[k + 2] = R2{-a, +a};
+        point_list[k + 3] = R2{-a, -a};
+
+        for (size_t l = 0; l < 4; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 4;
+        break;
+      }
+      case 4: {
+        Assert(position_list.size() == 2);
+        const double& a = position_list[0];
+        const double& b = position_list[1];
+
+        point_list[k + 0] = R2{+a, +b};
+        point_list[k + 1] = R2{+a, -b};
+        point_list[k + 2] = R2{-a, +b};
+        point_list[k + 3] = R2{-a, -b};
+        point_list[k + 4] = R2{+b, +a};
+        point_list[k + 5] = R2{+b, -a};
+        point_list[k + 6] = R2{-b, +a};
+        point_list[k + 7] = R2{-b, -a};
+
+        for (size_t l = 0; l < 8; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 8;
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("invalid quadrature id");
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+
+    Assert(k == point_list.size(), "invalid number of quadrature points");
+  };
+
+  switch (degree) {
+  case 0:
+  case 1: {
+    constexpr size_t nb_points = 1;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 4.000000000000000e+00, {}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 2:
+  case 3: {
+    constexpr size_t nb_points = 4;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{3, 1.000000000000000e+00, {5.773502691896257e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 4:
+  case 5: {
+    constexpr size_t nb_points = 8;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 8.163265306122449e-01, {6.831300510639732e-01}},
+       Descriptor{3, 1.836734693877551e-01, {8.819171036881969e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 6:
+  case 7: {
+    constexpr size_t nb_points = 12;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.419753086419753e-01, {9.258200997725514e-01}},
+       Descriptor{3, 2.374317746906302e-01, {8.059797829185987e-01}},
+       Descriptor{3, 5.205929166673945e-01, {3.805544332083157e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 8:
+  case 9: {
+    constexpr size_t nb_points = 20;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 4.541639606867490e-01, {4.889268569743691e-01}},
+       Descriptor{3, 4.273123186577577e-02, {9.396552580968377e-01}},
+       Descriptor{3, 2.142003609268616e-01, {6.908805504863439e-01}},
+       Descriptor{4, 1.444522232603068e-01, {9.186204410567222e-01, 3.448720253644036e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 10:
+  case 11: {
+    constexpr size_t nb_points = 28;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.174004398687120e-01, {7.146178296646060e-01}},
+       Descriptor{3, 2.772741029838511e-01, {2.736572101714596e-01}},
+       Descriptor{3, 2.139336378782481e-01, {6.366039322123010e-01}},
+       Descriptor{4, 4.407456911498309e-02, {9.516303887840335e-01, 8.155654336896384e-01}},
+       Descriptor{4, 1.016213405196113e-01, {3.462072000476454e-01, 9.355678714875911e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 12:
+  case 13: {
+    constexpr size_t nb_points = 37;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 2.999933443589096e-01, {}},
+       Descriptor{2, 3.812862534985274e-02, {9.834613326132455e-01}},
+       Descriptor{2, 1.845354689698072e-01, {6.398614183671097e-01}},
+       Descriptor{3, 3.950714064745244e-02, {9.187784880797114e-01}},
+       Descriptor{3, 2.313985194006854e-01, {3.796285051867438e-01}},
+       Descriptor{3, 1.372420967130035e-01, {6.995542165133511e-01}},
+       Descriptor{4, 3.351978005038143e-02, {6.435973749966181e-01, 9.750688839059836e-01}},
+       Descriptor{4, 1.135751263643542e-01, {3.335398811647831e-01, 8.644276092670619e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 14:
+  case 15: {
+    constexpr size_t nb_points = 48;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.149329272635254e-01, {7.980989878970071e-01}},
+       Descriptor{2, 1.816857589672718e-01, {3.038530263984597e-01}},
+       Descriptor{3, 4.123848378876581e-02, {8.824222701068853e-01}},
+       Descriptor{3, 5.933746485839221e-03, {9.777897995399027e-01}},
+       Descriptor{4, 1.011491434774324e-01, {8.087121358195035e-01, 5.672135733965907e-01}},
+       Descriptor{4, 1.478367216328812e-01, {3.046547990370526e-01, 5.787361940358066e-01}},
+       Descriptor{4, 2.327319414467321e-02, {9.805048437245319e-01, 6.974636319909671e-01}},
+       Descriptor{4, 5.584548249231202e-02, {2.648441558723162e-01, 9.557425198095117e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 16:
+  case 17: {
+    constexpr size_t nb_points = 60;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.388043776872199e-01, {5.450429872091483e-01}},
+       Descriptor{2, 6.534782380746491e-02, {9.174971882191532e-01}},
+       Descriptor{3, 1.428564196221996e-01, {1.925542426140469e-01}},
+       Descriptor{3, 3.671590848525151e-02, {8.713286846954432e-01}},
+       Descriptor{3, 8.625569963814381e-02, {6.943880900895507e-01}},
+       Descriptor{3, 1.345562732119169e-01, {4.576829928831555e-01}},
+       Descriptor{3, 7.655633414909936e-03, {9.692042560915087e-01}},
+       Descriptor{4, 1.031767247849354e-01, {7.620882760801982e-01, 2.794777667151921e-01}},
+       Descriptor{4, 5.482640023729144e-02, {9.073943521982236e-01, 5.468986704143101e-01}},
+       Descriptor{4, 1.511981038825686e-02, {7.612486664390827e-01, 9.837879715015495e-01}},
+       Descriptor{4, 2.078099665596304e-02, {9.870947070447679e-01, 3.028074817888614e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 18:
+  case 19: {
+    constexpr size_t nb_points = 72;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 9.773736469282875e-02, {7.143442595727942e-01}},
+       Descriptor{2, 1.393076129224823e-01, {2.656720521209637e-01}},
+       Descriptor{2, 3.486958349188791e-02, {9.644342692579673e-01}},
+       Descriptor{3, 4.780454716279579e-02, {8.033797294676850e-01}},
+       Descriptor{3, 1.617715177911761e-03, {9.921654058971348e-01}},
+       Descriptor{3, 1.744104803435576e-02, {9.294496027996094e-01}},
+       Descriptor{4, 1.177258328400561e-01, {5.102782573693434e-01, 2.666403145945622e-01}},
+       Descriptor{4, 1.617957761165785e-02, {3.907342057752498e-01, 9.878929331417353e-01}},
+       Descriptor{4, 8.284661898340490e-02, {7.171679213097452e-01, 5.124918772160977e-01}},
+       Descriptor{4, 6.498908149259605e-02, {2.654400078112960e-01, 8.689024341545042e-01}},
+       Descriptor{4, 3.898055239641054e-02, {6.200353986932564e-01, 9.263029558071293e-01}},
+       Descriptor{4, 9.889400934743484e-03, {8.016715847185969e-01, 9.884465306839737e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 20:
+  case 21: {
+    constexpr size_t nb_points = 85;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.351928403561428e-01, {}},
+       Descriptor{2, 1.067941587859404e-01, {4.733510743582242e-01}},
+       Descriptor{2, 6.625720800494439e-02, {8.352784297558683e-01}},
+       Descriptor{3, 1.198844836463405e-01, {2.573719006072290e-01}},
+       Descriptor{3, 8.229461289220009e-03, {9.633378321156234e-01}},
+       Descriptor{3, 3.060364092877565e-02, {8.624519253796515e-01}},
+       Descriptor{3, 9.679179189359521e-02, {4.968979625193457e-01}},
+       Descriptor{3, 6.151634148131656e-02, {7.043321751954006e-01}},
+       Descriptor{4, 8.672849951320823e-02, {2.418781854767020e-01, 6.741462199553178e-01}},
+       Descriptor{4, 5.587911740412735e-02, {4.801569663127951e-01, 8.246473752709207e-01}},
+       Descriptor{4, 3.712882744091382e-02, {9.322419359217540e-01, 2.706991841016649e-01}},
+       Descriptor{4, 2.877560085102053e-02, {9.349023258240106e-01, 6.750395674370753e-01}},
+       Descriptor{4, 1.150952044249317e-02, {9.904554675295242e-01, 4.889162511771669e-01}},
+       Descriptor{4, 7.528482130401646e-03, {8.311352459759014e-01, 9.889606113865724e-01}},
+       Descriptor{4, 1.051230415825108e-02, {9.146960092229130e-02, 9.840171484895990e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(SquareGaussQuadrature::max_degree) + " on squares");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/analysis/SquareGaussQuadrature.hpp b/src/analysis/SquareGaussQuadrature.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..71ea25a7a7f25c65f862fbddc92f31f4d5057fa4
--- /dev/null
+++ b/src/analysis/SquareGaussQuadrature.hpp
@@ -0,0 +1,39 @@
+#ifndef SQUARE_GAUSS_QUADRATURE_HPP
+#define SQUARE_GAUSS_QUADRATURE_HPP
+
+#include <analysis/QuadratureFormula.hpp>
+
+/**
+ * Defines Square Gauss quadrature on the reference element
+ * \f$]-1,1[^2\f$
+ *
+ * \note formulae are provided by
+ *
+ * ‘On the identification of symmetric quadrature rules for finite
+ * element methods‘ by F.D. Witherden & P.E. Vincent (2015).
+ */
+class SquareGaussQuadrature final : public QuadratureFormula<2>
+{
+ public:
+  constexpr static size_t max_degree = 21;
+
+ private:
+  void _buildPointAndWeightLists(const size_t degree);
+
+ public:
+  SquareGaussQuadrature(SquareGaussQuadrature&&)      = default;
+  SquareGaussQuadrature(const SquareGaussQuadrature&) = default;
+
+ private:
+  friend class QuadratureManager;
+
+  explicit SquareGaussQuadrature(const size_t degree) : QuadratureFormula<2>(QuadratureType::Gauss)
+  {
+    this->_buildPointAndWeightLists(degree);
+  }
+
+  SquareGaussQuadrature()  = delete;
+  ~SquareGaussQuadrature() = default;
+};
+
+#endif   // SQUARE_GAUSS_QUADRATURE_HPP
diff --git a/src/analysis/TensorialGaussLegendreQuadrature.cpp b/src/analysis/TensorialGaussLegendreQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a306f44135b7a4cf31ddc6e906f0352ca1bde067
--- /dev/null
+++ b/src/analysis/TensorialGaussLegendreQuadrature.cpp
@@ -0,0 +1,296 @@
+#include <analysis/TensorialGaussLegendreQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+template <>
+void
+TensorialGaussLegendreQuadrature<1>::_buildPointAndWeightLists(const size_t degree)
+{
+  using R1 = TinyVector<1>;
+
+  const size_t nb_points = degree / 2 + 1;
+
+  SmallArray<R1> point_list(nb_points);
+  SmallArray<double> weight_list(nb_points);
+
+  switch (degree) {
+  case 0:
+  case 1: {
+    point_list[0] = R1{0};
+
+    weight_list[0] = 2;
+    break;
+  }
+  case 2:
+  case 3: {
+    point_list[0] = R1{-0.5773502691896257645091488};
+    point_list[1] = R1{+0.5773502691896257645091488};
+
+    weight_list[0] = 1;
+    weight_list[1] = 1;
+    break;
+  }
+  case 4:
+  case 5: {
+    point_list[0] = R1{-0.7745966692414833770358531};
+    point_list[1] = R1{0};
+    point_list[2] = R1{+0.7745966692414833770358531};
+
+    weight_list[0] = 0.5555555555555555555555556;
+    weight_list[1] = 0.8888888888888888888888889;
+    weight_list[2] = 0.5555555555555555555555556;
+    break;
+  }
+  case 6:
+  case 7: {
+    point_list[0] = R1{-0.8611363115940525752239465};
+    point_list[1] = R1{-0.3399810435848562648026658};
+    point_list[2] = R1{+0.3399810435848562648026658};
+    point_list[3] = R1{+0.8611363115940525752239465};
+
+    weight_list[0] = 0.3478548451374538573730639;
+    weight_list[1] = 0.6521451548625461426269361;
+    weight_list[2] = 0.6521451548625461426269361;
+    weight_list[3] = 0.3478548451374538573730639;
+    break;
+  }
+  case 8:
+  case 9: {
+    point_list[0] = R1{-0.9061798459386639927976269};
+    point_list[1] = R1{-0.5384693101056830910363144};
+    point_list[2] = R1{0};
+    point_list[3] = R1{+0.5384693101056830910363144};
+    point_list[4] = R1{+0.9061798459386639927976269};
+
+    weight_list[0] = 0.2369268850561890875142640;
+    weight_list[1] = 0.4786286704993664680412915;
+    weight_list[2] = 0.5688888888888888888888889;
+    weight_list[3] = 0.4786286704993664680412915;
+    weight_list[4] = 0.2369268850561890875142640;
+    break;
+  }
+  case 10:
+  case 11: {
+    point_list[0] = R1{-0.9324695142031520278123016};
+    point_list[1] = R1{-0.6612093864662645136613996};
+    point_list[2] = R1{-0.2386191860831969086305017};
+    point_list[3] = R1{+0.2386191860831969086305017};
+    point_list[4] = R1{+0.6612093864662645136613996};
+    point_list[5] = R1{+0.9324695142031520278123016};
+
+    weight_list[0] = 0.1713244923791703450402961;
+    weight_list[1] = 0.3607615730481386075698335;
+    weight_list[2] = 0.4679139345726910473898703;
+    weight_list[3] = 0.4679139345726910473898703;
+    weight_list[4] = 0.3607615730481386075698335;
+    weight_list[5] = 0.1713244923791703450402961;
+    break;
+  }
+  case 12:
+  case 13: {
+    point_list[0] = R1{-0.9491079123427585245261897};
+    point_list[1] = R1{-0.7415311855993944398638648};
+    point_list[2] = R1{-0.4058451513773971669066064};
+    point_list[3] = R1{0};
+    point_list[4] = R1{+0.4058451513773971669066064};
+    point_list[5] = R1{+0.7415311855993944398638648};
+    point_list[6] = R1{+0.9491079123427585245261897};
+
+    weight_list[0] = 0.1294849661688696932706114;
+    weight_list[1] = 0.2797053914892766679014678;
+    weight_list[2] = 0.3818300505051189449503698;
+    weight_list[3] = 0.4179591836734693877551020;
+    weight_list[4] = 0.3818300505051189449503698;
+    weight_list[5] = 0.2797053914892766679014678;
+    weight_list[6] = 0.1294849661688696932706114;
+    break;
+  }
+  case 14:
+  case 15: {
+    point_list[0] = R1{-0.9602898564975362316835609};
+    point_list[1] = R1{-0.7966664774136267395915539};
+    point_list[2] = R1{-0.5255324099163289858177390};
+    point_list[3] = R1{-0.1834346424956498049394761};
+    point_list[4] = R1{+0.1834346424956498049394761};
+    point_list[5] = R1{+0.5255324099163289858177390};
+    point_list[6] = R1{+0.7966664774136267395915539};
+    point_list[7] = R1{+0.9602898564975362316835609};
+
+    weight_list[0] = 0.1012285362903762591525314;
+    weight_list[1] = 0.2223810344533744705443560;
+    weight_list[2] = 0.3137066458778872873379622;
+    weight_list[3] = 0.3626837833783619829651504;
+    weight_list[4] = 0.3626837833783619829651504;
+    weight_list[5] = 0.3137066458778872873379622;
+    weight_list[6] = 0.2223810344533744705443560;
+    weight_list[7] = 0.1012285362903762591525314;
+    break;
+  }
+  case 16:
+  case 17: {
+    point_list[0] = R1{-0.9681602395076260898355762};
+    point_list[1] = R1{-0.8360311073266357942994298};
+    point_list[2] = R1{-0.6133714327005903973087020};
+    point_list[3] = R1{-0.3242534234038089290385380};
+    point_list[4] = R1{0};
+    point_list[5] = R1{+0.3242534234038089290385380};
+    point_list[6] = R1{+0.6133714327005903973087020};
+    point_list[7] = R1{+0.8360311073266357942994298};
+    point_list[8] = R1{+0.9681602395076260898355762};
+
+    weight_list[0] = 0.0812743883615744119718922;
+    weight_list[1] = 0.1806481606948574040584720;
+    weight_list[2] = 0.2606106964029354623187429;
+    weight_list[3] = 0.3123470770400028400686304;
+    weight_list[4] = 0.3302393550012597631645251;
+    weight_list[5] = 0.3123470770400028400686304;
+    weight_list[6] = 0.2606106964029354623187429;
+    weight_list[7] = 0.1806481606948574040584720;
+    weight_list[8] = 0.0812743883615744119718922;
+    break;
+  }
+  case 18:
+  case 19: {
+    point_list[0] = R1{-0.9739065285171717200779640};
+    point_list[1] = R1{-0.8650633666889845107320967};
+    point_list[2] = R1{-0.6794095682990244062343274};
+    point_list[3] = R1{-0.4333953941292471907992659};
+    point_list[4] = R1{-0.1488743389816312108848260};
+    point_list[5] = R1{+0.1488743389816312108848260};
+    point_list[6] = R1{+0.4333953941292471907992659};
+    point_list[7] = R1{+0.6794095682990244062343274};
+    point_list[8] = R1{+0.8650633666889845107320967};
+    point_list[9] = R1{+0.9739065285171717200779640};
+
+    weight_list[0] = 0.0666713443086881375935688;
+    weight_list[1] = 0.1494513491505805931457763;
+    weight_list[2] = 0.2190863625159820439955349;
+    weight_list[3] = 0.2692667193099963550912269;
+    weight_list[4] = 0.2955242247147528701738930;
+    weight_list[5] = 0.2955242247147528701738930;
+    weight_list[6] = 0.2692667193099963550912269;
+    weight_list[7] = 0.2190863625159820439955349;
+    weight_list[8] = 0.1494513491505805931457763;
+    weight_list[9] = 0.0666713443086881375935688;
+    break;
+  }
+  case 20:
+  case 21: {
+    point_list[0]  = R1{-0.9782286581460569928039380};
+    point_list[1]  = R1{-0.8870625997680952990751578};
+    point_list[2]  = R1{-0.7301520055740493240934163};
+    point_list[3]  = R1{-0.5190961292068118159257257};
+    point_list[4]  = R1{-0.2695431559523449723315320};
+    point_list[5]  = R1{0};
+    point_list[6]  = R1{+0.2695431559523449723315320};
+    point_list[7]  = R1{+0.5190961292068118159257257};
+    point_list[8]  = R1{+0.7301520055740493240934163};
+    point_list[9]  = R1{+0.8870625997680952990751578};
+    point_list[10] = R1{+0.9782286581460569928039380};
+
+    weight_list[0]  = 0.0556685671161736664827537;
+    weight_list[1]  = 0.1255803694649046246346940;
+    weight_list[2]  = 0.1862902109277342514260980;
+    weight_list[3]  = 0.2331937645919904799185237;
+    weight_list[4]  = 0.2628045445102466621806889;
+    weight_list[5]  = 0.2729250867779006307144835;
+    weight_list[6]  = 0.2628045445102466621806889;
+    weight_list[7]  = 0.2331937645919904799185237;
+    weight_list[8]  = 0.1862902109277342514260980;
+    weight_list[9]  = 0.1255803694649046246346940;
+    weight_list[10] = 0.0556685671161736664827537;
+    break;
+  }
+  case 22:
+  case 23: {
+    point_list[0]  = R1{-0.9815606342467192506905491};
+    point_list[1]  = R1{-0.9041172563704748566784659};
+    point_list[2]  = R1{-0.7699026741943046870368938};
+    point_list[3]  = R1{-0.5873179542866174472967024};
+    point_list[4]  = R1{-0.3678314989981801937526915};
+    point_list[5]  = R1{-0.1252334085114689154724414};
+    point_list[6]  = R1{+0.1252334085114689154724414};
+    point_list[7]  = R1{+0.3678314989981801937526915};
+    point_list[8]  = R1{+0.5873179542866174472967024};
+    point_list[9]  = R1{+0.7699026741943046870368938};
+    point_list[10] = R1{+0.9041172563704748566784659};
+    point_list[11] = R1{+0.9815606342467192506905491};
+
+    weight_list[0]  = 0.0471753363865118271946160;
+    weight_list[1]  = 0.1069393259953184309602547;
+    weight_list[2]  = 0.1600783285433462263346525;
+    weight_list[3]  = 0.2031674267230659217490645;
+    weight_list[4]  = 0.2334925365383548087608499;
+    weight_list[5]  = 0.2491470458134027850005624;
+    weight_list[6]  = 0.2491470458134027850005624;
+    weight_list[7]  = 0.2334925365383548087608499;
+    weight_list[8]  = 0.2031674267230659217490645;
+    weight_list[9]  = 0.1600783285433462263346525;
+    weight_list[10] = 0.1069393259953184309602547;
+    weight_list[11] = 0.0471753363865118271946160;
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("Gauss-Legendre quadrature formulae handle degrees up to " +
+                          std::to_string(TensorialGaussLegendreQuadrature<1>::max_degree));
+  }
+    // LCOV_EXCL_STOP
+  }
+
+  m_point_list  = point_list;
+  m_weight_list = weight_list;
+}
+
+template <>
+void
+TensorialGaussLegendreQuadrature<2>::_buildPointAndWeightLists(const size_t degree)
+{
+  const size_t nb_points_1d = degree / 2 + 1;
+  const size_t nb_points    = nb_points_1d * nb_points_1d;
+
+  SmallArray<TinyVector<2>> point_list(nb_points);
+  SmallArray<double> weight_list(nb_points);
+
+  TensorialGaussLegendreQuadrature<1> gauss_legendre_1d(degree);
+  const auto& point_list_1d  = gauss_legendre_1d.pointList();
+  const auto& weight_list_1d = gauss_legendre_1d.weightList();
+
+  size_t l = 0;
+  for (size_t i = 0; i < nb_points_1d; ++i) {
+    for (size_t j = 0; j < nb_points_1d; ++j, ++l) {
+      point_list[l]  = TinyVector<2>{point_list_1d[i][0], point_list_1d[j][0]};
+      weight_list[l] = weight_list_1d[i] * weight_list_1d[j];
+    }
+  }
+
+  this->m_point_list  = point_list;
+  this->m_weight_list = weight_list;
+}
+
+template <>
+void
+TensorialGaussLegendreQuadrature<3>::_buildPointAndWeightLists(const size_t degree)
+{
+  const size_t nb_points_1d = degree / 2 + 1;
+  const size_t nb_points    = nb_points_1d * nb_points_1d * nb_points_1d;
+
+  SmallArray<TinyVector<3>> point_list(nb_points);
+  SmallArray<double> weight_list(nb_points);
+
+  TensorialGaussLegendreQuadrature<1> gauss_legendre_1d(degree);
+  const auto& point_list_1d  = gauss_legendre_1d.pointList();
+  const auto& weight_list_1d = gauss_legendre_1d.weightList();
+
+  size_t l = 0;
+  for (size_t i = 0; i < nb_points_1d; ++i) {
+    for (size_t j = 0; j < nb_points_1d; ++j) {
+      for (size_t k = 0; k < nb_points_1d; ++k, ++l) {
+        point_list[l]  = TinyVector<3>{point_list_1d[i][0], point_list_1d[j][0], point_list_1d[k][0]};
+        weight_list[l] = weight_list_1d[i] * weight_list_1d[j] * weight_list_1d[k];
+      }
+    }
+  }
+
+  this->m_point_list  = point_list;
+  this->m_weight_list = weight_list;
+}
diff --git a/src/analysis/TensorialGaussLegendreQuadrature.hpp b/src/analysis/TensorialGaussLegendreQuadrature.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..51aadae6f64b13f2f8c3b68a59665174ad74150e
--- /dev/null
+++ b/src/analysis/TensorialGaussLegendreQuadrature.hpp
@@ -0,0 +1,40 @@
+#ifndef TENSORIAL_GAUSS_LEGENDRE_QUADRATURE_HPP
+#define TENSORIAL_GAUSS_LEGENDRE_QUADRATURE_HPP
+
+#include <analysis/QuadratureFormula.hpp>
+
+/**
+ * Defines Gauss Legendre quadrature on the reference element
+ * \f$]-1,1[^d\f$, where \f$d\f$ denotes the \a Dimension.
+ *
+ * \note formulae are extracted from High-order Finite Element Method [2004 -  Chapman & Hall]
+ */
+template <size_t Dimension>
+class TensorialGaussLegendreQuadrature final : public QuadratureFormula<Dimension>
+{
+ public:
+  constexpr static size_t max_degree = 23;
+
+ private:
+  void _buildPointAndWeightLists(const size_t degree);
+
+ public:
+  TensorialGaussLegendreQuadrature(TensorialGaussLegendreQuadrature&&)      = default;
+  TensorialGaussLegendreQuadrature(const TensorialGaussLegendreQuadrature&) = default;
+
+ private:
+  friend class QuadratureManager;
+
+  template <size_t D>
+  friend class TensorialGaussLegendreQuadrature;
+
+  explicit TensorialGaussLegendreQuadrature(const size_t degree) : QuadratureFormula<Dimension>(QuadratureType::Gauss)
+  {
+    this->_buildPointAndWeightLists(degree);
+  }
+
+  TensorialGaussLegendreQuadrature()  = delete;
+  ~TensorialGaussLegendreQuadrature() = default;
+};
+
+#endif   // TENSORIAL_GAUSS_LEGENDRE_QUADRATURE_HPP
diff --git a/src/analysis/TensorialGaussLobattoQuadrature.cpp b/src/analysis/TensorialGaussLobattoQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f53b49434335643684be80e20d5d750dde1c4e93
--- /dev/null
+++ b/src/analysis/TensorialGaussLobattoQuadrature.cpp
@@ -0,0 +1,185 @@
+#include <analysis/TensorialGaussLobattoQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+template <>
+void
+TensorialGaussLobattoQuadrature<1>::_buildPointAndWeightLists(const size_t degree)
+{
+  using R1 = TinyVector<1>;
+
+  const size_t nb_points = degree / 2 + 2;
+
+  SmallArray<TinyVector<1>> point_list(nb_points);
+  SmallArray<double> weight_list(nb_points);
+
+  switch (degree) {
+  case 0:
+  case 1: {
+    point_list[0] = R1{-1};
+    point_list[1] = R1{+1};
+
+    weight_list[0] = 1;
+    weight_list[1] = 1;
+    break;
+  }
+  case 2:
+  case 3: {
+    point_list[0] = R1{-1};
+    point_list[1] = R1{0};
+    point_list[2] = R1{+1};
+
+    weight_list[0] = 0.3333333333333333333333333;
+    weight_list[1] = 1.3333333333333333333333333;
+    weight_list[2] = 0.3333333333333333333333333;
+    break;
+  }
+  case 4:
+  case 5: {
+    point_list[0] = R1{-1};
+    point_list[1] = R1{-0.4472135954999579392818347};
+    point_list[2] = R1{+0.4472135954999579392818347};
+    point_list[3] = R1{+1};
+
+    weight_list[0] = 0.1666666666666666666666667;
+    weight_list[1] = 0.8333333333333333333333333;
+    weight_list[2] = 0.8333333333333333333333333;
+    weight_list[3] = 0.1666666666666666666666667;
+    break;
+  }
+  case 6:
+  case 7: {
+    point_list[0] = R1{-1};
+    point_list[1] = R1{-0.6546536707079771437982925};
+    point_list[2] = R1{0};
+    point_list[3] = R1{+0.6546536707079771437982925};
+    point_list[4] = R1{+1};
+
+    weight_list[0] = 0.1;
+    weight_list[1] = 0.5444444444444444444444444;
+    weight_list[2] = 0.7111111111111111111111111;
+    weight_list[3] = 0.5444444444444444444444444;
+    weight_list[4] = 0.1;
+    break;
+  }
+  case 8:
+  case 9: {
+    point_list[0] = R1{-1};
+    point_list[1] = R1{-0.7650553239294646928510030};
+    point_list[2] = R1{-0.2852315164806450963141510};
+    point_list[3] = R1{+0.2852315164806450963141510};
+    point_list[4] = R1{+0.7650553239294646928510030};
+    point_list[5] = R1{+1};
+
+    weight_list[0] = 0.0666666666666666666666667;
+    weight_list[1] = 0.3784749562978469803166128;
+    weight_list[2] = 0.5548583770354863530167205;
+    weight_list[3] = 0.5548583770354863530167205;
+    weight_list[4] = 0.3784749562978469803166128;
+    weight_list[5] = 0.0666666666666666666666667;
+    break;
+  }
+  case 10:
+  case 11: {
+    point_list[0] = R1{-1};
+    point_list[1] = R1{-0.8302238962785669298720322};
+    point_list[2] = R1{-0.4688487934707142138037719};
+    point_list[3] = R1{0};
+    point_list[4] = R1{+0.4688487934707142138037719};
+    point_list[5] = R1{+0.8302238962785669298720322};
+    point_list[6] = R1{+1};
+
+    weight_list[0] = 0.0476190476190476190476190;
+    weight_list[1] = 0.2768260473615659480107004;
+    weight_list[2] = 0.4317453812098626234178710;
+    weight_list[3] = 0.4876190476190476190476190;
+    weight_list[4] = 0.4317453812098626234178710;
+    weight_list[5] = 0.2768260473615659480107004;
+    weight_list[6] = 0.0476190476190476190476190;
+    break;
+  }
+  case 12:
+  case 13: {
+    point_list[0] = R1{-1};
+    point_list[1] = R1{-0.8717401485096066153374457};
+    point_list[2] = R1{-0.5917001814331423021445107};
+    point_list[3] = R1{-0.2092992179024788687686573};
+    point_list[4] = R1{+0.2092992179024788687686573};
+    point_list[5] = R1{+0.5917001814331423021445107};
+    point_list[6] = R1{+0.8717401485096066153374457};
+    point_list[7] = R1{+1};
+
+    weight_list[0] = 0.0357142857142857142857143;
+    weight_list[1] = 0.2107042271435060393829921;
+    weight_list[2] = 0.3411226924835043647642407;
+    weight_list[3] = 0.4124587946587038815670530;
+    weight_list[4] = 0.4124587946587038815670530;
+    weight_list[5] = 0.3411226924835043647642407;
+    weight_list[6] = 0.2107042271435060393829921;
+    weight_list[7] = 0.0357142857142857142857143;
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("Gauss-Lobatto quadrature formulae handle degrees up to " +
+                          std::to_string(TensorialGaussLobattoQuadrature<1>::max_degree));
+  }
+    // LCOV_EXCL_STOP
+  }
+
+  m_point_list  = point_list;
+  m_weight_list = weight_list;
+}
+
+template <>
+void
+TensorialGaussLobattoQuadrature<2>::_buildPointAndWeightLists(const size_t degree)
+{
+  const size_t nb_points_1d = degree / 2 + 2;
+  const size_t nb_points    = nb_points_1d * nb_points_1d;
+
+  SmallArray<TinyVector<2>> point_list(nb_points);
+  SmallArray<double> weight_list(nb_points);
+
+  TensorialGaussLobattoQuadrature<1> gauss_lobatto_1d(degree);
+  const auto& point_list_1d  = gauss_lobatto_1d.pointList();
+  const auto& weight_list_1d = gauss_lobatto_1d.weightList();
+
+  size_t l = 0;
+  for (size_t i = 0; i < nb_points_1d; ++i) {
+    for (size_t j = 0; j < nb_points_1d; ++j, ++l) {
+      point_list[l]  = TinyVector<2>{point_list_1d[i][0], point_list_1d[j][0]};
+      weight_list[l] = weight_list_1d[i] * weight_list_1d[j];
+    }
+  }
+
+  this->m_point_list  = point_list;
+  this->m_weight_list = weight_list;
+}
+
+template <>
+void
+TensorialGaussLobattoQuadrature<3>::_buildPointAndWeightLists(const size_t degree)
+{
+  const size_t nb_points_1d = degree / 2 + 2;
+  const size_t nb_points    = nb_points_1d * nb_points_1d * nb_points_1d;
+
+  SmallArray<TinyVector<3>> point_list(nb_points);
+  SmallArray<double> weight_list(nb_points);
+
+  TensorialGaussLobattoQuadrature<1> gauss_lobatto_1d(degree);
+  const auto& point_list_1d  = gauss_lobatto_1d.pointList();
+  const auto& weight_list_1d = gauss_lobatto_1d.weightList();
+
+  size_t l = 0;
+  for (size_t i = 0; i < nb_points_1d; ++i) {
+    for (size_t j = 0; j < nb_points_1d; ++j) {
+      for (size_t k = 0; k < nb_points_1d; ++k, ++l) {
+        point_list[l]  = TinyVector<3>{point_list_1d[i][0], point_list_1d[j][0], point_list_1d[k][0]};
+        weight_list[l] = weight_list_1d[i] * weight_list_1d[j] * weight_list_1d[k];
+      }
+    }
+  }
+
+  this->m_point_list  = point_list;
+  this->m_weight_list = weight_list;
+}
diff --git a/src/analysis/TensorialGaussLobattoQuadrature.hpp b/src/analysis/TensorialGaussLobattoQuadrature.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1a578bbbc1af1d7d4efdae4183cea1e3c6fb76ff
--- /dev/null
+++ b/src/analysis/TensorialGaussLobattoQuadrature.hpp
@@ -0,0 +1,40 @@
+#ifndef TENSORIAL_GAUSS_LOBATTO_QUADRATURE_HPP
+#define TENSORIAL_GAUSS_LOBATTO_QUADRATURE_HPP
+
+#include <analysis/QuadratureFormula.hpp>
+
+/**
+ * Defines Gauss Lobatto quadrature on the reference element
+ * \f$]-1,1[^d\f$, where \f$d\f$ denotes the \a Dimension.
+ *
+ * \note formulae are extracted from High-order Finite Element Method [2004 -  Chapman & Hall]
+ */
+template <size_t Dimension>
+class TensorialGaussLobattoQuadrature final : public QuadratureFormula<Dimension>
+{
+ public:
+  constexpr static size_t max_degree = 13;
+
+ private:
+  void _buildPointAndWeightLists(const size_t degree);
+
+ public:
+  TensorialGaussLobattoQuadrature(TensorialGaussLobattoQuadrature&&)      = default;
+  TensorialGaussLobattoQuadrature(const TensorialGaussLobattoQuadrature&) = default;
+
+ private:
+  friend class QuadratureManager;
+
+  template <size_t D>
+  friend class TensorialGaussLobattoQuadrature;
+
+  explicit TensorialGaussLobattoQuadrature(const size_t degree) : QuadratureFormula<Dimension>(QuadratureType::Gauss)
+  {
+    this->_buildPointAndWeightLists(degree);
+  }
+
+  TensorialGaussLobattoQuadrature()  = delete;
+  ~TensorialGaussLobattoQuadrature() = default;
+};
+
+#endif   // TENSORIAL_GAUSS_LOBATTO_QUADRATURE_HPP
diff --git a/src/analysis/TetrahedronGaussQuadrature.cpp b/src/analysis/TetrahedronGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..49fd5efe685c765aedccae3ee1b2c59d82915dd3
--- /dev/null
+++ b/src/analysis/TetrahedronGaussQuadrature.cpp
@@ -0,0 +1,690 @@
+#include <analysis/TetrahedronGaussQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+void
+TetrahedronGaussQuadrature::_buildPointAndWeightLists(const size_t degree)
+{
+  using R3 = TinyVector<3>;
+
+  struct Descriptor
+  {
+    int id;
+    double weight;
+    std::vector<double> lambda_list;
+  };
+
+  auto fill_quadrature_points = [](auto descriptor_list, auto& point_list, auto& weight_list) {
+    Assert(point_list.size() == weight_list.size());
+
+    const R3 A{0, 0, 0};
+    const R3 B{1, 0, 0};
+    const R3 C{0, 1, 0};
+    const R3 D{0, 0, 1};
+
+    size_t k = 0;
+    for (size_t i = 0; i < descriptor_list.size(); ++i) {
+      const auto [id, unit_weight, lambda_list] = descriptor_list[i];
+
+      const double w = (1. / 6) * unit_weight;
+
+      switch (id) {
+      case 1: {
+        Assert(lambda_list.size() == 0);
+        point_list[k]  = (1. / 4) * (A + B + C + D);
+        weight_list[k] = w;
+        ++k;
+        break;
+      }
+      case 2: {
+        Assert(lambda_list.size() == 1);
+        const double l0 = lambda_list[0];
+        const double l1 = 1 - 3 * l0;
+
+        point_list[k + 0] = l0 * A + l0 * B + l0 * C + l1 * D;
+        point_list[k + 1] = l0 * A + l0 * B + l1 * C + l0 * D;
+        point_list[k + 2] = l0 * A + l1 * B + l0 * C + l0 * D;
+        point_list[k + 3] = l1 * A + l0 * B + l0 * C + l0 * D;
+
+        for (size_t l = 0; l < 4; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 4;
+        break;
+      }
+      case 3: {
+        Assert(lambda_list.size() == 1);
+        const double l0 = lambda_list[0];
+        const double l1 = 0.5 - l0;
+
+        point_list[k + 0] = l0 * A + l0 * B + l1 * C + l1 * D;
+        point_list[k + 1] = l0 * A + l1 * B + l0 * C + l1 * D;
+        point_list[k + 2] = l0 * A + l1 * B + l1 * C + l0 * D;
+        point_list[k + 3] = l1 * A + l0 * B + l0 * C + l1 * D;
+        point_list[k + 4] = l1 * A + l0 * B + l1 * C + l0 * D;
+        point_list[k + 5] = l1 * A + l1 * B + l0 * C + l0 * D;
+
+        for (size_t l = 0; l < 6; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 6;
+        break;
+      }
+      case 4: {
+        Assert(lambda_list.size() == 2);
+        const double l0 = lambda_list[0];
+        const double l1 = lambda_list[1];
+        const double l2 = 1 - 2 * l0 - l1;
+
+        point_list[k + 0]  = l0 * A + l0 * B + l1 * C + l2 * D;
+        point_list[k + 1]  = l0 * A + l0 * B + l2 * C + l1 * D;
+        point_list[k + 2]  = l0 * A + l1 * B + l0 * C + l2 * D;
+        point_list[k + 3]  = l0 * A + l1 * B + l2 * C + l0 * D;
+        point_list[k + 4]  = l0 * A + l2 * B + l0 * C + l1 * D;
+        point_list[k + 5]  = l0 * A + l2 * B + l1 * C + l0 * D;
+        point_list[k + 6]  = l1 * A + l0 * B + l0 * C + l2 * D;
+        point_list[k + 7]  = l1 * A + l0 * B + l2 * C + l0 * D;
+        point_list[k + 8]  = l1 * A + l2 * B + l0 * C + l0 * D;
+        point_list[k + 9]  = l2 * A + l0 * B + l0 * C + l1 * D;
+        point_list[k + 10] = l2 * A + l0 * B + l1 * C + l0 * D;
+        point_list[k + 11] = l2 * A + l1 * B + l0 * C + l0 * D;
+
+        for (size_t l = 0; l < 12; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 12;
+        break;
+      }
+      case 5: {
+        Assert(lambda_list.size() == 3);
+        const double l0 = lambda_list[0];
+        const double l1 = lambda_list[1];
+        const double l2 = lambda_list[2];
+        const double l3 = 1 - l0 - l1 - l2;
+
+        point_list[k + 0]  = l0 * A + l1 * B + l2 * C + l3 * D;
+        point_list[k + 1]  = l0 * A + l1 * B + l3 * C + l2 * D;
+        point_list[k + 2]  = l0 * A + l2 * B + l1 * C + l3 * D;
+        point_list[k + 3]  = l0 * A + l2 * B + l3 * C + l1 * D;
+        point_list[k + 4]  = l0 * A + l3 * B + l1 * C + l2 * D;
+        point_list[k + 5]  = l0 * A + l3 * B + l2 * C + l1 * D;
+        point_list[k + 6]  = l1 * A + l0 * B + l2 * C + l3 * D;
+        point_list[k + 7]  = l1 * A + l0 * B + l3 * C + l2 * D;
+        point_list[k + 8]  = l1 * A + l2 * B + l0 * C + l3 * D;
+        point_list[k + 9]  = l1 * A + l2 * B + l3 * C + l0 * D;
+        point_list[k + 10] = l1 * A + l3 * B + l0 * C + l2 * D;
+        point_list[k + 11] = l1 * A + l3 * B + l2 * C + l0 * D;
+        point_list[k + 12] = l2 * A + l0 * B + l1 * C + l3 * D;
+        point_list[k + 13] = l2 * A + l0 * B + l3 * C + l1 * D;
+        point_list[k + 14] = l2 * A + l1 * B + l0 * C + l3 * D;
+        point_list[k + 15] = l2 * A + l1 * B + l3 * C + l0 * D;
+        point_list[k + 16] = l2 * A + l3 * B + l0 * C + l1 * D;
+        point_list[k + 17] = l2 * A + l3 * B + l1 * C + l0 * D;
+        point_list[k + 18] = l3 * A + l0 * B + l1 * C + l2 * D;
+        point_list[k + 19] = l3 * A + l0 * B + l2 * C + l1 * D;
+        point_list[k + 20] = l3 * A + l1 * B + l0 * C + l2 * D;
+        point_list[k + 21] = l3 * A + l1 * B + l2 * C + l0 * D;
+        point_list[k + 22] = l3 * A + l2 * B + l0 * C + l1 * D;
+        point_list[k + 23] = l3 * A + l2 * B + l1 * C + l0 * D;
+
+        for (size_t l = 0; l < 24; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 24;
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("invalid quadrature id");
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+  };
+
+  switch (degree) {
+  case 0:
+  case 1: {
+    constexpr size_t nb_points = 1;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.000000000000000e+00, {}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 2: {
+    constexpr size_t nb_points = 4;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.500000000000000e-01, {1.381966011250105e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 3: {
+    constexpr size_t nb_points = 8;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.274913115575064e-01, {3.286811466653490e-01}},
+       Descriptor{2, 1.225086884424936e-01, {1.119207275092916e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 4: {
+    constexpr size_t nb_points = 14;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 6.540916363445218e-02, {3.095821653179519e-01}},
+       Descriptor{2, 5.935526423100475e-02, {8.344277230397758e-02}},
+       Descriptor{3, 8.349038142302871e-02, {4.227790459299413e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 5: {
+    constexpr size_t nb_points = 14;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.126879257180158e-01, {3.108859192633006e-01}},
+       Descriptor{2, 7.349304311636196e-02, {9.273525031089122e-02}},
+       Descriptor{3, 4.254602077708147e-02, {4.544962958743504e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 6: {
+    constexpr size_t nb_points = 24;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.007721105532064e-02, {4.067395853461135e-02}},
+       Descriptor{2, 5.535718154365472e-02, {3.223378901422755e-01}},
+       Descriptor{2, 3.992275025816749e-02, {2.146028712591520e-01}},
+       Descriptor{4, 4.821428571428572e-02, {6.366100187501753e-02, 6.030056647916492e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 7: {
+    constexpr size_t nb_points = 35;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 9.548528946413085e-02, {}}, Descriptor{2, 4.232958120996703e-02, {3.157011497782028e-01}},
+       Descriptor{3, 3.189692783285758e-02, {4.495101774016036e-01}},
+       Descriptor{4, 8.110770829903342e-03, {2.126547254148325e-02, 8.108302410985485e-01}},
+       Descriptor{4, 3.720713072833462e-02, {1.888338310260010e-01, 5.751716375870001e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 8: {
+    constexpr size_t nb_points = 46;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 4.781298803430666e-02, {1.868127582707210e-01}},
+       Descriptor{2, 2.972785408679422e-02, {1.144624067612305e-01}},
+       Descriptor{2, 4.352732421897527e-02, {3.138065922724016e-01}},
+       Descriptor{2, 8.632736020814975e-03, {4.457737944884625e-02}},
+       Descriptor{3, 3.689054126986636e-02, {6.548349054384239e-02}},
+       Descriptor{4, 1.449702671085950e-02, {2.038931746621103e-01, 5.073320750421590e-03}},
+       Descriptor{4, 7.157401867243608e-03, {2.124777966957167e-02, 7.146769263933049e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 9: {
+    constexpr size_t nb_points = 59;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 5.686662425355173e-02, {}},
+       Descriptor{2, 2.569427668952318e-02, {1.679066052367428e-01}},
+       Descriptor{2, 2.298923353028370e-03, {9.143627051407625e-02}},
+       Descriptor{2, 3.045603866759101e-02, {3.218556648176533e-01}},
+       Descriptor{2, 7.123722340238881e-03, {4.183769590036560e-02}},
+       Descriptor{3, 3.684365548094388e-02, {1.067294385748464e-01}},
+       Descriptor{4, 1.032205678220985e-02, {3.395716818308889e-02, 7.183930939814244e-01}},
+       Descriptor{4, 7.634887153980855e-03, {4.606581810547776e-01, 7.868363447668793e-02}},
+       Descriptor{4, 2.035802261874757e-02, {1.843879435000152e-01, 5.972107227618499e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 10: {
+    constexpr size_t nb_points = 81;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 5.165089225609747e-02, {}},
+       Descriptor{2, 1.346797267166026e-02, {7.255175741743780e-02}},
+       Descriptor{2, 2.646691924968953e-02, {3.078959478669229e-01}},
+       Descriptor{3, 5.107047828143525e-03, {2.371913960722093e-02}},
+       Descriptor{3, 2.364999615824560e-02, {4.034202269308927e-01}},
+       Descriptor{4, 2.531736665096822e-02, {2.017386068476799e-01, 7.385418101848620e-02}},
+       Descriptor{4, 5.416263407447926e-03, {1.548397007438618e-01, 6.875048558830396e-01}},
+       Descriptor{4, 8.153659012054226e-03, {4.125991504993639e-01, 1.741437164939321e-01}},
+       Descriptor{4, 1.112597175961278e-02, {3.889072755780651e-02, 2.657575065627199e-01}},
+       Descriptor{4, 1.325678848264236e-03, {5.604847951021338e-03, 9.324134903525841e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 11: {
+    constexpr size_t nb_points = 110;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.468934358613075e-02, {2.993130923993425e-01}},
+       Descriptor{2, 3.380869530621681e-03, {3.263284396518180e-02}},
+       Descriptor{3, 1.676676867722563e-02, {1.742044154846892e-01}},
+       Descriptor{3, 1.932364034775851e-02, {4.012153758583749e-01}},
+       Descriptor{3, 1.653253610375797e-03, {4.942980948772996e-01}},
+       Descriptor{4, 9.799971247032874e-03, {1.231206549747224e-01, 7.273303463463835e-01}},
+       Descriptor{4, 2.036278491304826e-03, {1.300387048559024e-02, 1.702280734892642e-01}},
+       Descriptor{4, 8.207613752916718e-03, {4.293540117192574e-02, 6.182598097891158e-01}},
+       Descriptor{4, 1.510498736666349e-02, {1.844240320470778e-01, 5.399001168557016e-01}},
+       Descriptor{4, 1.274239266610639e-02, {2.591694074642826e-01, 4.554353196219595e-01}},
+       Descriptor{5, 5.273427059689129e-03, {5.351420009314595e-01, 1.097757236596031e-02, 3.414518661958190e-01}}
+
+      };
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 12: {
+    constexpr size_t nb_points = 168;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.761491553503257e-02, {2.135679944533018e-01}},
+       Descriptor{2, 8.351235933432746e-03, {8.080469951147343e-02}},
+       Descriptor{2, 1.777571920669439e-02, {1.460894685275485e-01}},
+       Descriptor{3, 1.016802439757214e-02, {4.359346229622011e-01}},
+       Descriptor{3, 1.574040002395383e-02, {3.723816950753983e-01}},
+       Descriptor{4, 1.511281145864287e-03, {1.481472606744865e-02, 6.940351772721454e-01}},
+       Descriptor{4, 8.726569887485608e-04, {4.406791967562980e-02, 5.787943875724942e-04}},
+       Descriptor{4, 3.712882661457528e-03, {2.900481455515819e-02, 7.927838364729656e-01}},
+       Descriptor{4, 2.358314255727376e-03, {1.384125788015036e-01, 7.217318354185758e-01}},
+       Descriptor{5, 4.490847036271700e-03, {1.155183527100142e-02, 2.002685156661767e-01, 4.459556015690982e-01}},
+       Descriptor{5, 7.549616048899519e-03, {7.295863195082626e-02, 2.545920450251540e-01, 4.229332866644536e-01}},
+       Descriptor{5, 1.850514589067692e-03, {5.001810761518710e-02, 3.937338659984053e-01, 5.538497716858680e-01}},
+       Descriptor{5, 9.780703581954099e-03, {2.518367824271116e-01, 3.608735666657864e-02, 5.985486290544021e-01}}
+
+      };
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 13: {
+    constexpr size_t nb_points = 172;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 8.606417789141471e-04, {2.023816786180974e-02}},
+       Descriptor{2, 2.260732681773922e-02, {2.890147352435263e-01}},
+       Descriptor{2, 9.680037326838918e-03, {9.402870008212708e-02}},
+       Descriptor{2, 7.170940325572750e-03, {1.976498544437255e-01}},
+       Descriptor{3, 7.784870979280401e-03, {4.046189676018364e-02}},
+       Descriptor{3, 5.120271481716123e-04, {4.999998048519694e-01}},
+       Descriptor{4, 8.880272171422971e-03, {6.752926495280243e-02, 6.118971302042935e-01}},
+       Descriptor{4, 1.062144779049374e-02, {1.360105145132029e-01, 4.868308338798159e-01}},
+       Descriptor{4, 5.873089331995857e-03, {2.780783459563702e-01, 4.273786080349000e-01}},
+       Descriptor{4, 9.235535397556015e-03, {1.967550197192861e-01, 5.667528680967985e-01}},
+       Descriptor{4, 1.456732207916522e-02, {3.862498629203497e-01, 6.201970724197543e-02}},
+       Descriptor{4, 2.658739862173390e-03, {2.353551905465277e-02, 1.086607820833199e-01}},
+       Descriptor{5, 3.221852775001763e-03, {3.362108930747719e-01, 5.446964977019162e-01, 1.155925493534321e-01}},
+       Descriptor{5, 1.464206741103990e-03, {6.859816301235152e-01, 2.886160494075550e-02, 2.745833302490004e-01}},
+       Descriptor{5, 2.268354927450138e-03, {7.356772467818598e-01, 1.545866468273804e-01, 1.103371356908743e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 14: {
+    constexpr size_t nb_points = 204;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 3.445433050323388e-03, {3.298151517846193e-01}},
+       Descriptor{2, 2.621255271870497e-03, {5.753828268975920e-02}},
+       Descriptor{2, 1.107383241893503e-04, {5.856168613783504e-03}},
+       Descriptor{2, 1.002858537247380e-02, {1.605554758479567e-01}},
+       Descriptor{2, 6.790408457809412e-03, {9.873964607404909e-02}},
+       Descriptor{2, 7.118148341899285e-03, {2.080531961597265e-01}},
+       Descriptor{3, 1.060052294564051e-03, {4.902479711877765e-01}},
+       Descriptor{3, 1.366983099498089e-02, {1.029394001155326e-01}},
+       Descriptor{3, 2.690741094676155e-03, {3.791346346121514e-02}},
+       Descriptor{3, 1.016460122584274e-02, {3.185735761838604e-01}},
+       Descriptor{4, 1.354439963772740e-02, {2.499092188634998e-01, 4.189387788376669e-01}},
+       Descriptor{4, 4.623340047669791e-03, {2.132633780618757e-01, 5.628766802497838e-01}},
+       Descriptor{4, 8.331498452473301e-04, {4.918128494015905e-02, 8.954689727140162e-01}},
+       Descriptor{4, 7.240296760857249e-03, {3.928626179700601e-01, 2.066357892967347e-02}},
+       Descriptor{4, 8.751807809978078e-04, {1.261760553257071e-02, 1.269022024074958e-01}},
+       Descriptor{5, 7.172536518098438e-03, {1.216187843486990e-01, 6.214000311762153e-02, 2.395005950785847e-01}},
+       Descriptor{5, 4.286856342790781e-03, {3.489022960470246e-01, 5.517941321184247e-01, 8.378145261134291e-02}},
+       Descriptor{5, 9.757527641506894e-04, {6.822383201682893e-01, 1.784164064765456e-02, 2.844407418708836e-01}},
+       Descriptor{5, 3.757936299766722e-03, {7.339982986585810e-01, 1.695395507001622e-01, 7.868554787111390e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 15: {
+    constexpr size_t nb_points = 264;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 5.112029771454935e-04, {1.692642158547220e-02}},
+       Descriptor{2, 1.620740592022658e-02, {2.836779254595722e-01}},
+       Descriptor{2, 1.552578602041314e-02, {1.821984139975859e-01}},
+       Descriptor{3, 1.219010259975421e-03, {1.450800515218459e-02}},
+       Descriptor{3, 8.472862853125917e-03, {1.427441437658564e-01}},
+       Descriptor{4, 1.797534305405733e-03, {7.321727256195354e-02, 7.766357493733004e-01}},
+       Descriptor{4, 8.198015220760061e-04, {1.056558410489699e-02, 2.225606554924344e-01}},
+       Descriptor{4, 7.681775272473012e-03, {2.637972626688146e-01, 5.575022240565973e-02}},
+       Descriptor{4, 4.479850385774781e-03, {4.354902702993817e-01, 1.112909267055329e-01}},
+       Descriptor{4, 5.615298608384355e-03, {5.347259364185193e-02, 5.448865322975603e-01}},
+       Descriptor{4, 1.769703685797290e-03, {1.105308939580100e-01, 7.694703779405406e-01}},
+       Descriptor{5, 1.131138329187022e-03, {4.104670514991076e-02, 6.030936440770986e-01, 2.328924415481402e-03}},
+       Descriptor{5, 8.404993407577487e-04, {9.201667327695258e-02, 2.939319332917082e-02, 8.672646051241815e-01}},
+       Descriptor{5, 3.011968607402684e-03, {6.822627868000052e-02, 7.077814873155971e-01, 2.200643755861662e-02}},
+       Descriptor{5, 5.084429327522668e-03, {1.890015556386176e-01, 1.295809597674534e-01, 6.807532978800637e-02}},
+       Descriptor{5, 7.139383728003996e-03, {3.054799663906012e-01, 4.695333003662676e-01, 7.251484323369149e-02}},
+       Descriptor{5, 3.378550568229294e-03, {1.509145500825105e-01, 5.778346261482753e-01, 1.112774031280626e-02}},
+       Descriptor{5, 2.201680777701462e-03, {3.368450658547645e-01, 1.082194753682293e-02, 4.176069528643034e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 16: {
+    constexpr size_t nb_points = 304;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 4.034878937238870e-03, {3.296714738440606e-01}},
+       Descriptor{2, 5.263428008047628e-03, {1.120421044173788e-01}},
+       Descriptor{2, 1.078639106857764e-02, {2.804460259110929e-01}},
+       Descriptor{2, 1.892340029030997e-03, {3.942164444076166e-02}},
+       Descriptor{3, 6.014511003000459e-03, {7.491741856476755e-02}},
+       Descriptor{3, 8.692191232873023e-03, {3.356931029556346e-01}},
+       Descriptor{4, 3.507623285784993e-03, {4.904898759556675e-02, 7.646870675801803e-01}},
+       Descriptor{4, 1.188662273319198e-03, {1.412609568309253e-02, 2.328268045894251e-01}},
+       Descriptor{4, 4.345225314466667e-03, {6.239652058154325e-02, 2.832417683077947e-01}},
+       Descriptor{4, 2.660686570203166e-03, {1.890959275696560e-01, 1.283187405611824e-02}},
+       Descriptor{4, 5.553174302124013e-03, {2.750176001295444e-01, 3.872709603194903e-01}},
+       Descriptor{4, 1.531431405395808e-04, {5.944898252569946e-03, 3.723805935523542e-02}},
+       Descriptor{4, 2.231204136715679e-03, {1.183058071099944e-01, 7.482941078308859e-01}},
+       Descriptor{5, 2.472377156249970e-03, {8.011846127872502e-02, 5.146357887888395e-01, 3.908021114187921e-01}},
+       Descriptor{5, 6.596122891817925e-03, {3.102585498627273e-01, 1.645739468379099e-01, 6.995093322963369e-02}},
+       Descriptor{5, 6.004349067697421e-04, {1.085240801928985e-01, 3.435867950145696e-02, 8.557156992205752e-01}},
+       Descriptor{5, 2.242330996876527e-03, {2.483824987814955e-01, 6.625317544850510e-01, 1.098323448764900e-02}},
+       Descriptor{5, 8.244422362471837e-04, {3.960091211067035e-01, 1.226898678006519e-02, 1.878187449597510e-02}},
+       Descriptor{5, 4.388015835267911e-03, {6.367516197137306e-02, 2.054604991324105e-01, 1.362449508885895e-01}},
+       Descriptor{5, 3.454278425740962e-03, {1.757650466139104e-01, 4.610678860796995e-01, 1.387535709612253e-01}},
+       Descriptor{5, 3.929289473335571e-03, {4.779942532006705e-01, 1.344788610299629e-02, 3.221398306538996e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 17: {
+    constexpr size_t nb_points = 364;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.943411054682655e-03, {1.092594110391400e-01}},
+       Descriptor{2, 4.821254562130973e-04, {1.706905335350991e-02}},
+       Descriptor{2, 1.633411882335971e-03, {6.030276440208671e-02}},
+       Descriptor{2, 8.937260544733967e-03, {1.802936778752487e-01}},
+       Descriptor{3, 3.539281041131444e-03, {2.075642494319923e-01}},
+       Descriptor{3, 5.127876775122913e-03, {6.350561875036179e-02}},
+       Descriptor{3, 8.505961705776655e-03, {1.425740948569834e-01}},
+       Descriptor{3, 1.362001059565090e-03, {1.525498961135818e-02}},
+       Descriptor{4, 1.952703594699083e-03, {2.817717468661823e-01, 6.605628545936354e-03}},
+       Descriptor{4, 3.552482332702156e-03, {2.532792896616394e-01, 3.473076755269789e-01}},
+       Descriptor{4, 4.735037872087881e-03, {1.194446836887992e-01, 5.129631932135305e-01}},
+       Descriptor{4, 4.076204052523780e-03, {2.796975892179978e-01, 4.040135150131379e-01}},
+       Descriptor{4, 4.397889616038591e-03, {5.914965001755916e-02, 2.771505768037979e-01}},
+       Descriptor{4, 8.159915418039479e-04, {1.156645687972039e-02, 3.372693231713962e-01}},
+       Descriptor{4, 6.436927403356441e-03, {2.546200946118619e-01, 8.899213511967391e-02}},
+       Descriptor{4, 2.290937029978224e-04, {6.672914677865158e-03, 8.335377000797282e-02}},
+       Descriptor{4, 3.188746694204980e-03, {5.954996366173630e-02, 1.488738514609226e-01}},
+       Descriptor{5, 1.991266259969879e-03, {9.563829425828949e-02, 1.308047471312760e-02, 1.683493064182302e-01}},
+       Descriptor{5, 1.209946636868781e-03, {6.035751534893870e-02, 6.376551993416253e-01, 1.062246299591423e-02}},
+       Descriptor{5, 2.943417982183902e-03, {5.720629662753272e-01, 1.575062824084584e-01, 1.289545879798304e-02}},
+       Descriptor{5, 3.454094554810564e-03, {5.982871618499548e-01, 1.530963612221368e-01, 6.489065131906320e-02}},
+       Descriptor{5, 8.241135414777571e-04, {8.721077921772252e-02, 1.165500297046612e-02, 4.707766223753580e-02}},
+       Descriptor{5, 1.383542378170185e-03, {4.137799680661914e-01, 4.085328910163256e-01, 1.048009320013415e-02}},
+       Descriptor{5, 1.781445235926538e-03, {3.904989583719425e-01, 1.167932299246964e-02, 7.360595669274642e-02}},
+       Descriptor{5, 6.969316351001401e-04, {7.735017403534717e-01, 8.163680371232981e-03, 2.545430962429988e-02}},
+       Descriptor{5, 5.722888401891606e-03, {5.603440494408158e-02, 1.529886055031752e-01, 3.226986469260043e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 18: {
+    constexpr size_t nb_points = 436;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.505388406750596e-03, {2.063721818681210e-01}},
+       Descriptor{2, 7.201441531528436e-03, {1.625387945176406e-01}},
+       Descriptor{2, 5.801801233694815e-03, {3.116266728255643e-01}},
+       Descriptor{2, 2.465088392849791e-03, {3.307279160890313e-01}},
+       Descriptor{2, 8.235271059110021e-04, {3.374408201299506e-02}},
+       Descriptor{2, 2.396153395824959e-03, {6.568138278425954e-02}},
+       Descriptor{2, 1.662026538062453e-04, {1.169072420259711e-02}},
+       Descriptor{3, 4.747626606883917e-05, {3.051729141214794e-01}},
+       Descriptor{3, 5.654960404175805e-04, {2.498171653415399e-01}},
+       Descriptor{3, 1.126644847587598e-03, {1.336652291605620e-02}},
+       Descriptor{3, 1.348246151880201e-03, {2.503423435758855e-01}},
+       Descriptor{3, 3.657069096677683e-04, {3.324517722542840e-01}},
+       Descriptor{3, 5.853171992565114e-03, {3.328935974705466e-01}},
+       Descriptor{3, 1.956691034821430e-03, {1.053889982941293e-01}},
+       Descriptor{3, 1.673244252179039e-03, {4.317721941366494e-02}},
+       Descriptor{4, 1.684409973125634e-03, {3.310306761504429e-02, 1.352754021303201e-01}},
+       Descriptor{4, 9.489733030971150e-04, {1.946185784467675e-01, 1.171879047047253e-03}},
+       Descriptor{4, 1.039083135931653e-03, {1.321252126131118e-02, 3.278960098149503e-01}},
+       Descriptor{4, 2.868558324240348e-03, {1.229320721770598e-01, 2.028399843152170e-02}},
+       Descriptor{4, 3.672202784572132e-03, {6.199506095168380e-02, 3.498961219623653e-01}},
+       Descriptor{4, 4.747155546596363e-03, {8.407187546991698e-02, 1.666830796952924e-01}},
+       Descriptor{4, 4.150884078212972e-04, {7.867048874453891e-03, 1.859179591228751e-01}},
+       Descriptor{4, 2.504990122247683e-03, {4.217582498871236e-02, 2.482707411503117e-01}},
+       Descriptor{4, 2.629919627710238e-04, {8.683491872138840e-03, 6.698102226768764e-02}},
+       Descriptor{4, 5.237948818645674e-03, {1.111185367576208e-01, 2.918417077981162e-01}},
+       Descriptor{5, 9.530517345502799e-04, {3.685463845606022e-03, 6.377078964518405e-02, 2.161982818015302e-01}},
+       Descriptor{5, 4.424079498028044e-04, {9.147563979493070e-02, 4.024098103223798e-03, 4.866034767929566e-02}},
+       Descriptor{5, 1.756179008422938e-03, {1.326563740184036e-01, 4.168774597385260e-01, 2.814320586294362e-02}},
+       Descriptor{5, 2.434266881750153e-03, {4.439198669278809e-01, 2.464330224801677e-01, 2.919443405619611e-02}},
+       Descriptor{5, 3.936511965938193e-03, {2.395450885895190e-01, 1.271207488070096e-01, 2.457574497117561e-01}},
+       Descriptor{5, 3.199791269106457e-03, {1.657130887168779e-01, 2.137049061861850e-01, 7.023964653694080e-02}},
+       Descriptor{5, 3.797610312158748e-03, {1.788776686026366e-01, 3.290353014877974e-01, 6.661063766146717e-02}},
+       Descriptor{5, 1.495938562630981e-03, {3.340176116726106e-01, 4.950315956949369e-03, 1.887999372824127e-01}},
+       Descriptor{5, 1.703996181224725e-03, {5.458773009126385e-01, 7.846288361681254e-03, 7.104964990519175e-02}},
+       Descriptor{5, 3.628775117699059e-03, {2.510439859007851e-02, 1.293557534662013e-01, 2.529882400374321e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 19: {
+    constexpr size_t nb_points = 487;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 9.477424964421555e-03, {}},
+       Descriptor{2, 2.309652046579032e-03, {9.623346794170115e-02}},
+       Descriptor{2, 4.091492762240734e-03, {2.840710971185555e-01}},
+       Descriptor{2, 1.173654667448287e-03, {4.201218049762310e-02}},
+       Descriptor{3, 5.797759302818667e-03, {1.293925012179526e-01}},
+       Descriptor{4, 2.028992639106921e-03, {5.339312125330306e-02, 7.679699947393867e-01}},
+       Descriptor{4, 2.005858485106433e-04, {5.250553906919603e-03, 1.660339253205299e-01}},
+       Descriptor{4, 2.908059972456899e-03, {2.128993323694659e-01, 1.034045995093328e-01}},
+       Descriptor{4, 2.532078463287134e-03, {4.034135325977229e-01, 1.333356346432022e-01}},
+       Descriptor{4, 7.343695736316139e-04, {1.188811930811271e-02, 5.532379678070896e-01}},
+       Descriptor{4, 1.076183574539400e-03, {8.903116551206627e-02, 8.080695151909102e-01}},
+       Descriptor{4, 1.986415677131464e-03, {5.151678695899160e-02, 4.873280365294901e-01}},
+       Descriptor{4, 5.046317364293947e-03, {1.179992852297671e-01, 2.466149943644083e-01}},
+       Descriptor{4, 2.692282460531053e-03, {4.608512334928590e-02, 2.781414318856127e-01}},
+       Descriptor{4, 1.330636288503801e-03, {1.410186708097898e-01, 8.174649948389451e-03}},
+       Descriptor{4, 1.782928900852855e-03, {1.411034408699267e-01, 5.855868767598970e-01}},
+       Descriptor{4, 1.263015826184454e-04, {7.911862204821108e-03, 3.035883809061935e-02}},
+       Descriptor{4, 3.244260943819173e-03, {1.761012114703174e-01, 2.461239587816849e-01}},
+       Descriptor{5, 1.048498185712524e-03, {4.531137441520981e-02, 7.694124041149815e-01, 1.162671237541303e-02}},
+       Descriptor{5, 3.605740745734604e-03, {1.902469770309511e-01, 1.139907343389768e-01, 4.902896398807803e-02}},
+       Descriptor{5, 2.561558407869844e-03, {2.025882107314621e-01, 5.045224197189542e-01, 5.200356315054191e-02}},
+       Descriptor{5, 1.498918467834416e-03, {6.280459865457699e-02, 3.899342137938394e-01, 9.651021927014570e-03}},
+       Descriptor{5, 1.814324572327483e-03, {1.689034611068301e-01, 1.025101977116908e-02, 2.542260337897459e-01}},
+       Descriptor{5, 2.883938073996170e-03, {1.282802298826546e-01, 3.714709314621824e-01, 2.826241348940526e-01}},
+       Descriptor{5, 3.191411305221261e-03, {3.753164302248334e-01, 2.323738231259893e-01, 5.648574604597608e-02}},
+       Descriptor{5, 3.955282336986757e-04, {8.176634693298535e-02, 2.823323910477210e-02, 8.840321921276471e-01}},
+       Descriptor{5, 1.642211231915762e-03, {3.727215008878210e-01, 4.691373442926859e-01, 1.052014400647361e-02}},
+       Descriptor{5, 4.640920897459689e-04, {2.881077012617455e-01, 6.241150434253934e-03, 2.040787180394686e-02}},
+       Descriptor{5, 1.210865339994028e-03, {8.483677030488673e-03, 2.548571009364550e-01, 8.369886684600136e-02}},
+       Descriptor{5, 3.653283017278065e-03, {1.193128036212033e-01, 5.081225906571617e-01, 5.013138003585081e-02}},
+       Descriptor{5, 1.744791238762615e-03, {4.198369201417426e-01, 1.094996106379021e-02, 3.155028856836414e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 20: {
+    constexpr size_t nb_points = 552;
+    SmallArray<R3> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 5.418454790084648e-03, {2.967759596934131e-01}},
+       Descriptor{2, 3.820897988248627e-03, {1.209047972112185e-01}},
+       Descriptor{2, 4.057458152334048e-03, {3.177959071881044e-01}},
+       Descriptor{2, 4.147670297750325e-03, {2.012655712414790e-01}},
+       Descriptor{2, 5.289566609934361e-03, {1.678838969272885e-01}},
+       Descriptor{2, 9.720418198623532e-04, {3.621493960968947e-02}},
+       Descriptor{3, 2.988176136780452e-03, {5.260244961748377e-02}},
+       Descriptor{3, 7.061746923528361e-03, {3.045785563313042e-01}},
+       Descriptor{4, 1.138014164865969e-03, {3.228230549839516e-02, 8.181532733687802e-01}},
+       Descriptor{4, 3.284328089439585e-04, {8.126514470519638e-03, 1.671963931828212e-01}},
+       Descriptor{4, 4.517305074533165e-03, {1.851459522994347e-01, 6.611011876662430e-02}},
+       Descriptor{4, 2.743957098139693e-03, {4.110775382017770e-01, 1.281831845252744e-01}},
+       Descriptor{4, 5.345104029866354e-04, {1.005979740685446e-02, 5.566366248307155e-01}},
+       Descriptor{4, 8.527602773365834e-04, {8.230161503723588e-02, 8.242515811428504e-01}},
+       Descriptor{4, 1.608673320950611e-03, {3.801282860266467e-02, 6.072922714391159e-01}},
+       Descriptor{4, 2.164021789623034e-03, {8.297816905794758e-02, 1.927591204214162e-01}},
+       Descriptor{4, 1.298964551990821e-03, {3.232830149045374e-02, 2.290899793113822e-01}},
+       Descriptor{4, 7.540221084980848e-04, {1.406521940177762e-01, 4.062459178585371e-03}},
+       Descriptor{4, 3.936392177197076e-03, {1.139719439112915e-01, 5.168656572693773e-01}},
+       Descriptor{4, 8.260437254311268e-05, {7.453571377174895e-03, 2.517856316811188e-02}},
+       Descriptor{4, 2.202593581265821e-03, {1.110171267302689e-01, 3.907070674791312e-01}},
+       Descriptor{5, 1.142912288312416e-03, {5.313810503913767e-02, 5.333767142737392e-01, 9.768492667580009e-03}},
+       Descriptor{5, 8.037341179884985e-04, {9.439207428728114e-02, 9.450517146275739e-02, 7.609480923624367e-01}},
+       Descriptor{5, 6.441055085633393e-04, {5.505445164272958e-02, 7.653955692270024e-01, 5.660747947619223e-03}},
+       Descriptor{5, 2.284803061568028e-03, {1.798637301234305e-01, 9.751720382220438e-02, 3.466187077541209e-02}},
+       Descriptor{5, 1.768215173403588e-03, {2.121920547752147e-01, 4.095036586170944e-01, 4.613078410418590e-02}},
+       Descriptor{5, 1.364093116604035e-03, {1.290161512351960e-01, 4.765557207500070e-01, 9.956030373782836e-03}},
+       Descriptor{5, 1.409513020242023e-03, {1.614873223066114e-01, 1.376084748677020e-02, 2.264813194055542e-01}},
+       Descriptor{5, 5.352196172820926e-03, {1.059283078939860e-01, 4.143500741402470e-01, 1.944202417194029e-01}},
+       Descriptor{5, 2.357251753798956e-03, {2.046229328456809e-01, 2.864083155254953e-01, 3.607794123452725e-02}},
+       Descriptor{5, 2.335500281519085e-04, {7.441796869877018e-02, 2.361583233015298e-02, 8.995240292632410e-01}},
+       Descriptor{5, 8.299191110448388e-04, {2.938352359686784e-01, 5.078745759286403e-01, 4.379772363790332e-03}},
+       Descriptor{5, 3.480327275620159e-04, {2.899004919931303e-01, 2.388614940511808e-03, 2.095834844884252e-02}},
+       Descriptor{5, 1.081306497459309e-03, {6.920395527608953e-03, 2.780090233488638e-01, 8.666224435610116e-02}},
+       Descriptor{5, 3.363592992643566e-03, {1.053191068972152e-01, 5.431130574905816e-01, 4.625008859127536e-02}},
+       Descriptor{5, 1.138819523953005e-03, {3.387615700292962e-01, 8.792325219258773e-03, 4.002505388571689e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(TetrahedronGaussQuadrature::max_degree) + " on tetrahedra");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/analysis/TetrahedronGaussQuadrature.hpp b/src/analysis/TetrahedronGaussQuadrature.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..352ff079a409952d99af6931bf75b8b84b06d622
--- /dev/null
+++ b/src/analysis/TetrahedronGaussQuadrature.hpp
@@ -0,0 +1,38 @@
+#ifndef TETRAHEDRON_GAUSS_QUADRATURE_HPP
+#define TETRAHEDRON_GAUSS_QUADRATURE_HPP
+
+#include <analysis/QuadratureFormula.hpp>
+
+/**
+ * Defines Gauss quadrature on the P1 reference tetrahedron element
+ *
+ * \note formulae are provided by
+ *
+ * 'High-order symmetric cubature rules for tetrahedra and pyramids'
+ * Jan Jaśkowiec & N. Sukumar (2020).
+ */
+class TetrahedronGaussQuadrature final : public QuadratureFormula<3>
+{
+ public:
+  constexpr static size_t max_degree = 20;
+
+ private:
+  void _buildPointAndWeightLists(const size_t degree);
+
+ public:
+  TetrahedronGaussQuadrature(TetrahedronGaussQuadrature&&)      = default;
+  TetrahedronGaussQuadrature(const TetrahedronGaussQuadrature&) = default;
+
+ private:
+  friend class QuadratureManager;
+
+  explicit TetrahedronGaussQuadrature(const size_t degree) : QuadratureFormula<3>(QuadratureType::Gauss)
+  {
+    this->_buildPointAndWeightLists(degree);
+  }
+
+  TetrahedronGaussQuadrature()  = delete;
+  ~TetrahedronGaussQuadrature() = default;
+};
+
+#endif   // TETRAHEDRON_GAUSS_QUADRATURE_HPP
diff --git a/src/analysis/TriangleGaussQuadrature.cpp b/src/analysis/TriangleGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..627edc4ff8293a7a00b689b5602fa2f8d20c3911
--- /dev/null
+++ b/src/analysis/TriangleGaussQuadrature.cpp
@@ -0,0 +1,492 @@
+#include <analysis/TriangleGaussQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+void
+TriangleGaussQuadrature::_buildPointAndWeightLists(const size_t degree)
+{
+  using R2 = TinyVector<2>;
+
+  struct Descriptor
+  {
+    int id;
+    double weight;
+    std::vector<double> lambda_list;
+  };
+
+  auto fill_quadrature_points = [](auto descriptor_list, auto& point_list, auto& weight_list) {
+    Assert(point_list.size() == weight_list.size());
+
+    const R2 A{0, 0};
+    const R2 B{1, 0};
+    const R2 C{0, 1};
+
+    size_t k = 0;
+    for (size_t i = 0; i < descriptor_list.size(); ++i) {
+      const auto [id, w, position_list] = descriptor_list[i];
+
+      switch (id) {
+      case 1: {
+        Assert(position_list.size() == 0);
+
+        point_list[k]  = (1. / 3) * (A + B + C);
+        weight_list[k] = w;
+
+        k += 1;
+        break;
+      }
+      case 2: {
+        Assert(position_list.size() == 1);
+        const double& l0 = position_list[0];
+        const double& l1 = 1 - 2 * l0;
+
+        point_list[k + 0] = l0 * A + l0 * B + l1 * C;
+        point_list[k + 1] = l0 * A + l1 * B + l0 * C;
+        point_list[k + 2] = l1 * A + l0 * B + l0 * C;
+
+        for (size_t l = 0; l < 3; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 3;
+        break;
+      }
+      case 3: {
+        Assert(position_list.size() == 2);
+        const double& l0 = position_list[0];
+        const double& l1 = position_list[1];
+        const double& l2 = 1 - l0 - l1;
+
+        point_list[k + 0] = l0 * A + l1 * B + l2 * C;
+        point_list[k + 1] = l0 * A + l2 * B + l1 * C;
+        point_list[k + 2] = l1 * A + l0 * B + l2 * C;
+        point_list[k + 3] = l1 * A + l2 * B + l0 * C;
+        point_list[k + 4] = l2 * A + l0 * B + l1 * C;
+        point_list[k + 5] = l2 * A + l1 * B + l0 * C;
+
+        for (size_t l = 0; l < 6; ++l) {
+          weight_list[k + l] = w;
+        }
+
+        k += 6;
+        break;
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("invalid quadrature id");
+      }
+        // LCOV_EXCL_STOP
+      }
+    }
+
+    Assert(k == point_list.size(), "invalid number of quadrature points");
+  };
+
+  switch (degree) {
+  case 0:
+  case 1: {
+    constexpr size_t nb_points = 1;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 5.000000000000000e-01, {}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 2: {
+    constexpr size_t nb_points = 3;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.666666666666667e-01, {1.666666666666667e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 3:
+  case 4: {
+    constexpr size_t nb_points = 6;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.116907948390057e-01, {4.459484909159649e-01}},
+       Descriptor{2, 5.497587182766094e-02, {9.157621350977074e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 5: {
+    constexpr size_t nb_points = 7;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.125000000000000e-01, {}}, Descriptor{2, 6.296959027241357e-02, {1.012865073234563e-01}},
+       Descriptor{2, 6.619707639425310e-02, {4.701420641051151e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 6: {
+    constexpr size_t nb_points = 12;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.542245318510341e-02, {6.308901449150223e-02}},
+       Descriptor{2, 5.839313786318968e-02, {2.492867451709104e-01}},
+       Descriptor{3, 4.142553780918679e-02, {3.103524510337844e-01, 5.314504984481695e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 7: {
+    constexpr size_t nb_points = 15;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 8.272525055396066e-03, {3.373064855458785e-02}},
+       Descriptor{2, 6.397208561507779e-02, {2.415773825954036e-01}},
+       Descriptor{2, 3.854332309299303e-02, {4.743096925047182e-01}},
+       Descriptor{3, 2.793936645159989e-02, {1.986833147973516e-01, 4.703664465259523e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 8: {
+    constexpr size_t nb_points = 16;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 7.215780383889359e-02, {}}, Descriptor{2, 4.754581713364231e-02, {4.592925882927232e-01}},
+       Descriptor{2, 5.160868526735912e-02, {1.705693077517602e-01}},
+       Descriptor{2, 1.622924881159904e-02, {5.054722831703098e-02}},
+       Descriptor{3, 1.361515708721750e-02, {2.631128296346381e-01, 8.394777409957605e-03}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 9: {
+    constexpr size_t nb_points = 19;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 4.856789814139942e-02, {}},
+       Descriptor{2, 3.891377050238714e-02, {4.370895914929366e-01}},
+       Descriptor{2, 3.982386946360512e-02, {1.882035356190327e-01}},
+       Descriptor{2, 1.566735011356954e-02, {4.896825191987376e-01}},
+       Descriptor{2, 1.278883782934902e-02, {4.472951339445271e-02}},
+       Descriptor{3, 2.164176968864469e-02, {2.219629891607657e-01, 3.683841205473629e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 10: {
+    constexpr size_t nb_points = 25;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 4.087166457314299e-02, {}},
+       Descriptor{2, 6.676484406574783e-03, {3.205537321694351e-02}},
+       Descriptor{2, 2.297898180237237e-02, {1.421611010565644e-01}},
+       Descriptor{3, 3.195245319821202e-02, {1.481328857838206e-01, 3.218129952888354e-01}},
+       Descriptor{3, 1.709232408147971e-02, {3.691467818278110e-01, 2.961988948872977e-02}},
+       Descriptor{3, 1.264887885364419e-02, {1.637017337371825e-01, 2.836766533993844e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 11: {
+    constexpr size_t nb_points = 28;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 4.288058986611211e-02, {}},
+       Descriptor{2, 5.215935256447348e-03, {2.848541761437191e-02}},
+       Descriptor{2, 3.525784205585829e-02, {2.102199567031783e-01}},
+       Descriptor{2, 1.931537961850966e-02, {1.026354827122464e-01}},
+       Descriptor{2, 8.303136527292684e-03, {4.958919009658909e-01}},
+       Descriptor{2, 3.365807703973415e-02, {4.384659267643522e-01}},
+       Descriptor{3, 5.145144786476639e-03, {7.325427686064452e-03, 1.493247886520824e-01}},
+       Descriptor{3, 2.016623832025028e-02, {2.895811256377058e-01, 4.601050016542996e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 12: {
+    constexpr size_t nb_points = 33;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.213341904072602e-02, {4.882037509455415e-01}},
+       Descriptor{2, 1.424302603443877e-02, {1.092578276593543e-01}},
+       Descriptor{2, 3.127060659795138e-02, {2.714625070149261e-01}},
+       Descriptor{2, 3.965821254986819e-03, {2.464636343633559e-02}},
+       Descriptor{2, 2.495916746403047e-02, {4.401116486585931e-01}},
+       Descriptor{3, 1.089179251930378e-02, {2.303415635526714e-02, 2.916556797383409e-01}},
+       Descriptor{3, 2.161368182970710e-02, {2.554542286385174e-01, 1.162960196779266e-01}},
+       Descriptor{3, 7.541838788255719e-03, {2.138249025617059e-02, 8.513377925102400e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 13: {
+    constexpr size_t nb_points = 37;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 3.398001829341582e-02, {}},
+       Descriptor{2, 1.199720096444737e-02, {4.890769464525394e-01}},
+       Descriptor{2, 2.913924255959999e-02, {2.213722862918329e-01}},
+       Descriptor{2, 2.780098376522666e-02, {4.269414142598004e-01}},
+       Descriptor{2, 3.026168551769586e-03, {2.150968110884318e-02}},
+       Descriptor{3, 1.208951990579691e-02, {1.635974010678505e-01, 8.789548303219732e-02}},
+       Descriptor{3, 7.482700552582834e-03, {2.437018690109383e-02, 1.109220428034634e-01}},
+       Descriptor{3, 1.732063807042419e-02, {6.801224355420665e-02, 3.084417608921178e-01}},
+       Descriptor{3, 4.795340501771632e-03, {2.725158177734296e-01, 5.126389102382369e-03}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 14: {
+    constexpr size_t nb_points = 42;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 2.108129436849651e-02, {1.772055324125434e-01}},
+       Descriptor{2, 1.639417677206267e-02, {4.176447193404539e-01}},
+       Descriptor{2, 7.216849834888334e-03, {6.179988309087260e-02}},
+       Descriptor{2, 1.094179068471444e-02, {4.889639103621786e-01}},
+       Descriptor{2, 2.588705225364579e-02, {2.734775283088386e-01}},
+       Descriptor{2, 2.461701801200041e-03, {1.939096124870105e-02}},
+       Descriptor{3, 7.218154056766920e-03, {1.464695005565441e-02, 2.983728821362577e-01}},
+       Descriptor{3, 1.233287660628184e-02, {1.722666878213556e-01, 5.712475740364794e-02}},
+       Descriptor{3, 1.928575539353034e-02, {9.291624935697182e-02, 3.368614597963450e-01}},
+       Descriptor{3, 2.505114419250336e-03, {1.189744976969569e-01, 1.268330932872025e-03}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 15: {
+    constexpr size_t nb_points = 49;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 2.216769369109204e-02, {}},
+       Descriptor{2, 2.135689078573028e-02, {4.053622141339755e-01}},
+       Descriptor{2, 8.222368781312581e-03, {7.017355289998602e-02}},
+       Descriptor{2, 8.698074000381707e-03, {4.741706814380198e-01}},
+       Descriptor{2, 2.339168086435481e-02, {2.263787134203496e-01}},
+       Descriptor{2, 4.786923091230043e-03, {4.949969567691262e-01}},
+       Descriptor{2, 1.480387318952688e-03, {1.581172625098864e-02}},
+       Descriptor{3, 7.801286415287982e-03, {3.146482428124508e-01, 1.837611238568109e-02}},
+       Descriptor{3, 2.014926686009050e-03, {7.094860523645553e-02, 9.139237037308396e-03}},
+       Descriptor{3, 1.436029346260067e-02, {9.424205359215536e-02, 1.905355894763939e-01}},
+       Descriptor{3, 5.836310590787923e-03, {1.863871372816638e-02, 1.680686452224144e-01}},
+       Descriptor{3, 1.565773814248464e-02, {9.579672364760859e-02, 3.389506114752772e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 16: {
+    constexpr size_t nb_points = 55;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 2.263228303690940e-02, {}},
+       Descriptor{2, 2.054646157184948e-02, {2.459900704671417e-01}},
+       Descriptor{2, 2.035591665621268e-02, {4.155848968854205e-01}},
+       Descriptor{2, 7.390817345112202e-03, {8.535556658670032e-02}},
+       Descriptor{2, 1.470920484949405e-02, {1.619186441912712e-01}},
+       Descriptor{2, 2.209273156075285e-03, {5.000000000000000e-01}},
+       Descriptor{2, 1.298716664913858e-02, {4.752807275459421e-01}},
+       Descriptor{3, 9.469136232207850e-03, {1.910747636405291e-01, 5.475517491470312e-02}},
+       Descriptor{3, 8.272333574175241e-04, {8.552204200227611e-03, 2.320342776881371e-02}},
+       Descriptor{3, 7.504300892142903e-03, {3.317645234741477e-01, 1.893177828040591e-02}},
+       Descriptor{3, 3.973796966696249e-03, {8.069616698587292e-02, 1.903012974369745e-02}},
+       Descriptor{3, 1.599180503968503e-02, {3.082449691963540e-01, 1.026061902393981e-01}},
+       Descriptor{3, 2.695593558424406e-03, {1.874417824837821e-01, 5.936350016822270e-03}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 17: {
+    constexpr size_t nb_points = 60;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{2, 1.365546326405105e-02, {4.171034443615992e-01}},
+       Descriptor{2, 1.386943788818821e-03, {1.475549166075395e-02}},
+       Descriptor{2, 1.250972547524868e-02, {4.655978716188903e-01}},
+       Descriptor{2, 1.315631529400899e-02, {1.803581162663706e-01}},
+       Descriptor{2, 6.229500401152721e-03, {6.665406347959693e-02}},
+       Descriptor{2, 1.885811857639764e-02, {2.857065024365866e-01}},
+       Descriptor{3, 3.989150102964797e-03, {1.591922874727927e-01, 1.601764236211930e-02}},
+       Descriptor{3, 1.124388627334553e-02, {6.734937786736120e-02, 3.062815917461865e-01}},
+       Descriptor{3, 5.199219977919768e-03, {4.154754592952291e-01, 1.322967276008689e-02}},
+       Descriptor{3, 1.027894916022726e-02, {1.687225134952595e-01, 7.804234056828242e-02}},
+       Descriptor{3, 4.346107250500596e-03, {2.717918700553548e-01, 1.313587083400269e-02}},
+       Descriptor{3, 2.292174200867934e-03, {7.250547079900242e-02, 1.157517590318062e-02}},
+       Descriptor{3, 1.308581296766849e-02, {2.992189424769703e-01, 1.575054779268699e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 18: {
+    constexpr size_t nb_points = 67;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.817786765071333e-02, {}},
+       Descriptor{2, 1.665223501669507e-02, {3.999556280675762e-01}},
+       Descriptor{2, 6.023323816999855e-03, {4.875803015748695e-01}},
+       Descriptor{2, 9.474585753389433e-03, {4.618095064064492e-01}},
+       Descriptor{2, 1.823754470447182e-02, {2.422647025142720e-01}},
+       Descriptor{2, 3.564663009859485e-03, {3.883025608868559e-02}},
+       Descriptor{2, 8.279579976001624e-03, {9.194774212164319e-02}},
+       Descriptor{3, 6.879808117471103e-03, {1.838227079254640e-01, 4.580491585986078e-02}},
+       Descriptor{3, 1.189095545007642e-02, {1.226967573719275e-01, 2.063492574338379e-01}},
+       Descriptor{3, 2.265267251128533e-03, {3.956834343322697e-01, 3.897611033473383e-03}},
+       Descriptor{3, 3.420055059803591e-03, {1.081957937910333e-01, 1.346201674144499e-02}},
+       Descriptor{3, 8.873744551010202e-03, {3.197516245253774e-01, 4.026028346990806e-02}},
+       Descriptor{3, 2.505330437289861e-03, {2.357721849581917e-01, 5.298335186609765e-03}},
+       Descriptor{3, 6.114740634805449e-04, {2.709091099516201e-02, 5.483600420423190e-04}},
+       Descriptor{3, 1.274108765591222e-02, {3.334935294498808e-01, 1.205876951639246e-01}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 19: {
+    constexpr size_t nb_points = 73;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.723469885200617e-02, {}},
+       Descriptor{2, 3.554628298899065e-03, {5.252389035120897e-02}},
+       Descriptor{2, 5.160877571472141e-03, {4.925126750413369e-01}},
+       Descriptor{2, 7.617175546509150e-03, {1.114488733230214e-01}},
+       Descriptor{2, 1.149179501337080e-02, {4.591942010395437e-01}},
+       Descriptor{2, 1.576876744657749e-02, {4.039697225519012e-01}},
+       Descriptor{2, 1.232595742409543e-02, {1.781701047817643e-01}},
+       Descriptor{2, 8.826613882214238e-04, {1.163946118378945e-02}},
+       Descriptor{2, 1.587650968300154e-02, {2.551616329136077e-01}},
+       Descriptor{3, 4.847742243427523e-03, {3.914585933169222e-02, 1.306976762680324e-01}},
+       Descriptor{3, 1.317316098869537e-02, {1.293125644701578e-01, 3.113176298095413e-01}},
+       Descriptor{3, 1.641038275917910e-03, {3.646177809746111e-01, 2.068925896604807e-03}},
+       Descriptor{3, 9.053972465606226e-03, {2.214348854323312e-01, 7.456029460162668e-02}},
+       Descriptor{3, 1.463157551735100e-03, {1.424257573657563e-01, 5.007288257354491e-03}},
+       Descriptor{3, 8.051081382012054e-03, {3.540280097352752e-01, 4.088801119601688e-02}},
+       Descriptor{3, 4.227943749768248e-03, {1.492405208198407e-02, 2.418945789605796e-01}},
+       Descriptor{3, 1.663600681429694e-03, {9.776025800888155e-03, 6.008627532230670e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+  case 20: {
+    constexpr size_t nb_points = 79;
+    SmallArray<R2> point_list(nb_points);
+    SmallArray<double> weight_list(nb_points);
+
+    std::array descriptor_list =   //
+      {Descriptor{1, 1.391011070145312e-02, {}},
+       Descriptor{2, 1.408320130752025e-02, {2.545792676733391e-01}},
+       Descriptor{2, 7.988407910666199e-04, {1.097614102839776e-02}},
+       Descriptor{2, 7.830230776074533e-03, {1.093835967117146e-01}},
+       Descriptor{2, 9.173462974252915e-03, {1.862949977445409e-01}},
+       Descriptor{2, 9.452399933232448e-03, {4.455510569559248e-01}},
+       Descriptor{2, 2.161275410665577e-03, {3.731088059888470e-02}},
+       Descriptor{2, 1.378805062907046e-02, {3.934253478170999e-01}},
+       Descriptor{2, 7.101825303408441e-03, {4.762456115404990e-01}},
+       Descriptor{3, 2.202897418558497e-03, {1.591337076570672e-01, 7.570780504696529e-03}},
+       Descriptor{3, 5.986398578954690e-03, {1.985181322287882e-01, 4.656036490766432e-02}},
+       Descriptor{3, 1.129869602125866e-03, {4.854937607623754e-03, 6.409058560843406e-02}},
+       Descriptor{3, 8.667225567219333e-03, {3.331348173095875e-01, 5.498747914298681e-02}},
+       Descriptor{3, 4.145711527613858e-03, {3.836368477537459e-02, 9.995229628813866e-02}},
+       Descriptor{3, 7.722607822099230e-03, {2.156070573900944e-01, 1.062272047202700e-01}},
+       Descriptor{3, 3.695681500255298e-03, {9.831548292802561e-03, 4.200237588162241e-01}},
+       Descriptor{3, 1.169174573182774e-02, {1.398080719917999e-01, 3.178601238357720e-01}},
+       Descriptor{3, 3.578200238457685e-03, {2.805814114236652e-01, 1.073721285601109e-02}}};
+
+    fill_quadrature_points(descriptor_list, point_list, weight_list);
+
+    m_point_list  = point_list;
+    m_weight_list = weight_list;
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(TriangleGaussQuadrature::max_degree) + " on triangles");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/analysis/TriangleGaussQuadrature.hpp b/src/analysis/TriangleGaussQuadrature.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9e1edd3dedc01392f5cfae1ed8c0ab277b3ab2ba
--- /dev/null
+++ b/src/analysis/TriangleGaussQuadrature.hpp
@@ -0,0 +1,37 @@
+#ifndef TRIANGLE_GAUSS_QUADRATURE_HPP
+#define TRIANGLE_GAUSS_QUADRATURE_HPP
+
+#include <analysis/QuadratureFormula.hpp>
+
+/**
+ * Defines Gauss quadrature on the P1 reference triangle
+ *
+ * \note formulae are provided by
+ *
+ * ‘On the identification of symmetric quadrature rules for finite
+ * element methods‘ by F.D. Witherden & P.E. Vincent (2015).
+ */
+class TriangleGaussQuadrature final : public QuadratureFormula<2>
+{
+ public:
+  constexpr static size_t max_degree = 20;
+
+ private:
+  void _buildPointAndWeightLists(const size_t degree);
+
+ public:
+  TriangleGaussQuadrature(TriangleGaussQuadrature&&)      = default;
+  TriangleGaussQuadrature(const TriangleGaussQuadrature&) = default;
+
+ private:
+  friend class QuadratureManager;
+  explicit TriangleGaussQuadrature(const size_t degree) : QuadratureFormula<2>(QuadratureType::Gauss)
+  {
+    this->_buildPointAndWeightLists(degree);
+  }
+
+  TriangleGaussQuadrature()  = delete;
+  ~TriangleGaussQuadrature() = default;
+};
+
+#endif   // TRIANGLE_GAUSS_QUADRATURE_HPP
diff --git a/src/geometry/CubeTransformation.hpp b/src/geometry/CubeTransformation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..48582eef10148522adb2e4cb60f5c6b0f29df9dc
--- /dev/null
+++ b/src/geometry/CubeTransformation.hpp
@@ -0,0 +1,78 @@
+#ifndef CUBE_TRANSFORMATION_HPP
+#define CUBE_TRANSFORMATION_HPP
+
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
+
+#include <array>
+
+class CubeTransformation
+{
+ public:
+  constexpr static size_t Dimension      = 3;
+  constexpr static size_t NumberOfPoints = 8;
+
+ private:
+  TinyMatrix<Dimension, NumberOfPoints - 1> m_coefficients;
+  TinyVector<Dimension> m_shift;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<Dimension>& x) const
+  {
+    const TinyVector<NumberOfPoints - 1> X{x[0], x[1], x[2], x[0] * x[1], x[1] * x[2], x[0] * x[2], x[0] * x[1] * x[2]};
+    return m_coefficients * X + m_shift;
+  }
+
+  PUGS_INLINE double
+  jacobianDeterminant(const TinyVector<Dimension>& X) const
+  {
+    static_assert(Dimension == 3, "invalid dimension");
+    const auto& T   = m_coefficients;
+    const double& x = X[0];
+    const double& y = X[1];
+    const double& z = X[2];
+
+    const TinyMatrix<Dimension, Dimension> J{T(0, 0) + T(0, 3) * y + T(0, 5) * z + T(0, 6) * y * z,
+                                             T(0, 1) + T(0, 3) * x + T(0, 4) * z + T(0, 6) * x * z,
+                                             T(0, 2) + T(0, 4) * y + T(0, 5) * x + T(0, 6) * x * y,
+                                             //
+                                             T(1, 0) + T(1, 3) * y + T(1, 5) * z + T(1, 6) * y * z,
+                                             T(1, 1) + T(1, 3) * x + T(1, 4) * z + T(1, 6) * x * z,
+                                             T(1, 2) + T(1, 4) * y + T(1, 5) * x + T(1, 6) * x * y,
+                                             //
+                                             T(2, 0) + T(2, 3) * y + T(2, 5) * z + T(2, 6) * y * z,
+                                             T(2, 1) + T(2, 3) * x + T(2, 4) * z + T(2, 6) * x * z,
+                                             T(2, 2) + T(2, 4) * y + T(2, 5) * x + T(2, 6) * x * y};
+
+    return det(J);
+  }
+
+  PUGS_INLINE
+  CubeTransformation(const TinyVector<Dimension>& a,
+                     const TinyVector<Dimension>& b,
+                     const TinyVector<Dimension>& c,
+                     const TinyVector<Dimension>& d,
+                     const TinyVector<Dimension>& e,
+                     const TinyVector<Dimension>& f,
+                     const TinyVector<Dimension>& g,
+                     const TinyVector<Dimension>& h)
+  {
+    for (size_t i = 0; i < Dimension; ++i) {
+      m_coefficients(i, 0) = 0.125 * (-a[i] + b[i] + c[i] - d[i] - e[i] + f[i] + g[i] - h[i]);
+      m_coefficients(i, 1) = 0.125 * (-a[i] - b[i] + c[i] + d[i] - e[i] - f[i] + g[i] + h[i]);
+      m_coefficients(i, 2) = 0.125 * (-a[i] - b[i] - c[i] - d[i] + e[i] + f[i] + g[i] + h[i]);
+      m_coefficients(i, 3) = 0.125 * (+a[i] - b[i] + c[i] - d[i] + e[i] - f[i] + g[i] - h[i]);
+      m_coefficients(i, 4) = 0.125 * (+a[i] + b[i] - c[i] - d[i] - e[i] - f[i] + g[i] + h[i]);
+      m_coefficients(i, 5) = 0.125 * (+a[i] - b[i] - c[i] + d[i] - e[i] + f[i] + g[i] - h[i]);
+      m_coefficients(i, 6) = 0.125 * (-a[i] + b[i] - c[i] + d[i] + e[i] - f[i] + g[i] - h[i]);
+
+      m_shift[i] = 0.125 * (a[i] + b[i] + c[i] + d[i] + e[i] + f[i] + g[i] + h[i]);
+    }
+  }
+
+  ~CubeTransformation() = default;
+};
+
+#endif   // CUBE_TRANSFORMATION_HPP
diff --git a/src/geometry/LineTransformation.hpp b/src/geometry/LineTransformation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ae0b00a8a1dce7e4b6b6bb13a737ee1114a202f1
--- /dev/null
+++ b/src/geometry/LineTransformation.hpp
@@ -0,0 +1,78 @@
+#ifndef LINE_TRANSFORMATION_HPP
+#define LINE_TRANSFORMATION_HPP
+
+#include <algebra/TinyVector.hpp>
+
+template <size_t GivenDimension>
+class LineTransformation;
+
+template <>
+class LineTransformation<1>
+{
+ public:
+  constexpr static size_t Dimension = 1;
+
+ private:
+  double m_jacobian;
+  TinyVector<Dimension> m_shift;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<1>& x) const
+  {
+    return m_jacobian * x + m_shift;
+  }
+
+  double
+  jacobianDeterminant() const
+  {
+    return m_jacobian;
+  }
+
+  PUGS_INLINE
+  LineTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b)
+  {
+    m_jacobian = 0.5 * (b[0] - a[0]);
+    m_shift    = 0.5 * (a + b);
+  }
+
+  ~LineTransformation() = default;
+};
+
+template <size_t GivenDimension>
+class LineTransformation
+{
+ public:
+  constexpr static size_t Dimension = GivenDimension;
+
+ private:
+  TinyVector<Dimension> m_velocity;
+  const double m_velocity_norm;
+  TinyVector<Dimension> m_shift;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<1>& x) const
+  {
+    return x[0] * m_velocity + m_shift;
+  }
+
+  double
+  velocityNorm() const
+  {
+    return m_velocity_norm;
+  }
+
+  PUGS_INLINE
+  LineTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b)
+    : m_velocity{0.5 * (b - a)}, m_velocity_norm{l2Norm(m_velocity)}, m_shift{0.5 * (a + b)}
+  {
+    static_assert(Dimension > 1);
+  }
+
+  ~LineTransformation() = default;
+};
+
+#endif   // LINE_TRANSFORMATION_HPP
diff --git a/src/geometry/PrismTransformation.hpp b/src/geometry/PrismTransformation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc83e1911e196e665a8d0954c7df3c81bb2abdf4
--- /dev/null
+++ b/src/geometry/PrismTransformation.hpp
@@ -0,0 +1,71 @@
+#ifndef PRISM_TRANSFORMATION_HPP
+#define PRISM_TRANSFORMATION_HPP
+
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
+
+class PrismTransformation
+{
+ private:
+  constexpr static size_t Dimension      = 3;
+  constexpr static size_t NumberOfPoints = 6;
+
+  TinyMatrix<Dimension, NumberOfPoints - 1> m_coefficients;
+  TinyVector<Dimension> m_shift;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<Dimension>& x) const
+  {
+    const TinyVector<NumberOfPoints - 1> X{x[0], x[1], x[2], x[0] * x[2], x[1] * x[2]};
+    return m_coefficients * X + m_shift;
+  }
+
+  double
+  jacobianDeterminant(const TinyVector<Dimension>& X) const
+  {
+    const auto& T   = m_coefficients;
+    const double& x = X[0];
+    const double& y = X[1];
+    const double& z = X[2];
+
+    const TinyMatrix<Dimension, Dimension> J{T(0, 0) + T(0, 3) * z,   //
+                                             T(0, 1) + T(0, 4) * z,   //
+                                             T(0, 2) + T(0, 3) * x + T(0, 4) * y,
+                                             //
+                                             T(1, 0) + T(1, 3) * z,   //
+                                             T(1, 1) + T(1, 4) * z,   //
+                                             T(1, 2) + T(1, 3) * x + T(1, 4) * y,
+                                             //
+                                             T(2, 0) + T(2, 3) * z,   //
+                                             T(2, 1) + T(2, 4) * z,   //
+                                             T(2, 2) + T(2, 3) * x + T(2, 4) * y};
+    return det(J);
+  }
+
+  PUGS_INLINE
+  PrismTransformation(const TinyVector<Dimension>& a,
+                      const TinyVector<Dimension>& b,
+                      const TinyVector<Dimension>& c,
+                      const TinyVector<Dimension>& d,
+                      const TinyVector<Dimension>& e,
+                      const TinyVector<Dimension>& f)
+  {
+    static_assert(Dimension == 3, "invalid dimension");
+
+    for (size_t i = 0; i < Dimension; ++i) {
+      m_coefficients(i, 0) = 0.5 * (b[i] - a[i] + e[i] - d[i]);
+      m_coefficients(i, 1) = 0.5 * (c[i] + f[i] - a[i] - d[i]);
+      m_coefficients(i, 2) = 0.5 * (d[i] - a[i]);
+      m_coefficients(i, 3) = 0.5 * (a[i] - b[i] + e[i] - d[i]);
+      m_coefficients(i, 4) = 0.5 * (f[i] - c[i] + a[i] - d[i]);
+    }
+
+    m_shift = 0.5 * (a + d);
+  }
+
+  ~PrismTransformation() = default;
+};
+
+#endif   // PRISM_TRANSFORMATION_HPP
diff --git a/src/geometry/PyramidTransformation.hpp b/src/geometry/PyramidTransformation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5b2a79a1b46a95b18b773b2aacb6448081453441
--- /dev/null
+++ b/src/geometry/PyramidTransformation.hpp
@@ -0,0 +1,68 @@
+#ifndef PYRAMID_TRANSFORMATION_HPP
+#define PYRAMID_TRANSFORMATION_HPP
+
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
+
+class PyramidTransformation
+{
+ private:
+  constexpr static size_t Dimension      = 3;
+  constexpr static size_t NumberOfPoints = 5;
+
+  TinyMatrix<Dimension, NumberOfPoints - 1> m_coefficients;
+  TinyVector<Dimension> m_shift;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<Dimension>& x) const
+  {
+    const TinyVector<NumberOfPoints - 1> X{x[0], x[1], x[2], x[0] * x[1]};
+    return m_coefficients * X + m_shift;
+  }
+
+  double
+  jacobianDeterminant(const TinyVector<Dimension>& X) const
+  {
+    const auto& T   = m_coefficients;
+    const double& x = X[0];
+    const double& y = X[1];
+
+    const TinyMatrix<Dimension, Dimension> J{T(0, 0) + T(0, 3) * y,   //
+                                             T(0, 1) + T(0, 3) * x,   //
+                                             T(0, 2),
+                                             //
+                                             T(1, 0) + T(1, 3) * y,   //
+                                             T(1, 1) + T(1, 3) * x,   //
+                                             T(1, 2),
+                                             //
+                                             T(2, 0) + T(2, 3) * y,   //
+                                             T(2, 1) + T(2, 3) * x,   //
+                                             T(2, 2)};
+    return det(J);
+  }
+
+  PUGS_INLINE
+  PyramidTransformation(const TinyVector<Dimension>& a,
+                        const TinyVector<Dimension>& b,
+                        const TinyVector<Dimension>& c,
+                        const TinyVector<Dimension>& d,
+                        const TinyVector<Dimension>& e)
+  {
+    static_assert(Dimension == 3, "invalid dimension");
+
+    m_shift = 0.25 * (a + b + c + d);
+
+    for (size_t i = 0; i < Dimension; ++i) {
+      m_coefficients(i, 0) = 0.5 * (b[i] + c[i]) - m_shift[i];
+      m_coefficients(i, 1) = 0.5 * (c[i] + d[i]) - m_shift[i];
+      m_coefficients(i, 2) = e[i] - m_shift[i];
+      m_coefficients(i, 3) = 0.5 * (a[i] + c[i]) - m_shift[i];
+    }
+  }
+
+  ~PyramidTransformation() = default;
+};
+
+#endif   // PYRAMID_TRANSFORMATION_HPP
diff --git a/src/geometry/SquareTransformation.hpp b/src/geometry/SquareTransformation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d6474f33e4ecbaa4bcfba525dadc9fd38e95962d
--- /dev/null
+++ b/src/geometry/SquareTransformation.hpp
@@ -0,0 +1,123 @@
+#ifndef SQUARE_TRANSFORMATION_HPP
+#define SQUARE_TRANSFORMATION_HPP
+
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
+
+#include <array>
+
+template <size_t GivenDimension>
+class SquareTransformation;
+
+template <>
+class SquareTransformation<2>
+{
+ public:
+  constexpr static size_t Dimension      = 2;
+  constexpr static size_t NumberOfPoints = 4;
+
+ private:
+  TinyMatrix<Dimension, NumberOfPoints - 1> m_coefficients;
+  TinyVector<Dimension> m_shift;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<2>& x) const
+  {
+    const TinyVector<NumberOfPoints - 1> X{x[0], x[1], x[0] * x[1]};
+    return m_coefficients * X + m_shift;
+  }
+
+  PUGS_INLINE double
+  jacobianDeterminant(const TinyVector<Dimension>& X) const
+  {
+    const auto& T   = m_coefficients;
+    const double& x = X[0];
+    const double& y = X[1];
+
+    const TinyMatrix<Dimension, Dimension> J{T(0, 0) + T(0, 2) * y,   //
+                                             T(0, 1) + T(0, 2) * x,
+                                             //
+                                             T(1, 0) + T(1, 2) * y,   //
+                                             T(1, 1) + T(1, 2) * x};
+    return det(J);
+  }
+
+  PUGS_INLINE
+  SquareTransformation(const TinyVector<Dimension>& a,
+                       const TinyVector<Dimension>& b,
+                       const TinyVector<Dimension>& c,
+                       const TinyVector<Dimension>& d)
+  {
+    for (size_t i = 0; i < Dimension; ++i) {
+      m_coefficients(i, 0) = 0.25 * (-a[i] + b[i] + c[i] - d[i]);
+      m_coefficients(i, 1) = 0.25 * (-a[i] - b[i] + c[i] + d[i]);
+      m_coefficients(i, 2) = 0.25 * (+a[i] - b[i] + c[i] - d[i]);
+
+      m_shift[i] = 0.25 * (a[i] + b[i] + c[i] + d[i]);
+    }
+  }
+
+  ~SquareTransformation() = default;
+};
+
+template <size_t GivenDimension>
+class SquareTransformation
+{
+ public:
+  constexpr static size_t Dimension = GivenDimension;
+  static_assert(Dimension == 3, "Square transformation is defined in dimension 2 or 3");
+
+  constexpr static size_t NumberOfPoints = 4;
+
+ private:
+  TinyMatrix<Dimension, NumberOfPoints - 1> m_coefficients;
+  TinyVector<Dimension> m_shift;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<2>& x) const
+  {
+    const TinyVector<NumberOfPoints - 1> X{x[0], x[1], x[0] * x[1]};
+    return m_coefficients * X + m_shift;
+  }
+
+  PUGS_INLINE double
+  areaVariationNorm(const TinyVector<2>& X) const
+  {
+    const auto& T   = m_coefficients;
+    const double& x = X[0];
+    const double& y = X[1];
+
+    const TinyVector<Dimension> dxT{T(0, 0) + T(0, 2) * y,   //
+                                    T(1, 0) + T(1, 2) * y,   //
+                                    T(2, 0) + T(2, 2) * y};
+
+    const TinyVector<Dimension> dyT{T(0, 1) + T(0, 2) * x,   //
+                                    T(1, 1) + T(1, 2) * x,   //
+                                    T(2, 1) + T(2, 2) * x};
+
+    return l2Norm(crossProduct(dxT, dyT));
+  }
+
+  PUGS_INLINE
+  SquareTransformation(const TinyVector<Dimension>& a,
+                       const TinyVector<Dimension>& b,
+                       const TinyVector<Dimension>& c,
+                       const TinyVector<Dimension>& d)
+  {
+    for (size_t i = 0; i < Dimension; ++i) {
+      m_coefficients(i, 0) = 0.25 * (-a[i] + b[i] + c[i] - d[i]);
+      m_coefficients(i, 1) = 0.25 * (-a[i] - b[i] + c[i] + d[i]);
+      m_coefficients(i, 2) = 0.25 * (+a[i] - b[i] + c[i] - d[i]);
+
+      m_shift[i] = 0.25 * (a[i] + b[i] + c[i] + d[i]);
+    }
+  }
+
+  ~SquareTransformation() = default;
+};
+
+#endif   // SQUARE_TRANSFORMATION_HPP
diff --git a/src/geometry/TetrahedronTransformation.hpp b/src/geometry/TetrahedronTransformation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..64244266957a350e8be8f78c2eab3d5fead629e1
--- /dev/null
+++ b/src/geometry/TetrahedronTransformation.hpp
@@ -0,0 +1,53 @@
+#ifndef TETRAHEDRON_TRANSFORMATION_HPP
+#define TETRAHEDRON_TRANSFORMATION_HPP
+
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
+
+class TetrahedronTransformation
+{
+ public:
+  constexpr static size_t Dimension = 3;
+
+ private:
+  constexpr static size_t NumberOfPoints = Dimension + 1;
+
+  TinyMatrix<Dimension> m_jacobian;
+  TinyVector<Dimension> m_shift;
+  double m_jacobian_determinant;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<Dimension>& x) const
+  {
+    return m_jacobian * x + m_shift;
+  }
+
+  double
+  jacobianDeterminant() const
+  {
+    return m_jacobian_determinant;
+  }
+
+  PUGS_INLINE
+  TetrahedronTransformation(const TinyVector<Dimension>& a,
+                            const TinyVector<Dimension>& b,
+                            const TinyVector<Dimension>& c,
+                            const TinyVector<Dimension>& d)
+  {
+    for (size_t i = 0; i < Dimension; ++i) {
+      m_jacobian(i, 0) = b[i] - a[i];
+      m_jacobian(i, 1) = c[i] - a[i];
+      m_jacobian(i, 2) = d[i] - a[i];
+    }
+
+    m_shift = a;
+
+    m_jacobian_determinant = det(m_jacobian);
+  }
+
+  ~TetrahedronTransformation() = default;
+};
+
+#endif   // TETRAHEDRON_TRANSFORMATION_HPP
diff --git a/src/geometry/TriangleTransformation.hpp b/src/geometry/TriangleTransformation.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9210e0bda55bb19dfdfd877e509801fa87f0a4f0
--- /dev/null
+++ b/src/geometry/TriangleTransformation.hpp
@@ -0,0 +1,93 @@
+#ifndef TRIANGLE_TRANSFORMATION_HPP
+#define TRIANGLE_TRANSFORMATION_HPP
+
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
+
+template <size_t GivenDimension>
+class TriangleTransformation;
+
+template <>
+class TriangleTransformation<2>
+{
+ public:
+  constexpr static size_t Dimension = 2;
+
+ private:
+  TinyMatrix<Dimension> m_jacobian;
+  TinyVector<Dimension> m_shift;
+  double m_jacobian_determinant;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<2>& x) const
+  {
+    return m_jacobian * x + m_shift;
+  }
+
+  double
+  jacobianDeterminant() const
+  {
+    return m_jacobian_determinant;
+  }
+
+  PUGS_INLINE
+  TriangleTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b, const TinyVector<Dimension>& c)
+  {
+    for (size_t i = 0; i < Dimension; ++i) {
+      m_jacobian(i, 0) = b[i] - a[i];
+      m_jacobian(i, 1) = c[i] - a[i];
+    }
+
+    m_shift = a;
+
+    m_jacobian_determinant = det(m_jacobian);
+  }
+
+  ~TriangleTransformation() = default;
+};
+
+template <size_t GivenDimension>
+class TriangleTransformation
+{
+ public:
+  constexpr static size_t Dimension = GivenDimension;
+  static_assert(Dimension == 3, "Triangle transformation is defined in dimension 2 or 3");
+
+ private:
+  TinyMatrix<Dimension, 2> m_jacobian;
+  TinyVector<Dimension> m_shift;
+  double m_area_variation_norm;
+
+ public:
+  PUGS_INLINE
+  TinyVector<Dimension>
+  operator()(const TinyVector<2>& x) const
+  {
+    return m_jacobian * x + m_shift;
+  }
+
+  double
+  areaVariationNorm() const
+  {
+    return m_area_variation_norm;
+  }
+
+  PUGS_INLINE
+  TriangleTransformation(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b, const TinyVector<Dimension>& c)
+  {
+    for (size_t i = 0; i < Dimension; ++i) {
+      m_jacobian(i, 0) = b[i] - a[i];
+      m_jacobian(i, 1) = c[i] - a[i];
+    }
+
+    m_shift = a;
+
+    m_area_variation_norm = l2Norm(crossProduct(b - a, c - a));
+  }
+
+  ~TriangleTransformation() = default;
+};
+
+#endif   // TRIANGLE_TRANSFORMATION_HPP
diff --git a/src/language/algorithms/ElasticityDiamondAlgorithm.cpp b/src/language/algorithms/ElasticityDiamondAlgorithm.cpp
index c97191c532630f360026c1a40779b94a89f5f590..b14557ebd7599b9375c12d8ebe6f03311a9342fd 100644
--- a/src/language/algorithms/ElasticityDiamondAlgorithm.cpp
+++ b/src/language/algorithms/ElasticityDiamondAlgorithm.cpp
@@ -9,13 +9,13 @@
 #include <algebra/Vector.hpp>
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/MeshFaceBoundary.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/NeumannBoundaryConditionDescriptor.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
@@ -352,13 +352,12 @@ ElasticityDiamondScheme<Dimension>::ElasticityDiamondScheme(
     }
 
     {
-      std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+      std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
 
       MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
 
       std::shared_ptr mapper =
-        DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
-          mesh->connectivity());
+        DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity());
 
       CellValue<double> dual_muj =
         InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(mu_id,
diff --git a/src/language/algorithms/Heat5PointsAlgorithm.cpp b/src/language/algorithms/Heat5PointsAlgorithm.cpp
index 7583aa1d8d7df39862bcdbf76b4592a23fc25032..ffd1e810277ca05104cc00dd183c283dcf81f9a0 100644
--- a/src/language/algorithms/Heat5PointsAlgorithm.cpp
+++ b/src/language/algorithms/Heat5PointsAlgorithm.cpp
@@ -10,12 +10,12 @@
 #include <algebra/Vector.hpp>
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 
 template <size_t Dimension>
 Heat5PointsAlgorithm<Dimension>::Heat5PointsAlgorithm(
@@ -77,13 +77,12 @@ Heat5PointsAlgorithm<Dimension>::Heat5PointsAlgorithm(
     }
 
     {
-      std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+      std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
 
       MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
 
       std::shared_ptr mapper =
-        DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
-          mesh->connectivity());
+        DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity());
 
       NodeValue<double> Trd{diamond_mesh->connectivity()};
 
diff --git a/src/language/algorithms/HeatDiamondAlgorithm.cpp b/src/language/algorithms/HeatDiamondAlgorithm.cpp
index b5d5f22d6041b742da239ce4d011849643291356..d21c25062dd24234a2e3d619ce1847a9bb1b310b 100644
--- a/src/language/algorithms/HeatDiamondAlgorithm.cpp
+++ b/src/language/algorithms/HeatDiamondAlgorithm.cpp
@@ -9,14 +9,14 @@
 #include <algebra/Vector.hpp>
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/MeshFaceBoundary.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <mesh/SubItemValuePerItem.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/FourierBoundaryConditionDescriptor.hpp>
@@ -317,13 +317,12 @@ HeatDiamondScheme<Dimension>::HeatDiamondScheme(
     }
 
     {
-      std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+      std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
 
       MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
 
       std::shared_ptr mapper =
-        DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
-          mesh->connectivity());
+        DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity());
 
       NodeValue<double> Trd{diamond_mesh->connectivity()};
 
diff --git a/src/language/algorithms/HeatDiamondAlgorithm2.cpp b/src/language/algorithms/HeatDiamondAlgorithm2.cpp
index cad78599d7036d152480590f097c27890dfa57b4..c36e796780f77ccd58c2fa7559a8c5cab9478d87 100644
--- a/src/language/algorithms/HeatDiamondAlgorithm2.cpp
+++ b/src/language/algorithms/HeatDiamondAlgorithm2.cpp
@@ -9,14 +9,14 @@
 #include <algebra/Vector.hpp>
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/MeshFaceBoundary.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <mesh/SubItemValuePerItem.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/FourierBoundaryConditionDescriptor.hpp>
@@ -344,13 +344,12 @@ HeatDiamondScheme2<Dimension>::HeatDiamondScheme2(
     }
 
     {
-      std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+      std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
 
       MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
 
       std::shared_ptr mapper =
-        DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
-          mesh->connectivity());
+        DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity());
 
       NodeValue<double> Trd{diamond_mesh->connectivity()};
 
diff --git a/src/language/algorithms/ParabolicHeat.cpp b/src/language/algorithms/ParabolicHeat.cpp
index 0b7595524bb9a72dff4e71335a1928b7071ab578..d535751ae11c75f2cf08e20024435d82738010d2 100644
--- a/src/language/algorithms/ParabolicHeat.cpp
+++ b/src/language/algorithms/ParabolicHeat.cpp
@@ -6,13 +6,13 @@
 #include <language/algorithms/ParabolicHeat.hpp>
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <mesh/SubItemValuePerItem.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/FourierBoundaryConditionDescriptor.hpp>
diff --git a/src/language/algorithms/UnsteadyElasticity.cpp b/src/language/algorithms/UnsteadyElasticity.cpp
index a58f50a99fd5ddf3c4af2bd29db3cc0a83617de2..6eb2017c1286f73356ae126f8e19998ae8015de8 100644
--- a/src/language/algorithms/UnsteadyElasticity.cpp
+++ b/src/language/algorithms/UnsteadyElasticity.cpp
@@ -9,13 +9,13 @@
 #include <algebra/Vector.hpp>
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/MeshFaceBoundary.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/NeumannBoundaryConditionDescriptor.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
diff --git a/src/language/ast/ASTBuilder.cpp b/src/language/ast/ASTBuilder.cpp
index a4dc8c822bab53a67be975b6444eb9dffa9bb6be..109ce527cffee1cc4aaa4221079894f8f0357338 100644
--- a/src/language/ast/ASTBuilder.cpp
+++ b/src/language/ast/ASTBuilder.cpp
@@ -74,7 +74,7 @@ struct ASTBuilder::simplify_unary : TAO_PEGTL_NAMESPACE::parse_tree::apply<ASTBu
           transform(n, st...);
         }
       }
-    } else if (n->children.size() == 2) {
+    } else if ((n->children.size() == 2) and (n->is_type<language::unary_expression>())) {
       if (n->children[0]->is_type<language::unary_plus>()) {
         n = std::move(n->children[1]);
         transform(n, st...);
diff --git a/src/language/modules/CMakeLists.txt b/src/language/modules/CMakeLists.txt
index f7f1baeafe34261a38033dd3d2db828df334ce25..c4edd2505b39320d39b25b7653667f7992567ccc 100644
--- a/src/language/modules/CMakeLists.txt
+++ b/src/language/modules/CMakeLists.txt
@@ -10,6 +10,7 @@ add_library(PugsLanguageModules
   MeshModule.cpp
   ModuleRepository.cpp
   SchemeModule.cpp
+  SocketModule.cpp
   UnaryOperatorRegisterForVh.cpp
   UtilsModule.cpp
   WriterModule.cpp
diff --git a/src/language/modules/MathModule.cpp b/src/language/modules/MathModule.cpp
index 69f6890fbc5e8c3e3515901acd5b5102100e8c35..2217424e43bb9b276cf2260ce88c547252f88982 100644
--- a/src/language/modules/MathModule.cpp
+++ b/src/language/modules/MathModule.cpp
@@ -70,6 +70,18 @@ MathModule::MathModule()
   this->_addBuiltinFunction("round", std::make_shared<BuiltinFunctionEmbedder<int64_t(double)>>(
                                        [](double x) -> int64_t { return std::lround(x); }));
 
+  this->_addBuiltinFunction("min", std::make_shared<BuiltinFunctionEmbedder<double(double, double)>>(
+                                     [](double x, double y) -> double { return std::min(x, y); }));
+
+  this->_addBuiltinFunction("min", std::make_shared<BuiltinFunctionEmbedder<int64_t(int64_t, int64_t)>>(
+                                     [](int64_t x, int64_t y) -> int64_t { return std::min(x, y); }));
+
+  this->_addBuiltinFunction("max", std::make_shared<BuiltinFunctionEmbedder<double(double, double)>>(
+                                     [](double x, double y) -> double { return std::max(x, y); }));
+
+  this->_addBuiltinFunction("max", std::make_shared<BuiltinFunctionEmbedder<int64_t(int64_t, int64_t)>>(
+                                     [](int64_t x, int64_t y) -> int64_t { return std::max(x, y); }));
+
   this->_addBuiltinFunction("dot",
                             std::make_shared<BuiltinFunctionEmbedder<double(const TinyVector<1>, const TinyVector<1>)>>(
                               [](const TinyVector<1> x, const TinyVector<1> y) -> double { return dot(x, y); }));
diff --git a/src/language/modules/MeshModule.cpp b/src/language/modules/MeshModule.cpp
index 86c9a0c106e9fe6eece190c93365fc42a47bf864..153c1be1154ac1a5432ae00ba66b56e7b44937c2 100644
--- a/src/language/modules/MeshModule.cpp
+++ b/src/language/modules/MeshModule.cpp
@@ -4,60 +4,19 @@
 #include <language/node_processor/ExecutionPolicy.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
 #include <language/utils/FunctionTable.hpp>
-#include <language/utils/PugsFunctionAdapter.hpp>
 #include <language/utils/SymbolTable.hpp>
 #include <language/utils/TypeDescriptor.hpp>
 #include <mesh/CartesianMeshBuilder.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/GmshReader.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/MeshRelaxer.hpp>
+#include <mesh/MeshTransformer.hpp>
 #include <utils/Exceptions.hpp>
 
 #include <Kokkos_Core.hpp>
 
-template <typename T>
-class MeshTransformation;
-template <typename OutputType, typename InputType>
-class MeshTransformation<OutputType(InputType)> : public PugsFunctionAdapter<OutputType(InputType)>
-{
-  static constexpr size_t Dimension = OutputType::Dimension;
-  using Adapter                     = PugsFunctionAdapter<OutputType(InputType)>;
-
- public:
-  static inline std::shared_ptr<Mesh<Connectivity<Dimension>>>
-  transform(const FunctionSymbolId& function_symbol_id, std::shared_ptr<const IMesh> p_mesh)
-  {
-    using MeshType             = Mesh<Connectivity<Dimension>>;
-    const MeshType& given_mesh = dynamic_cast<const MeshType&>(*p_mesh);
-
-    auto& expression    = Adapter::getFunctionExpression(function_symbol_id);
-    auto convert_result = Adapter::getResultConverter(expression.m_data_type);
-
-    Array<ExecutionPolicy> context_list = Adapter::getContextList(expression);
-
-    NodeValue<const InputType> given_xr = given_mesh.xr();
-    NodeValue<OutputType> xr(given_mesh.connectivity());
-
-    using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space;
-    Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens;
-
-    parallel_for(given_mesh.numberOfNodes(), [=, &expression, &tokens](NodeId r) {
-      const int32_t t = tokens.acquire();
-
-      auto& execution_policy = context_list[t];
-
-      Adapter::convertArgs(execution_policy.currentContext(), given_xr[r]);
-      auto result = expression.execute(execution_policy);
-      xr[r]       = convert_result(std::move(result));
-
-      tokens.release(t);
-    });
-
-    return std::make_shared<MeshType>(given_mesh.shared_connectivity(), xr);
-  }
-};
-
 MeshModule::MeshModule()
 {
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IMesh>>);
@@ -78,23 +37,20 @@ MeshModule::MeshModule()
 
                               [](std::shared_ptr<const IMesh> p_mesh,
                                  const FunctionSymbolId& function_id) -> std::shared_ptr<const IMesh> {
-                                switch (p_mesh->dimension()) {
-                                case 1: {
-                                  using TransformT = TinyVector<1>(TinyVector<1>);
-                                  return MeshTransformation<TransformT>::transform(function_id, p_mesh);
-                                }
-                                case 2: {
-                                  using TransformT = TinyVector<2>(TinyVector<2>);
-                                  return MeshTransformation<TransformT>::transform(function_id, p_mesh);
-                                }
-                                case 3: {
-                                  using TransformT = TinyVector<3>(TinyVector<3>);
-                                  return MeshTransformation<TransformT>::transform(function_id, p_mesh);
-                                }
-                                default: {
-                                  throw UnexpectedError("invalid mesh dimension");
-                                }
-                                }
+                                return MeshTransformer{}.transform(function_id, p_mesh);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("relax",
+                            std::make_shared<BuiltinFunctionEmbedder<
+                              std::shared_ptr<const IMesh>(const std::shared_ptr<const IMesh>&,
+                                                           const std::shared_ptr<const IMesh>&, const double&)>>(
+
+                              [](const std::shared_ptr<const IMesh>& source_mesh,
+                                 const std::shared_ptr<const IMesh>& destination_mesh,
+                                 const double& theta) -> std::shared_ptr<const IMesh> {
+                                return MeshRelaxer{}.relax(source_mesh, destination_mesh, theta);
                               }
 
                               ));
@@ -190,19 +146,51 @@ MeshModule::MeshModule()
                                   using MeshType = Mesh<Connectivity<1>>;
 
                                   std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
-                                  return DiamondDualMeshManager::instance().getDiamondDualMesh(p_mesh);
+                                  return DualMeshManager::instance().getDiamondDualMesh(*p_mesh);
+                                }
+                                case 2: {
+                                  using MeshType = Mesh<Connectivity<2>>;
+
+                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+                                  return DualMeshManager::instance().getDiamondDualMesh(*p_mesh);
+                                }
+                                case 3: {
+                                  using MeshType = Mesh<Connectivity<3>>;
+
+                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+                                  return DualMeshManager::instance().getDiamondDualMesh(*p_mesh);
+                                }
+                                default: {
+                                  throw UnexpectedError("invalid dimension");
+                                }
+                                }
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("medianDual",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const IMesh>(
+                              const std::shared_ptr<const IMesh>&)>>(
+
+                              [](const std::shared_ptr<const IMesh>& i_mesh) -> std::shared_ptr<const IMesh> {
+                                switch (i_mesh->dimension()) {
+                                case 1: {
+                                  using MeshType = Mesh<Connectivity<1>>;
+
+                                  std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
+                                  return DualMeshManager::instance().getMedianDualMesh(*p_mesh);
                                 }
                                 case 2: {
                                   using MeshType = Mesh<Connectivity<2>>;
 
                                   std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
-                                  return DiamondDualMeshManager::instance().getDiamondDualMesh(p_mesh);
+                                  return DualMeshManager::instance().getMedianDualMesh(*p_mesh);
                                 }
                                 case 3: {
                                   using MeshType = Mesh<Connectivity<3>>;
 
                                   std::shared_ptr p_mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
-                                  return DiamondDualMeshManager::instance().getDiamondDualMesh(p_mesh);
+                                  return DualMeshManager::instance().getMedianDualMesh(*p_mesh);
                                 }
                                 default: {
                                   throw UnexpectedError("invalid dimension");
diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp
index 71a1368b9dc374b19562bae27cc14a5d2b54c233..85810cbfc3f12bba920ae2ee99002be73f0db39d 100644
--- a/src/language/modules/ModuleRepository.cpp
+++ b/src/language/modules/ModuleRepository.cpp
@@ -6,6 +6,7 @@
 #include <language/modules/MathModule.hpp>
 #include <language/modules/MeshModule.hpp>
 #include <language/modules/SchemeModule.hpp>
+#include <language/modules/SocketModule.hpp>
 #include <language/modules/UtilsModule.hpp>
 #include <language/modules/WriterModule.hpp>
 #include <language/utils/BasicAffectationRegistrerFor.hpp>
@@ -56,6 +57,7 @@ ModuleRepository::ModuleRepository()
   this->_subscribe(std::make_unique<MathModule>());
   this->_subscribe(std::make_unique<MeshModule>());
   this->_subscribe(std::make_unique<SchemeModule>());
+  this->_subscribe(std::make_unique<SocketModule>());
   this->_subscribe(std::make_unique<UtilsModule>());
   this->_subscribe(std::make_unique<WriterModule>());
 }
diff --git a/src/language/modules/SchemeModule.cpp b/src/language/modules/SchemeModule.cpp
index 98013190e42d1759aab0566462fe670dba2562d3..7a86aafc149391454290b552e3543ea0cf0674c9 100644
--- a/src/language/modules/SchemeModule.cpp
+++ b/src/language/modules/SchemeModule.cpp
@@ -1,5 +1,8 @@
 #include <language/modules/SchemeModule.hpp>
 
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
 #include <language/algorithms/ElasticityDiamondAlgorithm.hpp>
 #include <language/algorithms/Heat5PointsAlgorithm.hpp>
 #include <language/algorithms/HeatDiamondAlgorithm.hpp>
@@ -23,10 +26,13 @@
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/DiscreteFunctionDescriptorP0.hpp>
 #include <scheme/DiscreteFunctionDescriptorP0Vector.hpp>
+#include <scheme/DiscreteFunctionIntegrator.hpp>
 #include <scheme/DiscreteFunctionInterpoler.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <scheme/DiscreteFunctionUtils.hpp>
+#include <scheme/DiscreteFunctionVectorIntegrator.hpp>
 #include <scheme/DiscreteFunctionVectorInterpoler.hpp>
+#include <scheme/ExternalBoundaryConditionDescriptor.hpp>
 #include <scheme/FixedBoundaryConditionDescriptor.hpp>
 #include <scheme/FourierBoundaryConditionDescriptor.hpp>
 #include <scheme/FreeBoundaryConditionDescriptor.hpp>
@@ -39,6 +45,7 @@
 #include <scheme/ScalarDiamondScheme.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
 #include <scheme/VectorDiamondScheme.hpp>
+#include <utils/Socket.hpp>
 
 #include <memory>
 
@@ -46,6 +53,7 @@ SchemeModule::SchemeModule()
 {
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IDiscreteFunction>>);
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IDiscreteFunctionDescriptor>>);
+  this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IQuadratureDescriptor>>);
 
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IBoundaryDescriptor>>);
   this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const IBoundaryConditionDescriptor>>);
@@ -67,6 +75,64 @@ SchemeModule::SchemeModule()
 
                               ));
 
+  this->_addBuiltinFunction("Gauss", std::make_shared<
+                                       BuiltinFunctionEmbedder<std::shared_ptr<const IQuadratureDescriptor>(uint64_t)>>(
+                                       [](uint64_t degree) -> std::shared_ptr<const IQuadratureDescriptor> {
+                                         return std::make_shared<GaussQuadratureDescriptor>(degree);
+                                       }
+
+                                       ));
+
+  this->_addBuiltinFunction("GaussLobatto",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<std::shared_ptr<const IQuadratureDescriptor>(uint64_t)>>(
+                              [](uint64_t degree) -> std::shared_ptr<const IQuadratureDescriptor> {
+                                return std::make_shared<GaussLobattoQuadratureDescriptor>(degree);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("GaussLegendre",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<std::shared_ptr<const IQuadratureDescriptor>(uint64_t)>>(
+                              [](uint64_t degree) -> std::shared_ptr<const IQuadratureDescriptor> {
+                                return std::make_shared<GaussLegendreQuadratureDescriptor>(degree);
+                              }
+
+                              ));
+
+  this
+    ->_addBuiltinFunction("integrate",
+                          std::make_shared<BuiltinFunctionEmbedder<
+                            std::shared_ptr<const IDiscreteFunction>(std::shared_ptr<const IMesh>,
+                                                                     std::shared_ptr<const IQuadratureDescriptor>,
+                                                                     std::shared_ptr<const IDiscreteFunctionDescriptor>,
+                                                                     const std::vector<FunctionSymbolId>&)>>(
+                            [](std::shared_ptr<const IMesh> mesh,
+                               std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor,
+                               std::shared_ptr<const IDiscreteFunctionDescriptor> discrete_function_descriptor,
+                               const std::vector<FunctionSymbolId>& function_id_list)
+                              -> std::shared_ptr<const IDiscreteFunction> {
+                              return DiscreteFunctionVectorIntegrator{mesh, quadrature_descriptor,
+                                                                      discrete_function_descriptor, function_id_list}
+                                .integrate();
+                            }
+
+                            ));
+
+  this->_addBuiltinFunction("integrate",
+                            std::make_shared<BuiltinFunctionEmbedder<
+                              std::shared_ptr<const IDiscreteFunction>(std::shared_ptr<const IMesh>,
+                                                                       std::shared_ptr<const IQuadratureDescriptor>,
+                                                                       const FunctionSymbolId&)>>(
+                              [](std::shared_ptr<const IMesh> mesh,
+                                 std::shared_ptr<const IQuadratureDescriptor> quadrature_descriptor,
+                                 const FunctionSymbolId& function_id) -> std::shared_ptr<const IDiscreteFunction> {
+                                return DiscreteFunctionIntegrator{mesh, quadrature_descriptor, function_id}.integrate();
+                              }
+
+                              ));
+
   this->_addBuiltinFunction(
     "interpolate",
     std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<
@@ -268,6 +334,20 @@ SchemeModule::SchemeModule()
 
                               ));
 
+  this->_addBuiltinFunction("external_fsi_velocity",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<
+                              const IBoundaryConditionDescriptor>(std::shared_ptr<const IBoundaryDescriptor>,
+                                                                  const std::shared_ptr<const Socket>&)>>(
+
+                              [](std::shared_ptr<const IBoundaryDescriptor> boundary,
+                                 const std::shared_ptr<const Socket>& socket)
+                                -> std::shared_ptr<const IBoundaryConditionDescriptor> {
+                                return std::make_shared<ExternalBoundaryConditionDescriptor>("external_fsi_velocity",
+                                                                                             boundary, socket);
+                              }
+
+                              ));
+
   this->_addBuiltinFunction("glace_solver",
                             std::make_shared<BuiltinFunctionEmbedder<std::tuple<
                               std::shared_ptr<const IMesh>, std::shared_ptr<const IDiscreteFunction>,
diff --git a/src/language/modules/SchemeModule.hpp b/src/language/modules/SchemeModule.hpp
index 758b5f9c56fcdc928adfc4678a0a02a8dcfa5bd8..52fc163ee5b6bfcec2cbeceb6d618e9ce3d9c213 100644
--- a/src/language/modules/SchemeModule.hpp
+++ b/src/language/modules/SchemeModule.hpp
@@ -25,6 +25,11 @@ template <>
 inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IDiscreteFunctionDescriptor>> =
   ASTNodeDataType::build<ASTNodeDataType::type_id_t>("Vh_type");
 
+class IQuadratureDescriptor;
+template <>
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const IQuadratureDescriptor>> =
+  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("quadrature");
+
 class SchemeModule : public BuiltinModule
 {
   friend class MathFunctionRegisterForVh;
diff --git a/src/language/modules/SocketModule.cpp b/src/language/modules/SocketModule.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0540af2b60553c33f61ccaeeed049d9696f6ba5e
--- /dev/null
+++ b/src/language/modules/SocketModule.cpp
@@ -0,0 +1,291 @@
+#include <language/modules/SocketModule.hpp>
+
+#include <language/utils/BinaryOperatorProcessorBuilder.hpp>
+#include <language/utils/BuiltinFunctionEmbedder.hpp>
+#include <language/utils/OStream.hpp>
+#include <language/utils/OperatorRepository.hpp>
+#include <utils/Socket.hpp>
+
+SocketModule::SocketModule()
+{
+  this->_addTypeDescriptor(ast_node_data_type_from<std::shared_ptr<const Socket>>);
+
+  this->_addBuiltinFunction("createSocketServer",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(const uint64_t&)>>(
+
+                              [](const uint64_t& port_number) -> std::shared_ptr<const Socket> {
+                                return std::make_shared<const Socket>(createServerSocket(port_number));
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("acceptSocketClient",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(std::shared_ptr<const Socket>)>>(
+
+                              [](std::shared_ptr<const Socket> server_socket) -> std::shared_ptr<const Socket> {
+                                return std::make_shared<const Socket>(acceptClientSocket(*server_socket));
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("connectSocketServer",
+                            std::make_shared<BuiltinFunctionEmbedder<std::shared_ptr<const Socket>(const std::string&,
+                                                                                                   const uint64_t&)>>(
+
+                              [](const std::string& hostname,
+                                 const uint64_t& port_number) -> std::shared_ptr<const Socket> {
+                                return std::make_shared<const Socket>(connectServerSocket(hostname, port_number));
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const bool&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const bool& value) -> void {
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const uint64_t&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const uint64_t& value) -> void {
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const int64_t&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const int64_t& value) -> void {
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const double&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const double& value) -> void {
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyVector<1>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyVector<1>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyVector<2>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyVector<2>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyVector<3>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyVector<3>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyMatrix<1>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyMatrix<1>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyMatrix<2>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyMatrix<2>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write", std::make_shared<BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&,
+                                                                                   const TinyMatrix<3>&)>>(
+
+                                       [](const std::shared_ptr<const Socket>& socket,
+                                          const TinyMatrix<3>& value) -> void { write(*socket, value); }
+
+                                       ));
+
+  this->_addBuiltinFunction("write",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<void(const std::shared_ptr<const Socket>&, const std::string&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket, const std::string& value) -> void {
+                                write(*socket, value.size());
+                                write(*socket, value);
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_B",
+                            std::make_shared<BuiltinFunctionEmbedder<bool(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> bool {
+                                bool value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_N",
+                            std::make_shared<BuiltinFunctionEmbedder<uint64_t(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> uint64_t {
+                                uint64_t value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_Z",
+                            std::make_shared<BuiltinFunctionEmbedder<int64_t(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> int64_t {
+                                int64_t value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_R",
+                            std::make_shared<BuiltinFunctionEmbedder<double(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> double {
+                                double value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_R1", std::make_shared<
+                                         BuiltinFunctionEmbedder<TinyVector<1>(const std::shared_ptr<const Socket>&)>>(
+
+                                         [](const std::shared_ptr<const Socket>& socket) -> TinyVector<1> {
+                                           TinyVector<1> value;
+                                           read(*socket, value);
+
+                                           return value;
+                                         }
+
+                                         ));
+
+  this->_addBuiltinFunction("read_R2", std::make_shared<
+                                         BuiltinFunctionEmbedder<TinyVector<2>(const std::shared_ptr<const Socket>&)>>(
+
+                                         [](const std::shared_ptr<const Socket>& socket) -> TinyVector<2> {
+                                           TinyVector<2> value;
+                                           read(*socket, value);
+
+                                           return value;
+                                         }
+
+                                         ));
+
+  this->_addBuiltinFunction("read_R3", std::make_shared<
+                                         BuiltinFunctionEmbedder<TinyVector<3>(const std::shared_ptr<const Socket>&)>>(
+
+                                         [](const std::shared_ptr<const Socket>& socket) -> TinyVector<3> {
+                                           TinyVector<3> value;
+                                           read(*socket, value);
+
+                                           return value;
+                                         }
+
+                                         ));
+
+  this->_addBuiltinFunction("read_R1x1",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<TinyMatrix<1>(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> TinyMatrix<1> {
+                                TinyMatrix<1> value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_R2x2",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<TinyMatrix<2>(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> TinyMatrix<2> {
+                                TinyMatrix<2> value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this->_addBuiltinFunction("read_R3x3",
+                            std::make_shared<
+                              BuiltinFunctionEmbedder<TinyMatrix<3>(const std::shared_ptr<const Socket>&)>>(
+
+                              [](const std::shared_ptr<const Socket>& socket) -> TinyMatrix<3> {
+                                TinyMatrix<3> value;
+                                read(*socket, value);
+
+                                return value;
+                              }
+
+                              ));
+
+  this
+    ->_addBuiltinFunction("read_string",
+                          std::make_shared<BuiltinFunctionEmbedder<std::string(const std::shared_ptr<const Socket>&)>>(
+
+                            [](const std::shared_ptr<const Socket>& socket) -> std::string {
+                              size_t size;
+                              read(*socket, size);
+                              std::string value;
+                              if (size > 0) {
+                                value.resize(size);
+                                read(*socket, value);
+                              }
+                              return value;
+                            }
+
+                            ));
+}
+
+void
+SocketModule::registerOperators() const
+{
+  OperatorRepository& repository = OperatorRepository::instance();
+
+  repository.addBinaryOperator<language::shift_left_op>(
+    std::make_shared<BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const OStream>,
+                                                    std::shared_ptr<const OStream>, std::shared_ptr<const Socket>>>());
+}
diff --git a/src/language/modules/SocketModule.hpp b/src/language/modules/SocketModule.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b32b64fafc72829e6598cfa78a95ce14f61ed9b
--- /dev/null
+++ b/src/language/modules/SocketModule.hpp
@@ -0,0 +1,28 @@
+#ifndef SOCKET_MODULE_HPP
+#define SOCKET_MODULE_HPP
+
+#include <language/modules/BuiltinModule.hpp>
+#include <language/utils/ASTNodeDataTypeTraits.hpp>
+
+class Socket;
+
+template <>
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const Socket>> =
+  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("socket");
+
+class SocketModule : public BuiltinModule
+{
+ public:
+  std::string_view
+  name() const final
+  {
+    return "socket";
+  }
+
+  void registerOperators() const final;
+
+  SocketModule();
+  ~SocketModule() = default;
+};
+
+#endif   // SOCKET_MODULE_HPP
diff --git a/src/language/node_processor/AffectationProcessor.hpp b/src/language/node_processor/AffectationProcessor.hpp
index f24e8473be7717f4ddd2e8f20d3d2ab666f19001..002c0e4ed7aa427a20734689089de590cfd96bc0 100644
--- a/src/language/node_processor/AffectationProcessor.hpp
+++ b/src/language/node_processor/AffectationProcessor.hpp
@@ -165,7 +165,7 @@ class AffectationExecutor final : public IAffectationExecutor
                 [&](auto&& v) {
                   using Vi_T = std::decay_t<decltype(v)>;
                   if constexpr (std::is_convertible_v<Vi_T, double>) {
-                    m_lhs = v;
+                    m_lhs = TinyVector<1>(v);
                   } else {
                     // LCOV_EXCL_START
                     throw UnexpectedError("unexpected rhs type in affectation");
@@ -178,7 +178,7 @@ class AffectationExecutor final : public IAffectationExecutor
                 [&](auto&& v) {
                   using Vi_T = std::decay_t<decltype(v)>;
                   if constexpr (std::is_convertible_v<Vi_T, double>) {
-                    m_lhs = v;
+                    m_lhs = TinyMatrix<1>(v);
                   } else {
                     // LCOV_EXCL_START
                     throw UnexpectedError("unexpected rhs type in affectation");
diff --git a/src/language/node_processor/BinaryExpressionProcessor.hpp b/src/language/node_processor/BinaryExpressionProcessor.hpp
index 8cc1a430f96d2892110afaff0b1ac8bc6ccaf347..7f278d4c7d4a007ad5db4c9f5d444665ccca56d6 100644
--- a/src/language/node_processor/BinaryExpressionProcessor.hpp
+++ b/src/language/node_processor/BinaryExpressionProcessor.hpp
@@ -260,7 +260,7 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, std::shared
       return this->_eval(m_node.children[0]->execute(exec_policy), m_node.children[1]->execute(exec_policy));
     }
     catch (const NormalError& error) {
-      throw ParseError(error.what(), m_node.begin());
+      throw ParseError(error.what(), m_node.begin());   // LCOV_EXCL_LINE
     }
   }
 
@@ -297,7 +297,7 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, A_DataT, st
       return this->_eval(m_node.children[0]->execute(exec_policy), m_node.children[1]->execute(exec_policy));
     }
     catch (const NormalError& error) {
-      throw ParseError(error.what(), m_node.begin());
+      throw ParseError(error.what(), m_node.begin());   // LCOV_EXCL_LINE
     }
   }
 
@@ -335,7 +335,7 @@ struct BinaryExpressionProcessor<BinaryOpT, std::shared_ptr<ValueT>, std::shared
       return this->_eval(m_node.children[0]->execute(exec_policy), m_node.children[1]->execute(exec_policy));
     }
     catch (const NormalError& error) {
-      throw ParseError(error.what(), m_node.begin());
+      throw ParseError(error.what(), m_node.begin());   // LCOV_EXCL_LINE
     }
   }
 
diff --git a/src/language/node_processor/FunctionProcessor.hpp b/src/language/node_processor/FunctionProcessor.hpp
index cead55c20fd2e6b34edb4df510840d683fc8b5b8..c6c81d2938e9f774f0315f6a9af5c2e03c6a19e5 100644
--- a/src/language/node_processor/FunctionProcessor.hpp
+++ b/src/language/node_processor/FunctionProcessor.hpp
@@ -58,6 +58,10 @@ class FunctionExpressionProcessor final : public INodeProcessor
       return ReturnType{ZeroType::zero};
     } else if constexpr (std::is_convertible_v<ExpressionValueType, ReturnType>) {
       return static_cast<ReturnType>(std::get<ExpressionValueType>(m_function_expression.execute(exec_policy)));
+    } else if constexpr (std::is_arithmetic_v<ExpressionValueType> and
+                         (is_tiny_vector_v<ReturnType> or is_tiny_matrix_v<ReturnType>)) {
+      static_assert(ReturnType::Dimension == 1, "invalid conversion");
+      return ReturnType(std::get<ExpressionValueType>(m_function_expression.execute(exec_policy)));
     } else {
       throw UnexpectedError("invalid conversion");
     }
diff --git a/src/language/utils/CMakeLists.txt b/src/language/utils/CMakeLists.txt
index c8928003d045b7216b8edd8ea13b989d697d3268..cd3f8af2314e96e87bacec25ac1dfc08d015192b 100644
--- a/src/language/utils/CMakeLists.txt
+++ b/src/language/utils/CMakeLists.txt
@@ -23,8 +23,9 @@ add_library(PugsLanguageUtils
   BuiltinFunctionEmbedderUtils.cpp
   DataVariant.cpp
   EmbeddedData.cpp
-  EmbeddedIDiscreteFunctionOperators.cpp
   EmbeddedIDiscreteFunctionMathFunctions.cpp
+  EmbeddedIDiscreteFunctionOperators.cpp
+  EmbeddedIDiscreteFunctionUtils.cpp
   FunctionSymbolId.cpp
   IncDecOperatorRegisterForN.cpp
   IncDecOperatorRegisterForR.cpp
diff --git a/src/language/utils/EmbeddedIDiscreteFunctionMathFunctions.cpp b/src/language/utils/EmbeddedIDiscreteFunctionMathFunctions.cpp
index d4d10c6c7c531add6cd9f32c2ad376fcd97100a6..f49c4c033251bf46d7cd91e7c524af4d146db9d3 100644
--- a/src/language/utils/EmbeddedIDiscreteFunctionMathFunctions.cpp
+++ b/src/language/utils/EmbeddedIDiscreteFunctionMathFunctions.cpp
@@ -8,7 +8,7 @@
 #include <scheme/IDiscreteFunction.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
 
-#define DISCRETE_FUNCTION_CALL(FUNCTION, ARG)                                                                         \
+#define DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(FUNCTION, ARG)                                                           \
   if (ARG->dataType() == ASTNodeDataType::double_t and ARG->descriptor().type() == DiscreteFunctionType::P0) {        \
     switch (ARG->mesh()->dimension()) {                                                                               \
     case 1: {                                                                                                         \
@@ -28,55 +28,55 @@
     }                                                                                                                 \
     }                                                                                                                 \
   } else {                                                                                                            \
-    throw NormalError("invalid operand type " + operand_type_name(ARG));                                              \
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(ARG));                                       \
   }
 
 std::shared_ptr<const IDiscreteFunction>
 sqrt(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(sqrt, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(sqrt, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 abs(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(abs, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(abs, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 sin(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(sin, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(sin, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 cos(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(cos, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(cos, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 tan(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(tan, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(tan, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 asin(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(asin, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(asin, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 acos(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(acos, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(acos, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 atan(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(atan, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(atan, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
@@ -106,14 +106,14 @@ atan2(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<c
       return std::make_shared<const DiscreteFunctionType>(
         atan2(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g)));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
   }
 }
 
@@ -134,14 +134,14 @@ atan2(const double a, const std::shared_ptr<const IDiscreteFunction>& f)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return std::make_shared<const DiscreteFunctionType>(atan2(a, dynamic_cast<const DiscreteFunctionType&>(*f)));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
   }
 }
 
@@ -162,63 +162,63 @@ atan2(const std::shared_ptr<const IDiscreteFunction>& f, const double a)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return std::make_shared<const DiscreteFunctionType>(atan2(dynamic_cast<const DiscreteFunctionType&>(*f), a));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
   }
 }
 
 std::shared_ptr<const IDiscreteFunction>
 sinh(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(sinh, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(sinh, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 cosh(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(cosh, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(cosh, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 tanh(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(tanh, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(tanh, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 asinh(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(asinh, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(asinh, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 acosh(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(acosh, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(acosh, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 atanh(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(atanh, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(atanh, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 exp(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(exp, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(exp, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
 log(const std::shared_ptr<const IDiscreteFunction>& f)
 {
-  DISCRETE_FUNCTION_CALL(log, f);
+  DISCRETE_VH_TO_VH_REAL_FUNCTION_CALL(log, f);
 }
 
 std::shared_ptr<const IDiscreteFunction>
@@ -248,14 +248,14 @@ pow(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con
       return std::make_shared<const DiscreteFunctionType>(
         pow(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g)));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
   }
 }
 
@@ -276,14 +276,14 @@ pow(const double a, const std::shared_ptr<const IDiscreteFunction>& f)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return std::make_shared<const DiscreteFunctionType>(pow(a, dynamic_cast<const DiscreteFunctionType&>(*f)));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
   }
 }
 
@@ -304,14 +304,14 @@ pow(const std::shared_ptr<const IDiscreteFunction>& f, const double a)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return std::make_shared<const DiscreteFunctionType>(pow(dynamic_cast<const DiscreteFunctionType&>(*f), a));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
   }
 }
 
@@ -361,9 +361,11 @@ dot(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con
       return std::make_shared<const DiscreteFunctionResultType>(
         dot(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g)));
     }
+      // LCOV_EXCL_START
     default: {
-      throw UnexpectedError("invalid data dimension " + operand_type_name(f));
+      throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
     }
+      // LCOV_EXCL_STOP
     }
   }
 }
@@ -392,14 +394,14 @@ dot(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con
     case 3: {
       return dot<3>(f, g);
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
   }
 }
 
@@ -432,14 +434,14 @@ dot(const std::shared_ptr<const IDiscreteFunction>& f, const TinyVector<VectorDi
     case 3: {
       return dot<3, VectorDimension>(f, a);
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
   }
 }
 
@@ -472,14 +474,14 @@ dot(const TinyVector<VectorDimension>& a, const std::shared_ptr<const IDiscreteF
     case 3: {
       return dot<3, VectorDimension>(a, f);
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
   }
 }
 
@@ -518,12 +520,14 @@ min(const std::shared_ptr<const IDiscreteFunction>& f)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return min(dynamic_cast<const DiscreteFunctionType&>(*f));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    throw NormalError("invalid operand type " + operand_type_name(f));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
   }
 }
 
@@ -554,14 +558,14 @@ min(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con
       return std::make_shared<const DiscreteFunctionType>(
         min(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g)));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
   }
 }
 
@@ -582,14 +586,14 @@ min(const double a, const std::shared_ptr<const IDiscreteFunction>& f)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return std::make_shared<const DiscreteFunctionType>(min(a, dynamic_cast<const DiscreteFunctionType&>(*f)));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
   }
 }
 
@@ -610,14 +614,14 @@ min(const std::shared_ptr<const IDiscreteFunction>& f, const double a)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return std::make_shared<const DiscreteFunctionType>(min(dynamic_cast<const DiscreteFunctionType&>(*f), a));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
   }
 }
 
@@ -638,12 +642,14 @@ max(const std::shared_ptr<const IDiscreteFunction>& f)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return max(dynamic_cast<const DiscreteFunctionType&>(*f));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    throw NormalError("invalid operand type " + operand_type_name(f));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
   }
 }
 
@@ -674,14 +680,14 @@ max(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<con
       return std::make_shared<const DiscreteFunctionType>(
         max(dynamic_cast<const DiscreteFunctionType&>(*f), dynamic_cast<const DiscreteFunctionType&>(*g)));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
   }
 }
 
@@ -702,14 +708,14 @@ max(const double a, const std::shared_ptr<const IDiscreteFunction>& f)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return std::make_shared<const DiscreteFunctionType>(max(a, dynamic_cast<const DiscreteFunctionType&>(*f)));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(a) << " and " << operand_type_name(f);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
   }
 }
 
@@ -730,14 +736,14 @@ max(const std::shared_ptr<const IDiscreteFunction>& f, const double a)
       using DiscreteFunctionType = DiscreteFunctionP0<3, double>;
       return std::make_shared<const DiscreteFunctionType>(max(dynamic_cast<const DiscreteFunctionType&>(*f), a));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    std::stringstream os;
-    os << "incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(a);
-    throw NormalError(os.str());
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
   }
 }
 
@@ -759,12 +765,14 @@ sum_of(const std::shared_ptr<const IDiscreteFunction>& f)
       using DiscreteFunctionType = DiscreteFunctionP0<3, ValueT>;
       return sum(dynamic_cast<const DiscreteFunctionType&>(*f));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    throw NormalError("invalid operand type " + operand_type_name(f));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
   }
 }
 
@@ -800,12 +808,14 @@ integral_of(const std::shared_ptr<const IDiscreteFunction>& f)
       using DiscreteFunctionType = DiscreteFunctionP0<3, ValueT>;
       return integrate(dynamic_cast<const DiscreteFunctionType&>(*f));
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid mesh dimension");
     }
+      // LCOV_EXCL_STOP
     }
   } else {
-    throw NormalError("invalid operand type " + operand_type_name(f));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
   }
 }
 
diff --git a/src/language/utils/EmbeddedIDiscreteFunctionOperators.cpp b/src/language/utils/EmbeddedIDiscreteFunctionOperators.cpp
index 13def86d07990370bb7dce658dde09f8d06b565a..656164d6fc012ef2469f846d3e54f32a9c1d4927 100644
--- a/src/language/utils/EmbeddedIDiscreteFunctionOperators.cpp
+++ b/src/language/utils/EmbeddedIDiscreteFunctionOperators.cpp
@@ -9,16 +9,6 @@
 #include <scheme/IDiscreteFunction.hpp>
 #include <utils/Exceptions.hpp>
 
-template <typename LHS_T, typename RHS_T>
-PUGS_INLINE std::string
-invalid_operands(const LHS_T& f, const RHS_T& g)
-{
-  std::ostringstream os;
-  os << "undefined binary operator\n";
-  os << "note: incompatible operand types " << operand_type_name(f) << " and " << operand_type_name(g);
-  return os.str();
-}
-
 // unary operators
 template <typename UnaryOperatorT, typename DiscreteFunctionT>
 std::shared_ptr<const IDiscreteFunction>
@@ -52,9 +42,11 @@ applyUnaryOperation(const std::shared_ptr<const IDiscreteFunction>& f)
         auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>&>(*f);
         return applyUnaryOperation<UnaryOperatorT>(fh);
       }
+        // LCOV_EXCL_START
       default: {
-        throw UnexpectedError("invalid operand type " + operand_type_name(f));
+        throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
       }
+        // LCOV_EXCL_STOP
       }
     }
     case ASTNodeDataType::matrix_t: {
@@ -72,14 +64,18 @@ applyUnaryOperation(const std::shared_ptr<const IDiscreteFunction>& f)
         auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f);
         return applyUnaryOperation<UnaryOperatorT>(fh);
       }
+        // LCOV_EXCL_START
       default: {
-        throw UnexpectedError("invalid operand type " + operand_type_name(f));
+        throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
       }
+        // LCOV_EXCL_STOP
       }
     }
+      // LCOV_EXCL_START
     default: {
-      throw UnexpectedError("invalid operand type " + operand_type_name(f));
+      throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
     }
+      // LCOV_EXCL_STOP
     }
     break;
   }
@@ -89,15 +85,19 @@ applyUnaryOperation(const std::shared_ptr<const IDiscreteFunction>& f)
       auto fh = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*f);
       return applyUnaryOperation<UnaryOperatorT>(fh);
     }
+      // LCOV_EXCL_START
     default: {
-      throw UnexpectedError("invalid operand type " + operand_type_name(f));
+      throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
     }
+      // LCOV_EXCL_STOP
     }
     break;
   }
+    // LCOV_EXCL_START
   default: {
-    throw UnexpectedError("invalid operand type " + operand_type_name(f));
+    throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -115,9 +115,11 @@ applyUnaryOperation(const std::shared_ptr<const IDiscreteFunction>& f)
   case 3: {
     return applyUnaryOperation<UnaryOperatorT, 3>(f);
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid mesh dimension");
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -137,7 +139,7 @@ innerCompositionLaw(const DiscreteFunctionT& lhs, const DiscreteFunctionT& rhs)
   using data_type = typename DiscreteFunctionT::data_type;
   if constexpr ((std::is_same_v<language::multiply_op, BinOperatorT> and is_tiny_vector_v<data_type>) or
                 (std::is_same_v<language::divide_op, BinOperatorT> and not std::is_arithmetic_v<data_type>)) {
-    throw NormalError(invalid_operands(lhs, rhs));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(lhs, rhs));
   } else {
     return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(lhs, rhs))>(BinOp<BinOperatorT>{}.eval(lhs, rhs));
   }
@@ -149,7 +151,7 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f,
                     const std::shared_ptr<const IDiscreteFunction>& g)
 {
   Assert(f->mesh() == g->mesh());
-  Assert(isSameDiscretization(f, g));
+  Assert(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g));
 
   switch (f->dataType()) {
   case ASTNodeDataType::double_t: {
@@ -166,15 +168,17 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f,
         auto gh = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*g);
 
         if (fh.size() != gh.size()) {
-          throw NormalError(operand_type_name(f) + " spaces have different sizes");
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f) + " spaces have different sizes");
         }
 
         return innerCompositionLaw<BinOperatorT>(fh, gh);
       } else {
-        throw NormalError(invalid_operands(f, g));
+        throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
       }
     } else {
-      throw UnexpectedError(invalid_operands(f, g));
+      // LCOV_EXCL_START
+      throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
+      // LCOV_EXCL_STOP
     }
   }
   case ASTNodeDataType::vector_t: {
@@ -198,9 +202,11 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f,
 
       return innerCompositionLaw<BinOperatorT>(fh, gh);
     }
+      // LCOV_EXCL_START
     default: {
-      throw NormalError(invalid_operands(f, g));
+      throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
     }
+      // LCOV_EXCL_STOP
     }
   }
   case ASTNodeDataType::matrix_t: {
@@ -225,14 +231,18 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f,
 
       return innerCompositionLaw<BinOperatorT>(fh, gh);
     }
+      // LCOV_EXCL_START
     default: {
-      throw UnexpectedError("invalid data type " + operand_type_name(f));
+      throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
     }
+      // LCOV_EXCL_STOP
     }
   }
+    // LCOV_EXCL_START
   default: {
-    throw UnexpectedError("invalid data type " + operand_type_name(f));
+    throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::invalidOperandType(f));
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -246,9 +256,7 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f,
     throw NormalError("operands are defined on different meshes");
   }
 
-  if (not isSameDiscretization(f, g)) {
-    throw NormalError(invalid_operands(f, g));
-  }
+  Assert(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g));
 
   switch (mesh->dimension()) {
   case 1: {
@@ -260,9 +268,11 @@ innerCompositionLaw(const std::shared_ptr<const IDiscreteFunction>& f,
   case 3: {
     return innerCompositionLaw<BinOperatorT, 3>(f, g);
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid mesh dimension");
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -283,29 +293,23 @@ std::shared_ptr<const IDiscreteFunction>
 applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const IDiscreteFunction>& g)
 {
   Assert(fh.mesh() == g->mesh());
-  Assert(not isSameDiscretization(fh, *g));
+  Assert(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(fh, *g));
   using lhs_data_type = std::decay_t<typename DiscreteFunctionT::data_type>;
 
   switch (g->dataType()) {
   case ASTNodeDataType::double_t: {
-    if constexpr (not std::is_same_v<lhs_data_type, double>) {
-      if constexpr (not is_tiny_matrix_v<lhs_data_type>) {
-        auto gh = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*g);
-
-        return applyBinaryOperation<BinOperatorT>(fh, gh);
-      } else {
-        throw NormalError(invalid_operands(fh, g));
-      }
-    } else if constexpr (std::is_same_v<BinOperatorT, language::multiply_op> and
-                         std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<Dimension, double>>) {
+    if constexpr (std::is_same_v<BinOperatorT, language::multiply_op> and
+                  std::is_same_v<DiscreteFunctionT, DiscreteFunctionP0<Dimension, double>>) {
       if (g->descriptor().type() == DiscreteFunctionType::P0Vector) {
         auto gh = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*g);
         return applyBinaryOperation<BinOperatorT>(fh, gh);
       } else {
-        throw NormalError(invalid_operands(fh, g));
+        // LCOV_EXCL_START
+        throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g));
+        // LCOV_EXCL_STOP
       }
     } else {
-      throw UnexpectedError("should have called innerCompositionLaw");
+      throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g));
     }
   }
   case ASTNodeDataType::vector_t: {
@@ -318,7 +322,7 @@ applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const ID
 
           return applyBinaryOperation<BinOperatorT>(fh, gh);
         } else {
-          throw NormalError(invalid_operands(fh, g));
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g));
         }
       }
       case 2: {
@@ -328,7 +332,7 @@ applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const ID
 
           return applyBinaryOperation<BinOperatorT>(fh, gh);
         } else {
-          throw NormalError(invalid_operands(fh, g));
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g));
         }
       }
       case 3: {
@@ -338,15 +342,17 @@ applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const ID
 
           return applyBinaryOperation<BinOperatorT>(fh, gh);
         } else {
-          throw NormalError(invalid_operands(fh, g));
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g));
         }
       }
+        // LCOV_EXCL_START
       default: {
-        throw UnexpectedError("invalid rhs data type " + operand_type_name(g));
+        throw UnexpectedError("invalid rhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(g));
       }
+        // LCOV_EXCL_STOP
       }
     } else {
-      throw NormalError(invalid_operands(fh, g));
+      throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g));
     }
   }
   case ASTNodeDataType::matrix_t: {
@@ -368,17 +374,21 @@ applyBinaryOperation(const DiscreteFunctionT& fh, const std::shared_ptr<const ID
 
         return applyBinaryOperation<BinOperatorT>(fh, gh);
       }
+        // LCOV_EXCL_START
       default: {
-        throw UnexpectedError("invalid rhs data type " + operand_type_name(g));
+        throw UnexpectedError("invalid rhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(g));
       }
+        // LCOV_EXCL_STOP
       }
     } else {
-      throw NormalError(invalid_operands(fh, g));
+      throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(fh, g));
     }
   }
+    // LCOV_EXCL_START
   default: {
-    throw UnexpectedError("invalid rhs data type " + operand_type_name(g));
+    throw UnexpectedError("invalid rhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(g));
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -388,40 +398,45 @@ applyBinaryOperation(const std::shared_ptr<const IDiscreteFunction>& f,
                      const std::shared_ptr<const IDiscreteFunction>& g)
 {
   Assert(f->mesh() == g->mesh());
-  Assert(not isSameDiscretization(f, g));
-
-  switch (f->dataType()) {
-  case ASTNodeDataType::double_t: {
-    auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*f);
-
-    return applyBinaryOperation<BinOperatorT, Dimension>(fh, g);
-  }
-  case ASTNodeDataType::matrix_t: {
-    Assert(f->dataType().numberOfRows() == f->dataType().numberOfColumns());
-    switch (f->dataType().numberOfRows()) {
-    case 1: {
-      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f);
+  Assert(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g));
 
+  if (f->descriptor().type() == DiscreteFunctionType::P0) {
+    switch (f->dataType()) {
+    case ASTNodeDataType::double_t: {
+      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*f);
       return applyBinaryOperation<BinOperatorT, Dimension>(fh, g);
     }
-    case 2: {
-      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f);
+    case ASTNodeDataType::matrix_t: {
+      Assert(f->dataType().numberOfRows() == f->dataType().numberOfColumns());
+      switch (f->dataType().numberOfRows()) {
+      case 1: {
+        auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f);
 
-      return applyBinaryOperation<BinOperatorT, Dimension>(fh, g);
-    }
-    case 3: {
-      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f);
+        return applyBinaryOperation<BinOperatorT, Dimension>(fh, g);
+      }
+      case 2: {
+        auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f);
 
-      return applyBinaryOperation<BinOperatorT, Dimension>(fh, g);
+        return applyBinaryOperation<BinOperatorT, Dimension>(fh, g);
+      }
+      case 3: {
+        auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f);
+
+        return applyBinaryOperation<BinOperatorT, Dimension>(fh, g);
+      }
+        // LCOV_EXCL_START
+      default: {
+        throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f));
+      }
+        // LCOV_EXCL_STOP
+      }
     }
     default: {
-      throw UnexpectedError("invalid lhs data type " + operand_type_name(f));
+      throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
     }
     }
-  }
-  default: {
-    throw NormalError(invalid_operands(f, g));
-  }
+  } else {
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
   }
 }
 
@@ -435,7 +450,7 @@ applyBinaryOperation(const std::shared_ptr<const IDiscreteFunction>& f,
     throw NormalError("operands are defined on different meshes");
   }
 
-  Assert(not isSameDiscretization(f, g), "should call inner composition instead");
+  Assert(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g), "should call inner composition instead");
 
   switch (mesh->dimension()) {
   case 1: {
@@ -447,36 +462,38 @@ applyBinaryOperation(const std::shared_ptr<const IDiscreteFunction>& f,
   case 3: {
     return applyBinaryOperation<BinOperatorT, 3>(f, g);
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid mesh dimension");
   }
+    // LCOV_EXCL_STOP
   }
 }
 
 std::shared_ptr<const IDiscreteFunction>
 operator+(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g)
 {
-  if (isSameDiscretization(f, g)) {
+  if (EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)) {
     return innerCompositionLaw<language::plus_op>(f, g);
   } else {
-    throw NormalError(invalid_operands(f, g));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
   }
 }
 
 std::shared_ptr<const IDiscreteFunction>
 operator-(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g)
 {
-  if (isSameDiscretization(f, g)) {
+  if (EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)) {
     return innerCompositionLaw<language::minus_op>(f, g);
   } else {
-    throw NormalError(invalid_operands(f, g));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, g));
   }
 }
 
 std::shared_ptr<const IDiscreteFunction>
 operator*(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g)
 {
-  if (isSameDiscretization(f, g)) {
+  if (EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)) {
     return innerCompositionLaw<language::multiply_op>(f, g);
   } else {
     return applyBinaryOperation<language::multiply_op>(f, g);
@@ -486,7 +503,7 @@ operator*(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_p
 std::shared_ptr<const IDiscreteFunction>
 operator/(const std::shared_ptr<const IDiscreteFunction>& f, const std::shared_ptr<const IDiscreteFunction>& g)
 {
-  if (isSameDiscretization(f, g)) {
+  if (EmbeddedIDiscreteFunctionUtils::isSameDiscretization(f, g)) {
     return innerCompositionLaw<language::divide_op>(f, g);
   } else {
     return applyBinaryOperation<language::divide_op>(f, g);
@@ -507,7 +524,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const DiscreteFunctionT&
                          (is_tiny_matrix_v<rhs_data_type> or is_tiny_vector_v<rhs_data_type>)) {
       return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(a, f))>(BinOp<BinOperatorT>{}.eval(a, f));
     } else {
-      throw NormalError(invalid_operands(a, f));
+      throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
     }
   } else if constexpr (std::is_same_v<language::plus_op, BinOperatorT> or
                        std::is_same_v<language::minus_op, BinOperatorT>) {
@@ -516,16 +533,18 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const DiscreteFunctionT&
     } else if constexpr (std::is_same_v<lhs_data_type, rhs_data_type>) {
       return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(a, f))>(BinOp<BinOperatorT>{}.eval(a, f));
     } else {
-      throw NormalError(invalid_operands(a, f));
+      throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
     }
   } else if constexpr (std::is_same_v<language::divide_op, BinOperatorT>) {
     if constexpr (std::is_same_v<lhs_data_type, double> and std::is_arithmetic_v<rhs_data_type>) {
       return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(a, f))>(BinOp<BinOperatorT>{}.eval(a, f));
     } else {
-      throw NormalError(invalid_operands(a, f));
+      // LCOV_EXCL_START
+      throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
+      // LCOV_EXCL_STOP
     }
   } else {
-    throw NormalError(invalid_operands(a, f));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
   }
 }
 
@@ -543,10 +562,10 @@ applyBinaryOperationToVectorWithLeftConstant(const DataType& a, const DiscreteFu
                          (is_tiny_matrix_v<rhs_data_type> or is_tiny_vector_v<rhs_data_type>)) {
       return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(a, f))>(BinOp<BinOperatorT>{}.eval(a, f));
     } else {
-      throw NormalError(invalid_operands(a, f));
+      throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
     }
   } else {
-    throw NormalError(invalid_operands(a, f));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
   }
 }
 
@@ -566,7 +585,9 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
       auto fh = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*f);
       return applyBinaryOperationToVectorWithLeftConstant<BinOperatorT>(a, fh);
     } else {
-      throw NormalError(invalid_operands(a, f));
+      // LCOV_EXCL_START
+      throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
+      // LCOV_EXCL_STOP
     }
   }
   case ASTNodeDataType::vector_t: {
@@ -577,7 +598,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
           auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<1>>&>(*f);
           return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh);
         } else {
-          throw NormalError(invalid_operands(a, f));
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
         }
       }
       case 2: {
@@ -585,7 +606,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
           auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<2>>&>(*f);
           return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh);
         } else {
-          throw NormalError(invalid_operands(a, f));
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
         }
       }
       case 3: {
@@ -593,12 +614,14 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
           auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>&>(*f);
           return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh);
         } else {
-          throw NormalError(invalid_operands(a, f));
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
         }
       }
+        // LCOV_EXCL_START
       default: {
-        throw UnexpectedError("invalid lhs data type " + operand_type_name(f));
+        throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f));
       }
+        // LCOV_EXCL_STOP
       }
     } else {
       switch (f->dataType().dimension()) {
@@ -614,9 +637,11 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
         auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>&>(*f);
         return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh);
       }
+        // LCOV_EXCL_START
       default: {
-        throw UnexpectedError("invalid lhs data type " + operand_type_name(f));
+        throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f));
       }
+        // LCOV_EXCL_STOP
       }
     }
   }
@@ -629,7 +654,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
           auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f);
           return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh);
         } else {
-          throw NormalError(invalid_operands(a, f));
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
         }
       }
       case 2: {
@@ -637,7 +662,7 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
           auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f);
           return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh);
         } else {
-          throw NormalError(invalid_operands(a, f));
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
         }
       }
       case 3: {
@@ -645,12 +670,14 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
           auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f);
           return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh);
         } else {
-          throw NormalError(invalid_operands(a, f));
+          throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
         }
       }
+        // LCOV_EXCL_START
       default: {
-        throw UnexpectedError("invalid lhs data type " + operand_type_name(f));
+        throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f));
       }
+        // LCOV_EXCL_STOP
       }
     } else {
       switch (f->dataType().numberOfRows()) {
@@ -666,15 +693,19 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
         auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f);
         return applyBinaryOperationWithLeftConstant<BinOperatorT>(a, fh);
       }
+        // LCOV_EXCL_START
       default: {
-        throw UnexpectedError("invalid lhs data type " + operand_type_name(f));
+        throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f));
       }
+        // LCOV_EXCL_STOP
       }
     }
   }
+    // LCOV_EXCL_START
   default: {
-    throw NormalError(invalid_operands(a, f));
+    throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(a, f));
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -692,9 +723,11 @@ applyBinaryOperationWithLeftConstant(const DataType& a, const std::shared_ptr<co
   case 3: {
     return applyBinaryOperationWithLeftConstant<BinOperatorT, 3>(a, f);
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid mesh dimension");
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -709,13 +742,17 @@ applyBinaryOperationWithRightConstant(const DiscreteFunctionT& f, const DataType
 
   if constexpr (std::is_same_v<language::multiply_op, BinOperatorT>) {
     if constexpr (is_tiny_matrix_v<lhs_data_type> and is_tiny_matrix_v<rhs_data_type>) {
-      return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(f, a))>(BinOp<BinOperatorT>{}.eval(f, a));
+      if constexpr (lhs_data_type::NumberOfColumns == rhs_data_type::NumberOfRows) {
+        return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(f, a))>(BinOp<BinOperatorT>{}.eval(f, a));
+      } else {
+        throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
+      }
     } else if constexpr (std::is_same_v<lhs_data_type, double> and
                          (is_tiny_matrix_v<rhs_data_type> or is_tiny_vector_v<rhs_data_type> or
                           std::is_arithmetic_v<rhs_data_type>)) {
       return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(f, a))>(BinOp<BinOperatorT>{}.eval(f, a));
     } else {
-      throw NormalError(invalid_operands(f, a));
+      throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
     }
   } else if constexpr (std::is_same_v<language::plus_op, BinOperatorT> or
                        std::is_same_v<language::minus_op, BinOperatorT>) {
@@ -723,10 +760,10 @@ applyBinaryOperationWithRightConstant(const DiscreteFunctionT& f, const DataType
                   (std::is_arithmetic_v<lhs_data_type> and std::is_arithmetic_v<rhs_data_type>)) {
       return std::make_shared<decltype(BinOp<BinOperatorT>{}.eval(f, a))>(BinOp<BinOperatorT>{}.eval(f, a));
     } else {
-      throw NormalError(invalid_operands(f, a));
+      throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
     }
   } else {
-    throw NormalError(invalid_operands(f, a));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
   }
 }
 
@@ -735,7 +772,7 @@ std::shared_ptr<const IDiscreteFunction>
 applyBinaryOperationWithRightConstant(const std::shared_ptr<const IDiscreteFunction>& f, const DataType& a)
 {
   if (f->descriptor().type() != DiscreteFunctionType::P0) {
-    throw NormalError(invalid_operands(f, a));
+    throw NormalError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
   }
 
   switch (f->dataType()) {
@@ -746,61 +783,54 @@ applyBinaryOperationWithRightConstant(const std::shared_ptr<const IDiscreteFunct
     auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*f);
     return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
   }
+  case ASTNodeDataType::vector_t: {
+    switch (f->dataType().dimension()) {
+    case 1: {
+      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<1>>&>(*f);
+      return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
+    }
+    case 2: {
+      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<2>>&>(*f);
+      return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
+    }
+    case 3: {
+      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>&>(*f);
+      return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f));
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
   case ASTNodeDataType::matrix_t: {
     Assert(f->dataType().numberOfRows() == f->dataType().numberOfColumns());
-    if constexpr (is_tiny_matrix_v<DataType>) {
-      switch (f->dataType().numberOfRows()) {
-      case 1: {
-        if constexpr (std::is_same_v<DataType, TinyMatrix<1>>) {
-          auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f);
-          return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
-        } else {
-          throw NormalError(invalid_operands(f, a));
-        }
-      }
-      case 2: {
-        if constexpr (std::is_same_v<DataType, TinyMatrix<2>>) {
-          auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f);
-          return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
-        } else {
-          throw NormalError(invalid_operands(f, a));
-        }
-      }
-      case 3: {
-        if constexpr (std::is_same_v<DataType, TinyMatrix<3>>) {
-          auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f);
-          return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
-        } else {
-          throw NormalError(invalid_operands(f, a));
-        }
-      }
-      default: {
-        throw UnexpectedError("invalid lhs data type " + operand_type_name(f));
-      }
-      }
-    } else {
-      switch (f->dataType().numberOfRows()) {
-      case 1: {
-        auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f);
-        return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
-      }
-      case 2: {
-        auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f);
-        return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
-      }
-      case 3: {
-        auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f);
-        return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
-      }
-      default: {
-        throw UnexpectedError("invalid lhs data type " + operand_type_name(f));
-      }
-      }
+    switch (f->dataType().numberOfRows()) {
+    case 1: {
+      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>&>(*f);
+      return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
+    }
+    case 2: {
+      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>&>(*f);
+      return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
+    }
+    case 3: {
+      auto fh = dynamic_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>&>(*f);
+      return applyBinaryOperationWithRightConstant<BinOperatorT>(fh, a);
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid lhs data type " + EmbeddedIDiscreteFunctionUtils::getOperandTypeName(f));
+    }
+      // LCOV_EXCL_STOP
     }
   }
+    // LCOV_EXCL_START
   default: {
-    throw NormalError(invalid_operands(f, a));
+    throw UnexpectedError(EmbeddedIDiscreteFunctionUtils::incompatibleOperandTypes(f, a));
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -818,9 +848,11 @@ applyBinaryOperationWithRightConstant(const std::shared_ptr<const IDiscreteFunct
   case 3: {
     return applyBinaryOperationWithRightConstant<BinOperatorT, 3>(f, a);
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid mesh dimension");
   }
+    // LCOV_EXCL_STOP
   }
 }
 
diff --git a/src/language/utils/EmbeddedIDiscreteFunctionUtils.cpp b/src/language/utils/EmbeddedIDiscreteFunctionUtils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..286988b9b2849fb34dc84948be05414ebbad6d5b
--- /dev/null
+++ b/src/language/utils/EmbeddedIDiscreteFunctionUtils.cpp
@@ -0,0 +1,27 @@
+#include <language/utils/EmbeddedIDiscreteFunctionUtils.hpp>
+
+#include <utils/Exceptions.hpp>
+
+bool
+EmbeddedIDiscreteFunctionUtils::isSameDiscretization(const IDiscreteFunction& f, const IDiscreteFunction& g)
+{
+  if ((f.dataType() == g.dataType()) and (f.descriptor().type() == g.descriptor().type())) {
+    switch (f.dataType()) {
+    case ASTNodeDataType::double_t: {
+      return true;
+    }
+    case ASTNodeDataType::vector_t: {
+      return f.dataType().dimension() == g.dataType().dimension();
+    }
+    case ASTNodeDataType::matrix_t: {
+      return (f.dataType().numberOfRows() == g.dataType().numberOfRows()) and
+             (f.dataType().numberOfColumns() == g.dataType().numberOfColumns());
+    }
+    default: {
+      throw UnexpectedError("invalid data type " + getOperandTypeName(f));
+    }
+    }
+  } else {
+    return false;
+  }
+}
diff --git a/src/language/utils/EmbeddedIDiscreteFunctionUtils.hpp b/src/language/utils/EmbeddedIDiscreteFunctionUtils.hpp
index 981cd8be9ca166038b5aa4b913dae63488868b64..1e61f917dfa448add18b562dd403a70101dbec3b 100644
--- a/src/language/utils/EmbeddedIDiscreteFunctionUtils.hpp
+++ b/src/language/utils/EmbeddedIDiscreteFunctionUtils.hpp
@@ -1,58 +1,50 @@
 #ifndef EMBEDDED_I_DISCRETE_FUNCTION_UTILS_HPP
 #define EMBEDDED_I_DISCRETE_FUNCTION_UTILS_HPP
 
+#include <language/utils/ASTNodeDataType.hpp>
 #include <scheme/IDiscreteFunction.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
-#include <utils/Exceptions.hpp>
 
-#include <sstream>
 #include <string>
 
-template <typename T>
-PUGS_INLINE std::string
-operand_type_name(const T& t)
+struct EmbeddedIDiscreteFunctionUtils
 {
-  if constexpr (is_shared_ptr_v<T>) {
-    Assert(t.use_count() > 0);
-    return operand_type_name(*t);
-  } else if constexpr (std::is_base_of_v<IDiscreteFunction, std::decay_t<T>>) {
-    return "Vh(" + name(t.descriptor().type()) + ':' + dataTypeName(t.dataType()) + ')';
-  } else {
-    return dataTypeName(ast_node_data_type_from<T>);
+  template <typename T>
+  static PUGS_INLINE std::string
+  getOperandTypeName(const T& t)
+  {
+    if constexpr (is_shared_ptr_v<T>) {
+      Assert(t.use_count() > 0, "dangling shared_ptr");
+      return getOperandTypeName(*t);
+    } else if constexpr (std::is_base_of_v<IDiscreteFunction, std::decay_t<T>>) {
+      return "Vh(" + name(t.descriptor().type()) + ':' + dataTypeName(t.dataType()) + ')';
+    } else {
+      return dataTypeName(ast_node_data_type_from<T>);
+    }
   }
-}
 
-PUGS_INLINE
-bool
-isSameDiscretization(const IDiscreteFunction& f, const IDiscreteFunction& g)
-{
-  if ((f.dataType() == g.dataType()) and (f.descriptor().type() == g.descriptor().type())) {
-    switch (f.dataType()) {
-    case ASTNodeDataType::double_t: {
-      return true;
-    }
-    case ASTNodeDataType::vector_t: {
-      return f.dataType().dimension() == g.dataType().dimension();
-    }
-    case ASTNodeDataType::matrix_t: {
-      return (f.dataType().numberOfRows() == g.dataType().numberOfRows()) and
-             (f.dataType().numberOfColumns() == g.dataType().numberOfColumns());
-    }
-    default: {
-      throw UnexpectedError("invalid data type " + operand_type_name(f));
-    }
-    }
-  } else {
-    return false;
+  static bool isSameDiscretization(const IDiscreteFunction& f, const IDiscreteFunction& g);
+
+  static PUGS_INLINE bool
+  isSameDiscretization(const std::shared_ptr<const IDiscreteFunction>& f,
+                       const std::shared_ptr<const IDiscreteFunction>& g)
+  {
+    return isSameDiscretization(*f, *g);
   }
-}
 
-PUGS_INLINE
-bool
-isSameDiscretization(const std::shared_ptr<const IDiscreteFunction>& f,
-                     const std::shared_ptr<const IDiscreteFunction>& g)
-{
-  return isSameDiscretization(*f, *g);
-}
+  template <typename T1, typename T2>
+  PUGS_INLINE static std::string
+  incompatibleOperandTypes(const T1& t1, const T2& t2)
+  {
+    return "incompatible operand types " + getOperandTypeName(t1) + " and " + getOperandTypeName(t2);
+  }
+
+  template <typename T>
+  PUGS_INLINE static std::string
+  invalidOperandType(const T& t)
+  {
+    return "invalid operand type " + getOperandTypeName(t);
+  }
+};
 
 #endif   // EMBEDDED_I_DISCRETE_FUNCTION_UTILS_HPP
diff --git a/src/language/utils/EvaluateAtPoints.hpp b/src/language/utils/EvaluateAtPoints.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d842dd44b96b02caf23515d4c35da0a0ee277865
--- /dev/null
+++ b/src/language/utils/EvaluateAtPoints.hpp
@@ -0,0 +1,68 @@
+#ifndef EVALUATE_AT_POINTS_HPP
+#define EVALUATE_AT_POINTS_HPP
+
+#include <language/utils/PugsFunctionAdapter.hpp>
+#include <utils/Array.hpp>
+
+class FunctionSymbolId;
+
+template <typename T>
+class EvaluateAtPoints;
+template <typename OutputType, typename InputType>
+class EvaluateAtPoints<OutputType(InputType)> : public PugsFunctionAdapter<OutputType(InputType)>
+{
+  using Adapter = PugsFunctionAdapter<OutputType(InputType)>;
+
+ public:
+  template <typename InputArrayT, typename OutputArrayT>
+  static PUGS_INLINE void
+  evaluateTo(const FunctionSymbolId& function_symbol_id, const InputArrayT& position, OutputArrayT& value)
+  {
+    static_assert(std::is_same_v<std::remove_const_t<typename InputArrayT::data_type>, InputType>,
+                  "invalid input data type");
+    static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>,
+                  "invalid output data type");
+    Assert(size(value) == size(position));
+
+    auto& expression    = Adapter::getFunctionExpression(function_symbol_id);
+    auto convert_result = Adapter::getResultConverter(expression.m_data_type);
+
+    auto context_list = Adapter::getContextList(expression);
+
+    using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space;
+    Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens;
+
+    if constexpr (std::is_arithmetic_v<OutputType>) {
+      value.fill(0);
+    } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) {
+      value.fill(zero);
+    } else {
+      static_assert(std::is_same_v<OutputType, double>, "unexpected output type");
+    }
+
+    parallel_for(size(position), [=, &expression, &tokens](typename InputArrayT::index_type i) {
+      const int32_t t = tokens.acquire();
+
+      auto& execution_policy = context_list[t];
+
+      Adapter::convertArgs(execution_policy.currentContext(), position[i]);
+      auto result = expression.execute(execution_policy);
+      value[i]    = convert_result(std::move(result));
+
+      tokens.release(t);
+    });
+  }
+
+  template <class InputArrayT>
+  static PUGS_INLINE Array<OutputType>
+  evaluate(const FunctionSymbolId& function_symbol_id, const InputArrayT& position)
+  {
+    static_assert(std::is_same_v<std::remove_const_t<typename InputArrayT::data_type>, InputType>,
+                  "invalid input data type");
+    Array<OutputType> value(size(position));
+    evaluateTo(function_symbol_id, position, value);
+    return value;
+  }
+};
+
+#endif   // EVALUATE_AT_POINTS_HPP
diff --git a/src/language/utils/IntegrateCellArray.hpp b/src/language/utils/IntegrateCellArray.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..41450de3b9675fbe929fcaf0fde855ccb1be2fe4
--- /dev/null
+++ b/src/language/utils/IntegrateCellArray.hpp
@@ -0,0 +1,69 @@
+#ifndef INTEGRATE_CELL_ARRAY_HPP
+#define INTEGRATE_CELL_ARRAY_HPP
+
+#include <language/utils/IntegrateCellValue.hpp>
+#include <mesh/CellType.hpp>
+#include <mesh/ItemArray.hpp>
+
+template <typename T>
+class IntegrateCellArray;
+template <typename OutputType, typename InputType>
+class IntegrateCellArray<OutputType(InputType)>
+{
+  static constexpr size_t Dimension = OutputType::Dimension;
+
+ public:
+  template <typename MeshType>
+  PUGS_INLINE static CellArray<OutputType>
+  integrate(const std::vector<FunctionSymbolId>& function_symbol_id_list,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh)
+  {
+    CellArray<OutputType> cell_array{mesh.connectivity(), function_symbol_id_list.size()};
+
+    for (size_t i_function_symbol = 0; i_function_symbol < function_symbol_id_list.size(); ++i_function_symbol) {
+      const FunctionSymbolId& function_symbol_id = function_symbol_id_list[i_function_symbol];
+      CellValue<OutputType> cell_value =
+        IntegrateCellValue<OutputType(InputType)>::integrate(function_symbol_id, quadrature_descriptor, mesh);
+      parallel_for(
+        cell_value.numberOfItems(),
+        PUGS_LAMBDA(CellId cell_id) { cell_array[cell_id][i_function_symbol] = cell_value[cell_id]; });
+    }
+
+    return cell_array;
+  }
+
+  template <typename MeshType>
+  PUGS_INLINE static Table<OutputType>
+  integrate(const std::vector<FunctionSymbolId>& function_symbol_id_list,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const Array<const CellId>& list_of_cells)
+  {
+    Table<OutputType> table{list_of_cells.size(), function_symbol_id_list.size()};
+
+    for (size_t i_function_symbol = 0; i_function_symbol < function_symbol_id_list.size(); ++i_function_symbol) {
+      const FunctionSymbolId& function_symbol_id = function_symbol_id_list[i_function_symbol];
+      Array<OutputType> array =
+        IntegrateCellValue<OutputType(InputType)>::integrate(function_symbol_id, quadrature_descriptor, mesh,
+                                                             list_of_cells);
+
+      parallel_for(
+        array.size(), PUGS_LAMBDA(size_t i) { table[i][i_function_symbol] = array[i]; });
+    }
+
+    return table;
+  }
+
+  template <typename MeshType>
+  PUGS_INLINE static Table<OutputType>
+  integrate(const std::vector<FunctionSymbolId>& function_symbol_id_list,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const Array<CellId>& list_of_cells)
+  {
+    return integrate(function_symbol_id_list, quadrature_descriptor, mesh, Array<const CellId>{list_of_cells});
+  }
+};
+
+#endif   // INTEGRATE_CELL_ARRAY_HPP
diff --git a/src/language/utils/IntegrateCellValue.hpp b/src/language/utils/IntegrateCellValue.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cd8bacc072bef0f5c68b1702fa9fef33c6d94f7d
--- /dev/null
+++ b/src/language/utils/IntegrateCellValue.hpp
@@ -0,0 +1,51 @@
+#ifndef INTEGRATE_CELL_VALUE_HPP
+#define INTEGRATE_CELL_VALUE_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+#include <language/utils/IntegrateOnCells.hpp>
+#include <mesh/ItemType.hpp>
+#include <mesh/ItemValue.hpp>
+#include <mesh/Mesh.hpp>
+
+template <typename T>
+class IntegrateCellValue;
+template <typename OutputType, typename InputType>
+class IntegrateCellValue<OutputType(InputType)>
+{
+ public:
+  template <typename MeshType>
+  PUGS_INLINE static CellValue<OutputType>
+  integrate(const FunctionSymbolId& function_symbol_id,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh)
+  {
+    CellValue<OutputType> value(mesh.connectivity());
+    IntegrateOnCells<OutputType(const InputType)>::template integrateTo<MeshType>(function_symbol_id,
+                                                                                  quadrature_descriptor, mesh, value);
+
+    return value;
+  }
+
+  template <typename MeshType>
+  PUGS_INLINE static Array<OutputType>
+  integrate(const FunctionSymbolId& function_symbol_id,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const Array<const CellId>& list_of_cells)
+  {
+    return IntegrateOnCells<OutputType(const InputType)>::integrate(function_symbol_id, quadrature_descriptor, mesh,
+                                                                    Array<const CellId>{list_of_cells});
+  }
+
+  template <typename MeshType>
+  PUGS_INLINE static Array<OutputType>
+  integrate(const FunctionSymbolId& function_symbol_id,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const Array<CellId>& list_of_cells)
+  {
+    return integrate(function_symbol_id, quadrature_descriptor, mesh, Array<const CellId>{list_of_cells});
+  }
+};
+
+#endif   // INTEGRATE_CELL_VALUE_HPP
diff --git a/src/language/utils/IntegrateOnCells.hpp b/src/language/utils/IntegrateOnCells.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fa47bb7856ec695d87d7702d9c561a2ebfe6ca6b
--- /dev/null
+++ b/src/language/utils/IntegrateOnCells.hpp
@@ -0,0 +1,637 @@
+#ifndef INTEGRATE_ON_CELLS_HPP
+#define INTEGRATE_ON_CELLS_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/CubeTransformation.hpp>
+#include <geometry/LineTransformation.hpp>
+#include <geometry/PrismTransformation.hpp>
+#include <geometry/PyramidTransformation.hpp>
+#include <geometry/SquareTransformation.hpp>
+#include <geometry/TetrahedronTransformation.hpp>
+#include <geometry/TriangleTransformation.hpp>
+#include <language/utils/PugsFunctionAdapter.hpp>
+#include <mesh/CellType.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemId.hpp>
+#include <utils/Array.hpp>
+
+class FunctionSymbolId;
+
+template <typename T>
+class IntegrateOnCells;
+template <typename OutputType, typename InputType>
+class IntegrateOnCells<OutputType(InputType)> : public PugsFunctionAdapter<OutputType(InputType)>
+{
+ private:
+  using Adapter = PugsFunctionAdapter<OutputType(InputType)>;
+
+  template <size_t Dimension>
+  class AllCells
+  {
+   private:
+    const Connectivity<Dimension>& m_connectivity;
+
+   public:
+    using index_type = CellId;
+
+    PUGS_INLINE
+    CellId
+    cellId(const CellId cell_id) const
+    {
+      return cell_id;
+    }
+
+    PUGS_INLINE
+    size_t
+    size() const
+    {
+      return m_connectivity.numberOfCells();
+    }
+
+    PUGS_INLINE
+    AllCells(const Connectivity<Dimension>& connectivity) : m_connectivity{connectivity} {}
+  };
+
+  class CellList
+  {
+   private:
+    const Array<const CellId>& m_cell_list;
+
+   public:
+    using index_type = Array<const CellId>::index_type;
+
+    PUGS_INLINE
+    CellId
+    cellId(const index_type index) const
+    {
+      return m_cell_list[index];
+    }
+
+    PUGS_INLINE
+    size_t
+    size() const
+    {
+      return m_cell_list.size();
+    }
+
+    PUGS_INLINE
+    CellList(const Array<const CellId>& cell_list) : m_cell_list{cell_list} {}
+  };
+
+  template <typename MeshType, typename OutputArrayT, typename ListTypeT>
+  static PUGS_INLINE void
+  _tensorialIntegrateTo(const FunctionSymbolId& function_symbol_id,
+                        const IQuadratureDescriptor& quadrature_descriptor,
+                        const MeshType& mesh,
+                        const ListTypeT& cell_list,
+                        OutputArrayT& value)
+  {
+    constexpr size_t Dimension = MeshType::Dimension;
+
+    static_assert(std::is_same_v<TinyVector<Dimension>, InputType>, "invalid input data type");
+    static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>,
+                  "invalid output data type");
+
+    auto& expression    = Adapter::getFunctionExpression(function_symbol_id);
+    auto convert_result = Adapter::getResultConverter(expression.m_data_type);
+
+    auto context_list = Adapter::getContextList(expression);
+
+    using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space;
+    Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens;
+
+    if constexpr (std::is_arithmetic_v<OutputType>) {
+      value.fill(0);
+    } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) {
+      value.fill(zero);
+    } else {
+      static_assert(std::is_same_v<OutputType, double>, "unexpected output type");
+    }
+
+    const auto& connectivity = mesh.connectivity();
+
+    const auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+    const auto cell_type           = connectivity.cellType();
+    const auto xr                  = mesh.xr();
+
+    auto invalid_cell = std::make_pair(false, CellId{});
+
+    const auto qf = [&] {
+      if constexpr (Dimension == 1) {
+        return QuadratureManager::instance().getLineFormula(quadrature_descriptor);
+      } else if constexpr (Dimension == 2) {
+        return QuadratureManager::instance().getSquareFormula(quadrature_descriptor);
+      } else {
+        static_assert(Dimension == 3);
+        return QuadratureManager::instance().getCubeFormula(quadrature_descriptor);
+      }
+    }();
+
+    parallel_for(cell_list.size(), [=, &expression, &tokens, &invalid_cell, &qf,
+                                    &cell_list](typename ListTypeT::index_type index) {
+      const int32_t t        = tokens.acquire();
+      auto& execution_policy = context_list[t];
+
+      const CellId cell_id = cell_list.cellId(index);
+
+      const auto cell_to_node = cell_to_node_matrix[cell_id];
+
+      if constexpr (Dimension == 1) {
+        switch (cell_type[cell_id]) {
+        case CellType::Line: {
+          const LineTransformation<1> T(xr[cell_to_node[0]], xr[cell_to_node[1]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant() * convert_result(std::move(result));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else if constexpr (Dimension == 2) {
+        switch (cell_type[cell_id]) {
+        case CellType::Triangle: {
+          const SquareTransformation<2> T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[2]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+          }
+          break;
+        }
+        case CellType::Quadrangle: {
+          const SquareTransformation<2> T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[3]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else {
+        static_assert(Dimension == 3);
+
+        switch (cell_type[cell_id]) {
+        case CellType::Tetrahedron: {
+          const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                     xr[cell_to_node[2]],   //
+                                     xr[cell_to_node[3]], xr[cell_to_node[3]], xr[cell_to_node[3]],
+                                     xr[cell_to_node[3]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+          }
+          break;
+        }
+        case CellType::Pyramid: {
+          if (cell_to_node.size() == 5) {
+            const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],   //
+                                       xr[cell_to_node[3]],                                             //
+                                       xr[cell_to_node[4]], xr[cell_to_node[4]], xr[cell_to_node[4]],
+                                       xr[cell_to_node[4]]);
+
+            for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+              const auto xi = qf.point(i_point);
+              Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+              auto result = expression.execute(execution_policy);
+              value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+            }
+          } else {
+            // LCOV_EXCL_START
+            throw NotImplementedError("integration on pyramid with non-quadrangular base (number of nodes " +
+                                      std::to_string(cell_to_node.size()) + ")");
+            // LCOV_EXCL_STOP
+          }
+          break;
+        }
+        case CellType::Diamond: {
+          if (cell_to_node.size() == 5) {
+            {   // top tetrahedron
+              const CubeTransformation T0(xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                          xr[cell_to_node[3]],   //
+                                          xr[cell_to_node[4]], xr[cell_to_node[4]], xr[cell_to_node[4]],
+                                          xr[cell_to_node[4]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                Adapter::convertArgs(execution_policy.currentContext(), T0(xi));
+                auto result = expression.execute(execution_policy);
+                value[index] += qf.weight(i_point) * T0.jacobianDeterminant(xi) * convert_result(std::move(result));
+              }
+            }
+            {   // bottom tetrahedron
+              const CubeTransformation T1(xr[cell_to_node[3]], xr[cell_to_node[3]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[1]],   //
+                                          xr[cell_to_node[0]], xr[cell_to_node[0]], xr[cell_to_node[0]],
+                                          xr[cell_to_node[0]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                Adapter::convertArgs(execution_policy.currentContext(), T1(xi));
+                auto result = expression.execute(execution_policy);
+                value[index] += qf.weight(i_point) * T1.jacobianDeterminant(xi) * convert_result(std::move(result));
+              }
+            }
+          } else if (cell_to_node.size() == 6) {
+            {   // top pyramid
+              const CubeTransformation T0(xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                          xr[cell_to_node[4]],   //
+                                          xr[cell_to_node[5]], xr[cell_to_node[5]], xr[cell_to_node[5]],
+                                          xr[cell_to_node[5]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                Adapter::convertArgs(execution_policy.currentContext(), T0(xi));
+                auto result = expression.execute(execution_policy);
+                value[index] += qf.weight(i_point) * T0.jacobianDeterminant(xi) * convert_result(std::move(result));
+              }
+            }
+            {   // bottom pyramid
+              const CubeTransformation T1(xr[cell_to_node[4]], xr[cell_to_node[3]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[1]],   //
+                                          xr[cell_to_node[0]], xr[cell_to_node[0]], xr[cell_to_node[0]],
+                                          xr[cell_to_node[0]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                Adapter::convertArgs(execution_policy.currentContext(), T1(xi));
+                auto result = expression.execute(execution_policy);
+                value[index] += qf.weight(i_point) * T1.jacobianDeterminant(xi) * convert_result(std::move(result));
+              }
+            }
+          } else {
+            // LCOV_EXCL_START
+            throw NotImplementedError("integration on diamond with non-quadrangular base (" +
+                                      std::to_string(cell_to_node.size()) + ")");
+            // LCOV_EXCL_STOP
+          }
+          break;
+        }
+        case CellType::Prism: {
+          const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]],   //
+                                     xr[cell_to_node[2]], xr[cell_to_node[2]],   //
+                                     xr[cell_to_node[3]], xr[cell_to_node[4]],   //
+                                     xr[cell_to_node[5]], xr[cell_to_node[5]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+          }
+          break;
+        }
+        case CellType::Hexahedron: {
+          const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                     xr[cell_to_node[4]], xr[cell_to_node[5]], xr[cell_to_node[6]],
+                                     xr[cell_to_node[7]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+
+      tokens.release(t);
+    });
+
+    // LCOV_EXCL_START
+    if (invalid_cell.first) {
+      std::ostringstream os;
+      os << "invalid cell type for integration: " << name(cell_type[invalid_cell.second]);
+      throw UnexpectedError(os.str());
+    }
+    // LCOV_EXCL_STOP
+  }
+
+  template <typename MeshType, typename OutputArrayT, typename ListTypeT>
+  static PUGS_INLINE void
+  _directIntegrateTo(const FunctionSymbolId& function_symbol_id,
+                     const IQuadratureDescriptor& quadrature_descriptor,
+                     const MeshType& mesh,
+                     const ListTypeT& cell_list,
+                     OutputArrayT& value)
+  {
+    Assert(not quadrature_descriptor.isTensorial());
+
+    constexpr size_t Dimension = MeshType::Dimension;
+
+    static_assert(std::is_same_v<TinyVector<Dimension>, InputType>, "invalid input data type");
+    static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>,
+                  "invalid output data type");
+
+    auto& expression    = Adapter::getFunctionExpression(function_symbol_id);
+    auto convert_result = Adapter::getResultConverter(expression.m_data_type);
+
+    auto context_list = Adapter::getContextList(expression);
+
+    using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space;
+    Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens;
+
+    if constexpr (std::is_arithmetic_v<OutputType>) {
+      value.fill(0);
+    } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) {
+      value.fill(zero);
+    } else {
+      static_assert(std::is_same_v<OutputType, double>, "unexpected output type");
+    }
+
+    const auto& connectivity = mesh.connectivity();
+
+    const auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+    const auto cell_type           = connectivity.cellType();
+    const auto xr                  = mesh.xr();
+
+    auto invalid_cell = std::make_pair(false, CellId{});
+
+    parallel_for(cell_list.size(), [=, &expression, &tokens, &invalid_cell, &quadrature_descriptor,
+                                    &cell_list](const typename ListTypeT::index_type& index) {
+      const int32_t t        = tokens.acquire();
+      auto& execution_policy = context_list[t];
+
+      const CellId cell_id = cell_list.cellId(index);
+
+      const auto cell_to_node = cell_to_node_matrix[cell_id];
+
+      if constexpr (Dimension == 1) {
+        switch (cell_type[cell_id]) {
+        case CellType::Line: {
+          const LineTransformation<1> T(xr[cell_to_node[0]], xr[cell_to_node[1]]);
+          const auto qf = QuadratureManager::instance().getLineFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant() * convert_result(std::move(result));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else if constexpr (Dimension == 2) {
+        switch (cell_type[cell_id]) {
+        case CellType::Triangle: {
+          const TriangleTransformation<2> T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]]);
+          const auto qf = QuadratureManager::instance().getTriangleFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant() * convert_result(std::move(result));
+          }
+          break;
+        }
+        case CellType::Quadrangle: {
+          const SquareTransformation<2> T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[3]]);
+          const auto qf = QuadratureManager::instance().getSquareFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else {
+        static_assert(Dimension == 3);
+
+        switch (cell_type[cell_id]) {
+        case CellType::Tetrahedron: {
+          const TetrahedronTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]],   //
+                                            xr[cell_to_node[2]], xr[cell_to_node[3]]);
+          const auto qf = QuadratureManager::instance().getTetrahedronFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant() * convert_result(std::move(result));
+          }
+          break;
+        }
+        case CellType::Pyramid: {
+          if (cell_to_node.size() == 5) {
+            const PyramidTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],   //
+                                          xr[cell_to_node[3]], xr[cell_to_node[4]]);
+            const auto qf = QuadratureManager::instance().getPyramidFormula(quadrature_descriptor);
+
+            for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+              const auto xi = qf.point(i_point);
+              Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+              auto result = expression.execute(execution_policy);
+              value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+            }
+          } else {
+            // LCOV_EXCL_START
+            throw NotImplementedError("integration on pyramid with non-quadrangular base");
+            // LCOV_EXCL_STOP
+          }
+          break;
+        }
+        case CellType::Diamond: {
+          if (cell_to_node.size() == 5) {
+            const auto qf = QuadratureManager::instance().getTetrahedronFormula(quadrature_descriptor);
+            {   // top tetrahedron
+              const TetrahedronTransformation T0(xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],   //
+                                                 xr[cell_to_node[4]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                Adapter::convertArgs(execution_policy.currentContext(), T0(xi));
+                auto result = expression.execute(execution_policy);
+                value[index] += qf.weight(i_point) * T0.jacobianDeterminant() * convert_result(std::move(result));
+              }
+            }
+            {   // bottom tetrahedron
+              const TetrahedronTransformation T1(xr[cell_to_node[3]], xr[cell_to_node[2]], xr[cell_to_node[1]],   //
+                                                 xr[cell_to_node[0]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                Adapter::convertArgs(execution_policy.currentContext(), T1(xi));
+                auto result = expression.execute(execution_policy);
+                value[index] += qf.weight(i_point) * T1.jacobianDeterminant() * convert_result(std::move(result));
+              }
+            }
+          } else if (cell_to_node.size() == 6) {
+            const auto qf = QuadratureManager::instance().getPyramidFormula(quadrature_descriptor);
+            {   // top pyramid
+              const PyramidTransformation T0(xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                             xr[cell_to_node[4]], xr[cell_to_node[5]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                Adapter::convertArgs(execution_policy.currentContext(), T0(xi));
+                auto result = expression.execute(execution_policy);
+                value[index] += qf.weight(i_point) * T0.jacobianDeterminant(xi) * convert_result(std::move(result));
+              }
+            }
+            {   // bottom pyramid
+              const PyramidTransformation T1(xr[cell_to_node[4]], xr[cell_to_node[3]], xr[cell_to_node[2]],
+                                             xr[cell_to_node[1]], xr[cell_to_node[0]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                Adapter::convertArgs(execution_policy.currentContext(), T1(xi));
+                auto result = expression.execute(execution_policy);
+                value[index] += qf.weight(i_point) * T1.jacobianDeterminant(xi) * convert_result(std::move(result));
+              }
+            }
+          } else {
+            // LCOV_EXCL_START
+            throw NotImplementedError("integration on diamond with non-quadrangular base (" +
+                                      std::to_string(cell_to_node.size()) + ")");
+            // LCOV_EXCL_STOP
+          }
+          break;
+        }
+        case CellType::Prism: {
+          const PrismTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                      xr[cell_to_node[3]], xr[cell_to_node[4]], xr[cell_to_node[5]]);
+          const auto qf = QuadratureManager::instance().getPrismFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+          }
+          break;
+        }
+        case CellType::Hexahedron: {
+          const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                     xr[cell_to_node[4]], xr[cell_to_node[5]], xr[cell_to_node[6]],
+                                     xr[cell_to_node[7]]);
+          const auto qf = QuadratureManager::instance().getCubeFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            Adapter::convertArgs(execution_policy.currentContext(), T(xi));
+            auto result = expression.execute(execution_policy);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * convert_result(std::move(result));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+
+      tokens.release(t);
+    });
+
+    // LCOV_EXCL_START
+    if (invalid_cell.first) {
+      std::ostringstream os;
+      os << "invalid cell type for integration: " << name(cell_type[invalid_cell.second]);
+      throw UnexpectedError(os.str());
+    }
+    // LCOV_EXCL_STOP
+  }
+
+ public:
+  template <typename MeshType, typename ArrayT>
+  static PUGS_INLINE void
+  integrateTo(const FunctionSymbolId& function_symbol_id,
+              const IQuadratureDescriptor& quadrature_descriptor,
+              const MeshType& mesh,
+              ArrayT& value)
+  {
+    constexpr size_t Dimension = MeshType::Dimension;
+
+    if (quadrature_descriptor.isTensorial()) {
+      _tensorialIntegrateTo<MeshType, ArrayT>(function_symbol_id, quadrature_descriptor, mesh,
+                                              AllCells<Dimension>{mesh.connectivity()}, value);
+    } else {
+      _directIntegrateTo<MeshType, ArrayT>(function_symbol_id, quadrature_descriptor, mesh,
+                                           AllCells<Dimension>{mesh.connectivity()}, value);
+    }
+  }
+
+  template <typename MeshType, template <typename DataType> typename ArrayT>
+  static PUGS_INLINE ArrayT<OutputType>
+  integrate(const FunctionSymbolId& function_symbol_id,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const ArrayT<const CellId>& cell_list)
+  {
+    ArrayT<OutputType> value(size(cell_list));
+    if (quadrature_descriptor.isTensorial()) {
+      _tensorialIntegrateTo<MeshType, ArrayT<OutputType>>(function_symbol_id, quadrature_descriptor, mesh,
+                                                          CellList{cell_list}, value);
+    } else {
+      _directIntegrateTo<MeshType, ArrayT<OutputType>>(function_symbol_id, quadrature_descriptor, mesh,
+                                                       CellList{cell_list}, value);
+    }
+
+    return value;
+  }
+
+  template <typename MeshType, template <typename DataType> typename ArrayT>
+  static PUGS_INLINE ArrayT<OutputType>
+  integrate(const FunctionSymbolId& function_symbol_id,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const ArrayT<CellId>& cell_list)
+  {
+    return integrate(function_symbol_id, quadrature_descriptor, mesh, ArrayT<const CellId>{cell_list});
+  }
+};
+
+#endif   // INTEGRATE_ON_CELLS_HPP
diff --git a/src/language/utils/InterpolateItemArray.hpp b/src/language/utils/InterpolateItemArray.hpp
index c507c527ca9f1c59070410c09f802f524a3de083..17b72de6d62e0ac68062343010154257c43d3006 100644
--- a/src/language/utils/InterpolateItemArray.hpp
+++ b/src/language/utils/InterpolateItemArray.hpp
@@ -2,17 +2,15 @@
 #define INTERPOLATE_ITEM_ARRAY_HPP
 
 #include <language/utils/InterpolateItemValue.hpp>
-#include <language/utils/PugsFunctionAdapter.hpp>
 #include <mesh/ItemArray.hpp>
 #include <mesh/ItemType.hpp>
 
 template <typename T>
 class InterpolateItemArray;
 template <typename OutputType, typename InputType>
-class InterpolateItemArray<OutputType(InputType)> : public PugsFunctionAdapter<OutputType(InputType)>
+class InterpolateItemArray<OutputType(InputType)>
 {
   static constexpr size_t Dimension = OutputType::Dimension;
-  using Adapter                     = PugsFunctionAdapter<OutputType(InputType)>;
 
  public:
   template <ItemType item_type>
diff --git a/src/language/utils/InterpolateItemValue.hpp b/src/language/utils/InterpolateItemValue.hpp
index de12b563a19ded640f0b4bbae3184f383d90a2f3..dc3ef056b0693d4d32e695a33b7772aa11264b73 100644
--- a/src/language/utils/InterpolateItemValue.hpp
+++ b/src/language/utils/InterpolateItemValue.hpp
@@ -1,46 +1,23 @@
 #ifndef INTERPOLATE_ITEM_VALUE_HPP
 #define INTERPOLATE_ITEM_VALUE_HPP
 
-#include <language/utils/PugsFunctionAdapter.hpp>
+#include <language/utils/EvaluateAtPoints.hpp>
 #include <mesh/ItemType.hpp>
 #include <mesh/ItemValue.hpp>
 
 template <typename T>
 class InterpolateItemValue;
 template <typename OutputType, typename InputType>
-class InterpolateItemValue<OutputType(InputType)> : public PugsFunctionAdapter<OutputType(InputType)>
+class InterpolateItemValue<OutputType(InputType)>
 {
-  static constexpr size_t Dimension = OutputType::Dimension;
-  using Adapter                     = PugsFunctionAdapter<OutputType(InputType)>;
-
  public:
   template <ItemType item_type>
   PUGS_INLINE static ItemValue<OutputType, item_type>
   interpolate(const FunctionSymbolId& function_symbol_id, const ItemValue<const InputType, item_type>& position)
   {
-    auto& expression    = Adapter::getFunctionExpression(function_symbol_id);
-    auto convert_result = Adapter::getResultConverter(expression.m_data_type);
-
-    Array<ExecutionPolicy> context_list = Adapter::getContextList(expression);
-
-    using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space;
-    Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens;
     const IConnectivity& connectivity = *position.connectivity_ptr();
-
     ItemValue<OutputType, item_type> value(connectivity);
-    using ItemId = ItemIdT<item_type>;
-
-    parallel_for(connectivity.template numberOf<item_type>(), [=, &expression, &tokens](ItemId i) {
-      const int32_t t = tokens.acquire();
-
-      auto& execution_policy = context_list[t];
-
-      Adapter::convertArgs(execution_policy.currentContext(), position[i]);
-      auto result = expression.execute(execution_policy);
-      value[i]    = convert_result(std::move(result));
-
-      tokens.release(t);
-    });
+    EvaluateAtPoints<OutputType(const InputType)>::evaluateTo(function_symbol_id, position, value);
 
     return value;
   }
@@ -51,31 +28,15 @@ class InterpolateItemValue<OutputType(InputType)> : public PugsFunctionAdapter<O
               const ItemValue<const InputType, item_type>& position,
               const Array<const ItemIdT<item_type>>& list_of_items)
   {
-    auto& expression    = Adapter::getFunctionExpression(function_symbol_id);
-    auto convert_result = Adapter::getResultConverter(expression.m_data_type);
-
-    Array<ExecutionPolicy> context_list = Adapter::getContextList(expression);
-
-    using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space;
-    Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens;
-
-    Array<OutputType> value{list_of_items.size()};
+    Array<InputType> item_position{list_of_items.size()};
     using ItemId = ItemIdT<item_type>;
+    parallel_for(
+      list_of_items.size(), PUGS_LAMBDA(size_t i_item) {
+        ItemId item_id        = list_of_items[i_item];
+        item_position[i_item] = position[item_id];
+      });
 
-    parallel_for(list_of_items.size(), [=, &expression, &tokens](size_t i_item) {
-      ItemId item_id  = list_of_items[i_item];
-      const int32_t t = tokens.acquire();
-
-      auto& execution_policy = context_list[t];
-
-      Adapter::convertArgs(execution_policy.currentContext(), position[item_id]);
-      auto result   = expression.execute(execution_policy);
-      value[i_item] = convert_result(std::move(result));
-
-      tokens.release(t);
-    });
-
-    return value;
+    return EvaluateAtPoints<OutputType(const InputType)>::evaluate(function_symbol_id, item_position);
   }
 
   template <ItemType item_type>
diff --git a/src/language/utils/OStream.hpp b/src/language/utils/OStream.hpp
index d9410e25e35d2a3e9d5ac147911f6464d3b71eb7..bef02277ef8e07be09cc5731b33c6f5c3363e37d 100644
--- a/src/language/utils/OStream.hpp
+++ b/src/language/utils/OStream.hpp
@@ -23,6 +23,17 @@ class OStream
     return os;
   }
 
+  template <typename DataT>
+  friend std::shared_ptr<const OStream>
+  operator<<(const std::shared_ptr<const OStream>& os, const std::shared_ptr<const DataT>& t)
+  {
+    Assert(os.use_count() > 0, "non allocated stream");
+    if (os->m_ostream != nullptr) {
+      *os->m_ostream << *t;
+    }
+    return os;
+  }
+
   OStream(std::ostream& os) : m_ostream(&os) {}
   OStream() = default;
 
diff --git a/src/language/utils/PugsFunctionAdapter.hpp b/src/language/utils/PugsFunctionAdapter.hpp
index 3047652e3017fb2cd193b799a8edf454e3e4c22b..d39e0f4233df70709d3d765146447727f7541ae4 100644
--- a/src/language/utils/PugsFunctionAdapter.hpp
+++ b/src/language/utils/PugsFunctionAdapter.hpp
@@ -6,9 +6,9 @@
 #include <language/utils/ASTNodeDataType.hpp>
 #include <language/utils/ASTNodeDataTypeTraits.hpp>
 #include <language/utils/SymbolTable.hpp>
-#include <utils/Array.hpp>
 #include <utils/Exceptions.hpp>
 #include <utils/PugsMacros.hpp>
+#include <utils/SmallArray.hpp>
 
 #include <Kokkos_Core.hpp>
 
@@ -128,7 +128,7 @@ class PugsFunctionAdapter<OutputType(InputType...)>
   [[nodiscard]] PUGS_INLINE static auto
   getContextList(const ASTNode& expression)
   {
-    Array<ExecutionPolicy> context_list(Kokkos::DefaultExecutionSpace::impl_thread_pool_size());
+    SmallArray<ExecutionPolicy> context_list(Kokkos::DefaultExecutionSpace::impl_thread_pool_size());
     auto& context = expression.m_symbol_table->context();
 
     for (size_t i = 0; i < context_list.size(); ++i) {
diff --git a/src/language/utils/SymbolTable.hpp b/src/language/utils/SymbolTable.hpp
index ca674a1bb7e8053b1f2401b066d0a5c38c6f8151..9f7bc8129587b8acd04be2297d604871018cf0a2 100644
--- a/src/language/utils/SymbolTable.hpp
+++ b/src/language/utils/SymbolTable.hpp
@@ -98,6 +98,9 @@ class SymbolTable
       return os;
     }
 
+    Attributes& operator=(Attributes&&) = default;
+    Attributes& operator=(const Attributes&) = default;
+
     Attributes(const TAO_PEGTL_NAMESPACE::position& position, int32_t context_id)
       : m_position{position}, m_context_id{context_id}
     {}
diff --git a/src/main.cpp b/src/main.cpp
index cf382d69b491e85968d977e9023ac094d81c3bc2..7e03cdc8093c3b4034d24312b98a16a6f606b2d1 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,6 +1,7 @@
+#include <analysis/QuadratureManager.hpp>
 #include <language/PugsParser.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/SynchronizerManager.hpp>
 #include <utils/PugsUtils.hpp>
@@ -13,16 +14,18 @@ main(int argc, char* argv[])
 
   SynchronizerManager::create();
   RandomEngine::create();
+  QuadratureManager::create();
   MeshDataManager::create();
-  DiamondDualConnectivityManager::create();
-  DiamondDualMeshManager::create();
+  DualConnectivityManager::create();
+  DualMeshManager::create();
 
   parser(filename);
 
-  DiamondDualMeshManager::destroy();
-  DiamondDualConnectivityManager::destroy();
+  DualMeshManager::destroy();
+  DualConnectivityManager::destroy();
   MeshDataManager::destroy();
   RandomEngine::destroy();
+  QuadratureManager::destroy();
   SynchronizerManager::destroy();
 
   finalize();
diff --git a/src/mesh/CMakeLists.txt b/src/mesh/CMakeLists.txt
index c315ced7e506310c08c414d78b4df77aab646d4b..7634b113494dc081bed63091e722dc8ea4b757fa 100644
--- a/src/mesh/CMakeLists.txt
+++ b/src/mesh/CMakeLists.txt
@@ -8,21 +8,27 @@ add_library(
   ConnectivityComputer.cpp
   ConnectivityDispatcher.cpp
   DiamondDualConnectivityBuilder.cpp
-  DiamondDualConnectivityManager.cpp
   DiamondDualMeshBuilder.cpp
-  DiamondDualMeshManager.cpp
+  Dual1DConnectivityBuilder.cpp
+  Dual1DMeshBuilder.cpp
+  DualConnectivityManager.cpp
+  DualMeshManager.cpp
   GmshReader.cpp
   IConnectivity.cpp
   IMesh.cpp
   LogicalConnectivityBuilder.cpp
+  MedianDualConnectivityBuilder.cpp
+  MedianDualMeshBuilder.cpp
   MeshBuilderBase.cpp
   MeshDataManager.cpp
   MeshFaceBoundary.cpp
   MeshFlatFaceBoundary.cpp
   MeshFlatNodeBoundary.cpp
+  MeshRelaxer.cpp
   MeshLineNodeBoundary.cpp
   MeshNodeBoundary.cpp
   MeshRandomizer.cpp
+  MeshTransformer.cpp
   SynchronizerManager.cpp)
 
 # Additional dependencies
diff --git a/src/mesh/CartesianMeshBuilder.cpp b/src/mesh/CartesianMeshBuilder.cpp
index d6a4589cced3958ca7401a5a80d881d23fe87749..c0cf86a73886b59e56f417fbf1731c78e00d15d5 100644
--- a/src/mesh/CartesianMeshBuilder.cpp
+++ b/src/mesh/CartesianMeshBuilder.cpp
@@ -23,7 +23,7 @@ CartesianMeshBuilder::_getNodeCoordinates(const TinyVector<1>& a,
                                           const TinyVector<1, uint64_t>& cell_size,
                                           const IConnectivity& connectivity) const
 {
-  const double h = (b[0] - a[0]) / cell_size[0];
+  const TinyVector<1> h{(b[0] - a[0]) / cell_size[0]};
   NodeValue<TinyVector<1>> xr(connectivity);
   parallel_for(
     connectivity.numberOfNodes(), PUGS_LAMBDA(NodeId r) { xr[r] = a + r * h; });
@@ -125,14 +125,14 @@ CartesianMeshBuilder::CartesianMeshBuilder(const TinyVector<Dimension>& a,
                                            const TinyVector<Dimension>& b,
                                            const TinyVector<Dimension, uint64_t>& size)
 {
-  if (parallel::rank() == 0) {
-    TinyVector lenght = b - a;
-    for (size_t i = 0; i < Dimension; ++i) {
-      if (lenght[i] == 0) {
-        throw NormalError("invalid box definition corners share a component");
-      }
+  TinyVector lenght = b - a;
+  for (size_t i = 0; i < Dimension; ++i) {
+    if (lenght[i] == 0) {
+      throw NormalError("invalid box definition corners share a component");
     }
+  }
 
+  if (parallel::rank() == 0) {
     TinyVector<Dimension> corner0 = a;
     TinyVector<Dimension> corner1 = b;
 
diff --git a/src/mesh/CellType.hpp b/src/mesh/CellType.hpp
index 3a0f01520bba191ff4535dfb407ddda55edab3c9..335cfc6471b9baf41af1642c40c4325938ce53cb 100644
--- a/src/mesh/CellType.hpp
+++ b/src/mesh/CellType.hpp
@@ -11,6 +11,7 @@ enum class CellType : unsigned short
 
   Triangle,
   Quadrangle,
+  Polygon,
 
   Diamond,
   Hexahedron,
@@ -28,6 +29,8 @@ name(CellType cell_type)
     return "line";
   case CellType::Triangle:
     return "triangle";
+  case CellType::Polygon:
+    return "polygon";
   case CellType::Quadrangle:
     return "quadrangle";
   case CellType::Diamond:
diff --git a/src/mesh/Connectivity.hpp b/src/mesh/Connectivity.hpp
index 20caa238cb25019df43eccd6c05c3a65192a0a94..8a1f82dfce3b331f4bff0bc7d28be0e7abfd3498 100644
--- a/src/mesh/Connectivity.hpp
+++ b/src/mesh/Connectivity.hpp
@@ -314,6 +314,7 @@ class Connectivity final : public IConnectivity
   auto
   faceToEdgeMatrix() const
   {
+    static_assert(Dimension > 2, "face to edge matrix makes sense in dimension > 2");
     return this->template getItemToItemMatrix<ItemType::face, ItemType::edge>();
   }
 
@@ -321,6 +322,7 @@ class Connectivity final : public IConnectivity
   auto
   faceToNodeMatrix() const
   {
+    static_assert(Dimension > 1, "face to node matrix makes sense in dimension > 1");
     return this->template getItemToItemMatrix<ItemType::face, ItemType::node>();
   }
 
@@ -335,6 +337,7 @@ class Connectivity final : public IConnectivity
   auto
   edgeToFaceMatrix() const
   {
+    static_assert(Dimension > 2, "edge to face matrix makes sense in dimension > 2");
     return this->template getItemToItemMatrix<ItemType::edge, ItemType::face>();
   }
 
@@ -342,6 +345,7 @@ class Connectivity final : public IConnectivity
   auto
   edgeToNodeMatrix() const
   {
+    static_assert(Dimension > 1, "edge to node matrix makes sense in dimension > 1");
     return this->template getItemToItemMatrix<ItemType::edge, ItemType::node>();
   }
 
@@ -356,6 +360,7 @@ class Connectivity final : public IConnectivity
   auto
   nodeToFaceMatrix() const
   {
+    static_assert(Dimension > 1, "node to face matrix makes sense in dimension > 1");
     return this->template getItemToItemMatrix<ItemType::node, ItemType::face>();
   }
 
@@ -363,6 +368,7 @@ class Connectivity final : public IConnectivity
   auto
   nodeToEdgeMatrix() const
   {
+    static_assert(Dimension > 1, "node to edge matrix makes sense in dimension > 1");
     return this->template getItemToItemMatrix<ItemType::node, ItemType::edge>();
   }
 
diff --git a/src/mesh/ConnectivityBuilderBase.cpp b/src/mesh/ConnectivityBuilderBase.cpp
index 60c38bfb84fb975c0e2cdc3b07d1d97e39eb0021..55c56b3a4b78f28ecdf8613d04d4d0348b9e5c4a 100644
--- a/src/mesh/ConnectivityBuilderBase.cpp
+++ b/src/mesh/ConnectivityBuilderBase.cpp
@@ -61,6 +61,14 @@ ConnectivityBuilderBase::_computeCellFaceAndFaceNodeConnectivities(ConnectivityD
         face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
         break;
       }
+      case CellType::Polygon: {
+        cell_nb_faces[j] = cell_nodes.size();
+        for (size_t i = 0; i < cell_nodes.size(); ++i) {
+          Face f({cell_nodes[i], cell_nodes[(i + 1) % cell_nodes.size()]}, node_number_vector);
+          face_cells_map[f].emplace_back(std::make_tuple(j, i, f.reversed()));
+        }
+        break;
+      }
       default: {
         std::ostringstream error_msg;
         error_msg << name(descriptor.cell_type_vector[j]) << ": unexpected cell type in dimension 2";
@@ -116,10 +124,36 @@ ConnectivityBuilderBase::_computeCellFaceAndFaceNodeConnectivities(ConnectivityD
         face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
         break;
       }
+      case CellType::Prism: {
+        // face 0
+        Face f0({cell_nodes[2], cell_nodes[1], cell_nodes[0]}, node_number_vector);
+        face_cells_map[f0].emplace_back(std::make_tuple(j, 0, f0.reversed()));
+
+        // face 1
+        Face f1({cell_nodes[3], cell_nodes[4], cell_nodes[5]}, node_number_vector);
+        face_cells_map[f1].emplace_back(std::make_tuple(j, 1, f1.reversed()));
+
+        // face 2
+        Face f2({cell_nodes[1], cell_nodes[2], cell_nodes[5], cell_nodes[4]}, node_number_vector);
+        face_cells_map[f2].emplace_back(std::make_tuple(j, 2, f2.reversed()));
+
+        // face 3
+        Face f3({cell_nodes[0], cell_nodes[1], cell_nodes[4], cell_nodes[3]}, node_number_vector);
+        face_cells_map[f3].emplace_back(std::make_tuple(j, 3, f3.reversed()));
+
+        // face 4
+        Face f4({cell_nodes[2], cell_nodes[0], cell_nodes[3], cell_nodes[5]}, node_number_vector);
+        face_cells_map[f4].emplace_back(std::make_tuple(j, 4, f4.reversed()));
+
+        cell_nb_faces[j] = 5;
+        break;
+      }
       case CellType::Pyramid: {
         cell_nb_faces[j] = cell_nodes.size();
-        std::vector<unsigned int> base_nodes;
-        std::copy_n(cell_nodes.begin(), cell_nodes.size() - 1, std::back_inserter(base_nodes));
+        std::vector<unsigned int> base_nodes(cell_nodes.size() - 1);
+        for (size_t i = 0; i < base_nodes.size(); ++i) {
+          base_nodes[base_nodes.size() - 1 - i] = cell_nodes[i];
+        }
 
         // base face
         {
@@ -332,6 +366,22 @@ ConnectivityBuilderBase::_computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities(Co
         descriptor.cell_to_edge_vector.emplace_back(cell_edge_vector);
         break;
       }
+      case CellType::Prism: {
+        constexpr int local_edge[12][2] = {{0, 1}, {1, 2}, {2, 0}, {3, 4}, {4, 5}, {5, 3}, {0, 3}, {1, 4}, {2, 5}};
+        std::vector<unsigned int> cell_edge_vector;
+        cell_edge_vector.reserve(9);
+        for (int i_edge = 0; i_edge < 9; ++i_edge) {
+          const auto e = local_edge[i_edge];
+          Edge edge{{cell_nodes[e[0]], cell_nodes[e[1]]}, node_number_vector};
+          auto i = edge_id_map.find(edge);
+          if (i == edge_id_map.end()) {
+            throw NormalError("could not find this edge");
+          }
+          cell_edge_vector.push_back(i->second);
+        }
+        descriptor.cell_to_edge_vector.emplace_back(cell_edge_vector);
+        break;
+      }
       case CellType::Pyramid: {
         const size_t number_of_edges = 2 * cell_nodes.size();
         std::vector<unsigned int> base_nodes;
diff --git a/src/mesh/DiamondDualConnectivityBuilder.cpp b/src/mesh/DiamondDualConnectivityBuilder.cpp
index 31d54be8997e9571958399f17c44dbcede1c35e4..38e62b737d772d2db7d32a2baa4134c9af94f6e9 100644
--- a/src/mesh/DiamondDualConnectivityBuilder.cpp
+++ b/src/mesh/DiamondDualConnectivityBuilder.cpp
@@ -3,12 +3,11 @@
 #include <mesh/Connectivity.hpp>
 #include <mesh/ConnectivityDescriptor.hpp>
 #include <mesh/ConnectivityDispatcher.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
 #include <mesh/ItemValueUtils.hpp>
 #include <mesh/Mesh.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <mesh/RefId.hpp>
 #include <utils/Array.hpp>
-#include <utils/ArrayUtils.hpp>
 #include <utils/Messenger.hpp>
 
 #include <vector>
@@ -18,6 +17,8 @@ void
 DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor(const Connectivity<Dimension>& primal_connectivity,
                                                                     ConnectivityDescriptor& diamond_descriptor)
 {
+  static_assert((Dimension == 2) or (Dimension == 3), "invalid connectivity dimension");
+
   const size_t primal_number_of_nodes = primal_connectivity.numberOfNodes();
   const size_t primal_number_of_faces = primal_connectivity.numberOfFaces();
   const size_t primal_number_of_cells = primal_connectivity.numberOfCells();
@@ -73,35 +74,30 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor(const Connec
     diamond_descriptor.cell_number_vector[dual_cell_id] = primal_face_number[primal_face_id];
   });
 
-  if constexpr (Dimension == 3) {
-    const size_t number_of_edges = diamond_descriptor.edge_to_node_vector.size();
-    diamond_descriptor.edge_number_vector.resize(number_of_edges);
-    for (size_t i_edge = 0; i_edge < number_of_edges; ++i_edge) {
-      diamond_descriptor.edge_number_vector[i_edge] = i_edge;
-    }
-    if (parallel::size() > 1) {
-      throw NotImplementedError("parallel edge numbering is undefined");
-    }
-  }
-
   diamond_descriptor.cell_type_vector.resize(diamond_number_of_cells);
 
   const auto& primal_face_to_cell_matrix = primal_connectivity.faceToCellMatrix();
+  const auto& primal_face_to_node_matrix = primal_connectivity.faceToNodeMatrix();
 
+  static_assert(Dimension > 1);
   parallel_for(primal_number_of_faces, [&](FaceId face_id) {
     const size_t i_cell               = face_id;
     const auto& primal_face_cell_list = primal_face_to_cell_matrix[face_id];
 
-    if (primal_face_cell_list.size() == 1) {
-      diamond_descriptor.cell_type_vector[i_cell] = CellType::Triangle;
-    } else {
-      Assert(primal_face_cell_list.size() == 2);
-      diamond_descriptor.cell_type_vector[i_cell] = CellType::Quadrangle;
-    }
-
-    if constexpr (Dimension == 3) {
+    if constexpr (Dimension == 2) {
+      if (primal_face_cell_list.size() == 1) {
+        diamond_descriptor.cell_type_vector[i_cell] = CellType::Triangle;
+      } else {
+        Assert(primal_face_cell_list.size() == 2);
+        diamond_descriptor.cell_type_vector[i_cell] = CellType::Quadrangle;
+      }
+    } else if constexpr (Dimension == 3) {
       if (primal_face_cell_list.size() == 1) {
-        diamond_descriptor.cell_type_vector[i_cell] = CellType::Pyramid;
+        if (primal_face_to_node_matrix[face_id].size() == 3) {
+          diamond_descriptor.cell_type_vector[i_cell] = CellType::Tetrahedron;
+        } else {
+          diamond_descriptor.cell_type_vector[i_cell] = CellType::Pyramid;
+        }
       } else {
         Assert(primal_face_cell_list.size() == 2);
         diamond_descriptor.cell_type_vector[i_cell] = CellType::Diamond;
@@ -111,7 +107,6 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor(const Connec
 
   diamond_descriptor.cell_to_node_vector.resize(diamond_number_of_cells);
 
-  const auto& primal_face_to_node_matrix              = primal_connectivity.faceToNodeMatrix();
   const auto& primal_face_local_number_in_their_cells = primal_connectivity.faceLocalNumbersInTheirCells();
   const auto& cell_face_is_reversed                   = primal_connectivity.cellFaceIsReversed();
   parallel_for(primal_number_of_faces, [&](FaceId face_id) {
@@ -130,12 +125,17 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor(const Connec
       diamond_descriptor.cell_to_node_vector[i_diamond_cell][primal_face_node_list.size()] =
         primal_number_of_nodes + cell_id;
 
-      if (cell_face_is_reversed(cell_id, i_face_in_cell)) {
-        if constexpr (Dimension == 2) {
+      if constexpr (Dimension == 2) {
+        if (cell_face_is_reversed(cell_id, i_face_in_cell)) {
           std::swap(diamond_descriptor.cell_to_node_vector[i_diamond_cell][0],
                     diamond_descriptor.cell_to_node_vector[i_diamond_cell][1]);
-
-        } else {
+        }
+      } else {
+        if (not cell_face_is_reversed(cell_id, i_face_in_cell)) {
+          // In 3D the basis of the pyramid is described in the
+          // indirect way IF the face is not reversed. In other words
+          // the "topological normal" must point to the "top" of the
+          // pyramid.
           for (size_t i_node = 0; i_node < primal_face_node_list.size() / 2; ++i_node) {
             std::swap(diamond_descriptor.cell_to_node_vector[i_diamond_cell][i_node],
                       diamond_descriptor
@@ -179,93 +179,11 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor(const Connec
   });
 }
 
-template <>
-void
-DiamondDualConnectivityBuilder::_buildDiamondConnectivityDescriptor<1>(const Connectivity<1>& primal_connectivity,
-                                                                       ConnectivityDescriptor& diamond_descriptor)
-{
-  const size_t primal_number_of_nodes = primal_connectivity.numberOfNodes();
-  const size_t primal_number_of_faces = primal_connectivity.numberOfFaces();
-  const size_t primal_number_of_cells = primal_connectivity.numberOfCells();
-
-  const size_t diamond_number_of_nodes = 2 * primal_number_of_nodes - primal_number_of_cells;
-
-  const size_t diamond_number_of_cells = primal_number_of_faces;
-  const size_t number_of_kept_nodes    = 2 * (diamond_number_of_nodes - diamond_number_of_cells);
-
-  const auto& primal_node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
-  size_t next_kept_node_id               = 0;
-
-  diamond_descriptor.node_number_vector.resize(diamond_number_of_nodes);
-
-  const auto& primal_node_number = primal_connectivity.nodeNumber();
-
-  for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
-    const auto& primal_node_cell_list = primal_node_to_cell_matrix[primal_node_id];
-    if (primal_node_cell_list.size() == 1) {
-      diamond_descriptor.node_number_vector[next_kept_node_id++] = primal_node_number[primal_node_id];
-    }
-  }
-
-  const size_t primal_number_of_kept_nodes = next_kept_node_id;
-
-  const auto& primal_cell_number = primal_connectivity.cellNumber();
-
-  const size_t cell_number_shift = max(primal_node_number) + 1;
-
-  for (CellId primal_cell_id = 0; primal_cell_id < primal_number_of_cells; ++primal_cell_id) {
-    diamond_descriptor.node_number_vector[primal_number_of_kept_nodes + primal_cell_id] =
-      primal_cell_number[primal_cell_id] + cell_number_shift;
-  }
-
-  if (number_of_kept_nodes != next_kept_node_id) {
-    throw UnexpectedError("unexpected number of kept node" + std::to_string(next_kept_node_id) +
-                          " != " + std::to_string(number_of_kept_nodes));
-  }
-
-  diamond_descriptor.cell_number_vector.resize(diamond_number_of_cells);
-
-  for (NodeId primal_node_id = 0; primal_node_id < primal_number_of_nodes; ++primal_node_id) {
-    diamond_descriptor.cell_number_vector[primal_node_id] = primal_node_number[primal_node_id];
-  }
-
-  diamond_descriptor.cell_type_vector.resize(diamond_number_of_cells);
-
-  for (NodeId node_id = 0; node_id < primal_connectivity.numberOfNodes(); ++node_id) {
-    const size_t i_cell = node_id;
-
-    diamond_descriptor.cell_type_vector[i_cell] = CellType::Line;
-  }
-
-  diamond_descriptor.cell_to_node_vector.resize(diamond_number_of_cells);
-
-  const auto& primal_node_local_number_in_their_cells = primal_connectivity.nodeLocalNumbersInTheirCells();
-
-  {
-    size_t next_kept_node_id = 0;
-    for (NodeId i_node = 0; i_node < primal_connectivity.numberOfNodes(); ++i_node) {
-      const size_t& i_diamond_cell      = i_node;
-      const auto& primal_node_cell_list = primal_node_to_cell_matrix[i_node];
-      diamond_descriptor.cell_to_node_vector[i_diamond_cell].resize(2);
-      if (primal_node_cell_list.size() == 1) {
-        const auto i_node_in_cell = primal_node_local_number_in_their_cells(i_node, 0);
-
-        diamond_descriptor.cell_to_node_vector[i_diamond_cell][i_node_in_cell] = next_kept_node_id++;
-        diamond_descriptor.cell_to_node_vector[i_diamond_cell][1 - i_node_in_cell] =
-          number_of_kept_nodes + primal_node_cell_list[0];
-      } else {
-        diamond_descriptor.cell_to_node_vector[i_diamond_cell][0] = number_of_kept_nodes + primal_node_cell_list[0];
-        diamond_descriptor.cell_to_node_vector[i_diamond_cell][1] = number_of_kept_nodes + primal_node_cell_list[1];
-      }
-    }
-  }
-}
-
 template <size_t Dimension>
 void
 DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivity& i_primal_connectivity)
 {
-  static_assert(Dimension <= 3, "invalid connectivity dimension");
+  static_assert((Dimension == 2) or (Dimension == 3), "invalid connectivity dimension");
   using ConnectivityType = Connectivity<Dimension>;
 
   const ConnectivityType& primal_connectivity = dynamic_cast<const ConnectivityType&>(i_primal_connectivity);
@@ -274,11 +192,9 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit
 
   this->_buildDiamondConnectivityDescriptor(primal_connectivity, diamond_descriptor);
 
-  if constexpr (Dimension > 1) {
-    ConnectivityBuilderBase::_computeCellFaceAndFaceNodeConnectivities<Dimension>(diamond_descriptor);
-    if constexpr (Dimension > 2) {
-      ConnectivityBuilderBase::_computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities<Dimension>(diamond_descriptor);
-    }
+  ConnectivityBuilderBase::_computeCellFaceAndFaceNodeConnectivities<Dimension>(diamond_descriptor);
+  if constexpr (Dimension == 3) {
+    ConnectivityBuilderBase::_computeFaceEdgeAndEdgeNodeAndCellEdgeConnectivities<Dimension>(diamond_descriptor);
   }
 
   {
@@ -319,7 +235,7 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit
     }
   }
 
-  if constexpr (Dimension > 1) {
+  {
     const auto& primal_face_to_node_matrix = primal_connectivity.faceToNodeMatrix();
 
     using Face = ConnectivityFace<Dimension>;
@@ -372,7 +288,7 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit
     }
   }
 
-  if constexpr (Dimension > 2) {
+  if constexpr (Dimension == 3) {
     const auto& primal_edge_to_node_matrix = primal_connectivity.edgeToNodeMatrix();
     using Edge                             = ConnectivityFace<2>;
 
@@ -385,6 +301,19 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit
       return edge_to_id_map;
     }();
 
+    {
+      const size_t number_of_edges = diamond_descriptor.edge_to_node_vector.size();
+      diamond_descriptor.edge_number_vector.resize(number_of_edges);
+      for (size_t i_edge = 0; i_edge < number_of_edges; ++i_edge) {
+        diamond_descriptor.edge_number_vector[i_edge] = i_edge;
+      }
+      // LCOV_EXCL_START
+      if (parallel::size() > 1) {
+        throw NotImplementedError("parallel edge numbering is undefined");
+      }
+      // LCOV_EXCL_STOP
+    }
+
     for (size_t i_edge_list = 0; i_edge_list < primal_connectivity.template numberOfRefItemList<ItemType::edge>();
          ++i_edge_list) {
       const auto& primal_ref_edge_list = primal_connectivity.template refItemList<ItemType::edge>(i_edge_list);
@@ -428,21 +357,7 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit
 
   diamond_descriptor.node_owner_vector.resize(diamond_descriptor.node_number_vector.size());
 
-  if constexpr (Dimension == 1) {
-    const auto& node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
-    const auto& primal_node_owner   = primal_connectivity.nodeOwner();
-    size_t next_kept_node_id        = 0;
-    for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
-      if (node_to_cell_matrix[primal_node_id].size() == 1) {
-        diamond_descriptor.node_owner_vector[next_kept_node_id++] = primal_node_owner[primal_node_id];
-      }
-    }
-    const size_t number_of_kept_nodes = next_kept_node_id;
-    const auto& primal_cell_owner     = primal_connectivity.cellOwner();
-    for (CellId primal_cell_id = 0; primal_cell_id < primal_number_of_cells; ++primal_cell_id) {
-      diamond_descriptor.node_owner_vector[number_of_kept_nodes + primal_cell_id] = primal_cell_owner[primal_cell_id];
-    }
-  } else {
+  {
     const auto& primal_node_owner = primal_connectivity.nodeOwner();
     for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
       diamond_descriptor.node_owner_vector[primal_node_id] = primal_node_owner[primal_node_id];
@@ -453,13 +368,7 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit
     }
   }
 
-  if constexpr (Dimension == 1) {
-    diamond_descriptor.cell_owner_vector.resize(diamond_descriptor.cell_number_vector.size());
-    const auto& primal_node_owner = primal_connectivity.nodeOwner();
-    for (NodeId primal_node_id = 0; primal_node_id < primal_number_of_nodes; ++primal_node_id) {
-      diamond_descriptor.cell_owner_vector[primal_node_id] = primal_node_owner[primal_node_id];
-    }
-  } else {
+  {
     diamond_descriptor.cell_owner_vector.resize(diamond_descriptor.cell_number_vector.size());
     const size_t primal_number_of_faces = primal_connectivity.numberOfFaces();
     const auto& primal_face_owner       = primal_connectivity.faceOwner();
@@ -506,59 +415,23 @@ DiamondDualConnectivityBuilder::_buildDiamondConnectivityFrom(const IConnectivit
 
   m_connectivity = ConnectivityType::build(diamond_descriptor);
 
-  if constexpr (Dimension == 1) {
-    const auto& node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
-
-    NodeId dual_node_id            = 0;
-    m_primal_node_to_dual_node_map = [&]() {
-      std::vector<std::pair<NodeId, NodeId>> primal_node_to_dual_node_vector;
-      for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
-        if (node_to_cell_matrix[primal_node_id].size() == 1) {
-          primal_node_to_dual_node_vector.push_back(std::make_pair(primal_node_id, dual_node_id++));
-        }
-      }
-      return convert_to_array(primal_node_to_dual_node_vector);
-    }();
-
-    m_primal_cell_to_dual_node_map = [&]() {
-      CellIdToNodeIdMap primal_cell_to_dual_node_map{primal_number_of_cells};
-      for (CellId primal_cell_id = 0; primal_cell_id < primal_cell_to_dual_node_map.size(); ++primal_cell_id) {
-        primal_cell_to_dual_node_map[primal_cell_id] = std::make_pair(primal_cell_id, dual_node_id++);
-      }
-      return primal_cell_to_dual_node_map;
-    }();
-
-    m_primal_face_to_dual_cell_map = [&]() {
-      FaceIdToCellIdMap primal_face_to_dual_cell_map{primal_connectivity.numberOfFaces()};
-      for (size_t id = 0; id < primal_face_to_dual_cell_map.size(); ++id) {
-        const CellId dual_cell_id   = id;
-        const FaceId primal_face_id = id;
-
-        primal_face_to_dual_cell_map[id] = std::make_pair(primal_face_id, dual_cell_id);
-      }
-      return primal_face_to_dual_cell_map;
-    }();
-  }
-
   m_mapper =
-    std::make_shared<ConnectivityToDiamondDualConnectivityDataMapper<Dimension>>(primal_connectivity,
-                                                                                 dynamic_cast<const ConnectivityType&>(
-                                                                                   *m_connectivity),
-                                                                                 m_primal_node_to_dual_node_map,
-                                                                                 m_primal_cell_to_dual_node_map,
-                                                                                 m_primal_face_to_dual_cell_map);
+    std::make_shared<PrimalToDiamondDualConnectivityDataMapper<Dimension>>(primal_connectivity,
+                                                                           dynamic_cast<const ConnectivityType&>(
+                                                                             *m_connectivity),
+                                                                           m_primal_node_to_dual_node_map,
+                                                                           m_primal_cell_to_dual_node_map,
+                                                                           m_primal_face_to_dual_cell_map);
 }
 
 DiamondDualConnectivityBuilder::DiamondDualConnectivityBuilder(const IConnectivity& connectivity)
 {
+  // LCOV_EXCL_START
   if (parallel::size() > 1) {
     throw NotImplementedError("Construction of diamond dual mesh is not implemented in parallel");
   }
+  // LCOV_EXCL_STOP
   switch (connectivity.dimension()) {
-  case 1: {
-    this->_buildDiamondConnectivityFrom<1>(connectivity);
-    break;
-  }
   case 2: {
     this->_buildDiamondConnectivityFrom<2>(connectivity);
     break;
@@ -567,8 +440,10 @@ DiamondDualConnectivityBuilder::DiamondDualConnectivityBuilder(const IConnectivi
     this->_buildDiamondConnectivityFrom<3>(connectivity);
     break;
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid connectivity dimension: " + std::to_string(connectivity.dimension()));
   }
+    // LCOV_EXCL_STOP
   }
 }
diff --git a/src/mesh/DiamondDualConnectivityBuilder.hpp b/src/mesh/DiamondDualConnectivityBuilder.hpp
index 36b120811ad6478af90b89112740d272014b2e7a..da8b6c3d5a2e08895fe518bfd56a4f178a33ad46 100644
--- a/src/mesh/DiamondDualConnectivityBuilder.hpp
+++ b/src/mesh/DiamondDualConnectivityBuilder.hpp
@@ -2,6 +2,7 @@
 #define DIAMOND_DUAL_CONNECTIVITY_BUILDER_HPP
 
 #include <mesh/ConnectivityBuilderBase.hpp>
+#include <mesh/IPrimalToDualConnectivityDataMapper.hpp>
 #include <mesh/ItemIdToItemIdMap.hpp>
 
 #include <memory>
@@ -10,8 +11,6 @@ template <size_t>
 class Connectivity;
 class ConnectivityDescriptor;
 
-class IConnectivityToDiamondDualConnectivityDataMapper;
-
 class DiamondDualConnectivityBuilder : public ConnectivityBuilderBase
 {
  private:
@@ -19,7 +18,7 @@ class DiamondDualConnectivityBuilder : public ConnectivityBuilderBase
   CellIdToNodeIdMap m_primal_cell_to_dual_node_map;
   FaceIdToCellIdMap m_primal_face_to_dual_cell_map;
 
-  std::shared_ptr<IConnectivityToDiamondDualConnectivityDataMapper> m_mapper;
+  std::shared_ptr<IPrimalToDualConnectivityDataMapper> m_mapper;
 
   template <size_t Dimension>
   void _buildDiamondConnectivityDescriptor(const Connectivity<Dimension>&, ConnectivityDescriptor&);
@@ -27,11 +26,11 @@ class DiamondDualConnectivityBuilder : public ConnectivityBuilderBase
   template <size_t Dimension>
   void _buildDiamondConnectivityFrom(const IConnectivity&);
 
-  friend class DiamondDualConnectivityManager;
+  friend class DualConnectivityManager;
   DiamondDualConnectivityBuilder(const IConnectivity&);
 
  public:
-  std::shared_ptr<IConnectivityToDiamondDualConnectivityDataMapper>
+  std::shared_ptr<IPrimalToDualConnectivityDataMapper>
   mapper() const
   {
     return m_mapper;
diff --git a/src/mesh/DiamondDualConnectivityManager.cpp b/src/mesh/DiamondDualConnectivityManager.cpp
deleted file mode 100644
index 9459010b69e89acaf3f247866d2f6e81ef00cd81..0000000000000000000000000000000000000000
--- a/src/mesh/DiamondDualConnectivityManager.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-#include <utils/PugsAssert.hpp>
-
-#include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityBuilder.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <utils/Exceptions.hpp>
-
-#include <sstream>
-
-DiamondDualConnectivityManager* DiamondDualConnectivityManager::m_instance{nullptr};
-
-void
-DiamondDualConnectivityManager::create()
-{
-  Assert(m_instance == nullptr, "DiamondDualConnectivityManager is already created");
-  m_instance = new DiamondDualConnectivityManager;
-}
-
-void
-DiamondDualConnectivityManager::destroy()
-{
-  Assert(m_instance != nullptr, "DiamondDualConnectivityManager was not created!");
-
-  if (m_instance->m_connectivity_to_diamond_dual_connectivity_info_map.size() > 0) {
-    std::stringstream error;
-    error << ": some connectivities are still registered\n";
-    for (const auto& [connectivity, diamond_dual_connectivity_info] :
-         m_instance->m_connectivity_to_diamond_dual_connectivity_info_map) {
-      error << " - connectivity " << rang::fgB::magenta << connectivity << rang::style::reset << '\n';
-    }
-    throw UnexpectedError(error.str());
-  }
-  delete m_instance;
-  m_instance = nullptr;
-}
-
-void
-DiamondDualConnectivityManager::deleteConnectivity(const IConnectivity* p_connectivity)
-{
-  m_connectivity_to_diamond_dual_connectivity_info_map.erase(p_connectivity);
-}
-
-DiamondDualConnectivityManager::DiamondDualConnectivityInfo
-DiamondDualConnectivityManager::_getDiamondDualConnectivityInfo(const IConnectivity& connectivity)
-{
-  const IConnectivity* p_connectivity = &connectivity;
-
-  if (auto i_connectivity = m_connectivity_to_diamond_dual_connectivity_info_map.find(p_connectivity);
-      i_connectivity != m_connectivity_to_diamond_dual_connectivity_info_map.end()) {
-    return i_connectivity->second;
-  } else {
-    DiamondDualConnectivityBuilder builder{connectivity};
-
-    DiamondDualConnectivityInfo connectivity_info{builder.connectivity(), builder.mapper()};
-
-    m_connectivity_to_diamond_dual_connectivity_info_map[p_connectivity] = connectivity_info;
-
-    return connectivity_info;
-  }
-}
-
-template <size_t Dimension>
-std::shared_ptr<const Connectivity<Dimension>>
-DiamondDualConnectivityManager::getDiamondDualConnectivity(const Connectivity<Dimension>& connectivity)
-{
-  return std::dynamic_pointer_cast<const Connectivity<Dimension>>(
-    this->_getDiamondDualConnectivityInfo(connectivity).diamondDualConnectivity());
-}
-
-template <size_t Dimension>
-std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<Dimension>>
-DiamondDualConnectivityManager::getConnectivityToDiamondDualConnectivityDataMapper(
-  const Connectivity<Dimension>& connectivity)
-{
-  return std::dynamic_pointer_cast<const ConnectivityToDiamondDualConnectivityDataMapper<Dimension>>(
-    this->_getDiamondDualConnectivityInfo(connectivity).connectivityToDiamondDualConnectivityDataMapper());
-}
-
-template std::shared_ptr<const Connectivity<1>> DiamondDualConnectivityManager::getDiamondDualConnectivity(
-  const Connectivity<1>& connectivity);
-
-template std::shared_ptr<const Connectivity<2>> DiamondDualConnectivityManager::getDiamondDualConnectivity(
-  const Connectivity<2>& connectivity);
-
-template std::shared_ptr<const Connectivity<3>> DiamondDualConnectivityManager::getDiamondDualConnectivity(
-  const Connectivity<3>& connectivity);
-
-template std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<1>>
-DiamondDualConnectivityManager::getConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<1>&);
-
-template std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<2>>
-DiamondDualConnectivityManager::getConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<2>&);
-
-template std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<3>>
-DiamondDualConnectivityManager::getConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<3>&);
diff --git a/src/mesh/DiamondDualConnectivityManager.hpp b/src/mesh/DiamondDualConnectivityManager.hpp
deleted file mode 100644
index 33dfbaf4cf7cc8e59eebfc095624d43c5bf038b3..0000000000000000000000000000000000000000
--- a/src/mesh/DiamondDualConnectivityManager.hpp
+++ /dev/null
@@ -1,94 +0,0 @@
-#ifndef DIAMOND_DUAL_CONNECTIVITY_MANAGER_HPP
-#define DIAMOND_DUAL_CONNECTIVITY_MANAGER_HPP
-
-#include <mesh/IConnectivity.hpp>
-
-#include <memory>
-#include <unordered_map>
-
-template <size_t Dimension>
-class Connectivity;
-
-class IConnectivityToDiamondDualConnectivityDataMapper;
-
-template <size_t Dimension>
-class ConnectivityToDiamondDualConnectivityDataMapper;
-
-class DiamondDualConnectivityManager
-{
- private:
-  class DiamondDualConnectivityInfo
-  {
-   private:
-    std::shared_ptr<const IConnectivity> m_diamond_dual_connectivity;
-    std::shared_ptr<const IConnectivityToDiamondDualConnectivityDataMapper>
-      m_connectivity_to_diamond_dual_connectivity_data_mapper;
-
-   public:
-    PUGS_INLINE
-    std::shared_ptr<const IConnectivity>
-    diamondDualConnectivity() const
-    {
-      return m_diamond_dual_connectivity;
-    }
-
-    PUGS_INLINE
-    std::shared_ptr<const IConnectivityToDiamondDualConnectivityDataMapper>
-    connectivityToDiamondDualConnectivityDataMapper()
-    {
-      return m_connectivity_to_diamond_dual_connectivity_data_mapper;
-    }
-
-    DiamondDualConnectivityInfo& operator=(const DiamondDualConnectivityInfo&) = default;
-    DiamondDualConnectivityInfo& operator=(DiamondDualConnectivityInfo&&) = default;
-
-    DiamondDualConnectivityInfo()                                   = default;
-    DiamondDualConnectivityInfo(const DiamondDualConnectivityInfo&) = default;
-    DiamondDualConnectivityInfo(DiamondDualConnectivityInfo&&)      = default;
-
-    DiamondDualConnectivityInfo(const std::shared_ptr<const IConnectivity>& diamond_dual_connectivity,
-                                const std::shared_ptr<const IConnectivityToDiamondDualConnectivityDataMapper>&
-                                  connectivity_to_diamond_dual_connectivity_data_mapper)
-      : m_diamond_dual_connectivity{diamond_dual_connectivity},
-        m_connectivity_to_diamond_dual_connectivity_data_mapper{connectivity_to_diamond_dual_connectivity_data_mapper}
-    {}
-
-    ~DiamondDualConnectivityInfo() = default;
-  };
-
-  DiamondDualConnectivityInfo _getDiamondDualConnectivityInfo(const IConnectivity& connectivity);
-
-  std::unordered_map<const IConnectivity*, DiamondDualConnectivityInfo>
-    m_connectivity_to_diamond_dual_connectivity_info_map;
-
-  static DiamondDualConnectivityManager* m_instance;
-
-  DiamondDualConnectivityManager(const DiamondDualConnectivityManager&) = delete;
-  DiamondDualConnectivityManager(DiamondDualConnectivityManager&&)      = delete;
-
-  DiamondDualConnectivityManager()  = default;
-  ~DiamondDualConnectivityManager() = default;
-
- public:
-  static void create();
-  static void destroy();
-
-  PUGS_INLINE
-  static DiamondDualConnectivityManager&
-  instance()
-  {
-    Assert(m_instance != nullptr, "DiamondDualConnectivityManager was not created!");
-    return *m_instance;
-  }
-
-  void deleteConnectivity(const IConnectivity*);
-
-  template <size_t Dimension>
-  std::shared_ptr<const Connectivity<Dimension>> getDiamondDualConnectivity(const Connectivity<Dimension>&);
-
-  template <size_t Dimension>
-  std::shared_ptr<const ConnectivityToDiamondDualConnectivityDataMapper<Dimension>>
-  getConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<Dimension>&);
-};
-
-#endif   // DIAMOND_DUAL_CONNECTIVITY_MANAGER_HPP
diff --git a/src/mesh/DiamondDualMeshBuilder.cpp b/src/mesh/DiamondDualMeshBuilder.cpp
index df4157c9c19b377f77b754d3f33ac54141539f2f..3b3e3d80da6256092ba95bbd49f58e424cde8a56 100644
--- a/src/mesh/DiamondDualMeshBuilder.cpp
+++ b/src/mesh/DiamondDualMeshBuilder.cpp
@@ -1,25 +1,25 @@
 #include <mesh/DiamondDualMeshBuilder.hpp>
 
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityBuilder.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
 #include <mesh/ItemValueUtils.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 
 template <size_t Dimension>
 void
-DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const std::shared_ptr<const IMesh>& p_i_mesh)
+DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const IMesh& i_mesh)
 {
+  static_assert(Dimension > 1);
+
   using ConnectivityType = Connectivity<Dimension>;
   using MeshType         = Mesh<Connectivity<Dimension>>;
 
-  std::shared_ptr p_primal_mesh = std::dynamic_pointer_cast<const MeshType>(p_i_mesh);
-  const MeshType& primal_mesh   = *p_primal_mesh;
+  const MeshType& primal_mesh = dynamic_cast<const MeshType&>(i_mesh);
 
-  DiamondDualConnectivityManager& manager = DiamondDualConnectivityManager::instance();
+  DualConnectivityManager& manager = DualConnectivityManager::instance();
 
   std::shared_ptr<const ConnectivityType> p_diamond_connectivity =
     manager.getDiamondDualConnectivity(primal_mesh.connectivity());
@@ -31,34 +31,32 @@ DiamondDualMeshBuilder::_buildDualDiamondMeshFrom(const std::shared_ptr<const IM
   MeshData<Dimension>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
   const CellValue<const TinyVector<Dimension>> primal_xj = primal_mesh_data.xj();
 
-  std::shared_ptr connectivity_to_diamond_dual_connectivity_data_mapper =
-    manager.getConnectivityToDiamondDualConnectivityDataMapper(primal_mesh.connectivity());
+  std::shared_ptr primal_to_diamond_dual_connectivity_data_mapper =
+    manager.getPrimalToDiamondDualConnectivityDataMapper(primal_mesh.connectivity());
 
   NodeValue<TinyVector<Dimension>> diamond_xr{diamond_connectivity};
-  connectivity_to_diamond_dual_connectivity_data_mapper->toDualNode(primal_xr, primal_xj, diamond_xr);
+  primal_to_diamond_dual_connectivity_data_mapper->toDualNode(primal_xr, primal_xj, diamond_xr);
 
   m_mesh = std::make_shared<MeshType>(p_diamond_connectivity, diamond_xr);
 }
 
-DiamondDualMeshBuilder::DiamondDualMeshBuilder(const std::shared_ptr<const IMesh>& p_mesh)
+DiamondDualMeshBuilder::DiamondDualMeshBuilder(const IMesh& i_mesh)
 {
   std::cout << "building DiamondDualMesh\n";
 
-  switch (p_mesh->dimension()) {
-  case 1: {
-    this->_buildDualDiamondMeshFrom<1>(p_mesh);
-    break;
-  }
+  switch (i_mesh.dimension()) {
   case 2: {
-    this->_buildDualDiamondMeshFrom<2>(p_mesh);
+    this->_buildDualDiamondMeshFrom<2>(i_mesh);
     break;
   }
   case 3: {
-    this->_buildDualDiamondMeshFrom<3>(p_mesh);
+    this->_buildDualDiamondMeshFrom<3>(i_mesh);
     break;
   }
+    // LCOV_EXCL_START
   default: {
-    throw UnexpectedError("invalid mesh dimension: " + std::to_string(p_mesh->dimension()));
+    throw UnexpectedError("invalid mesh dimension: " + std::to_string(i_mesh.dimension()));
   }
+    // LCOV_EXCL_STOP
   }
 }
diff --git a/src/mesh/DiamondDualMeshBuilder.hpp b/src/mesh/DiamondDualMeshBuilder.hpp
index 2e47cfe21b4790a63e0e4aeba2fcfb9c59699286..1dc670638796be11a686173b7bc07bff6728fbf8 100644
--- a/src/mesh/DiamondDualMeshBuilder.hpp
+++ b/src/mesh/DiamondDualMeshBuilder.hpp
@@ -9,10 +9,10 @@ class DiamondDualMeshBuilder : public MeshBuilderBase
 {
  private:
   template <size_t Dimension>
-  void _buildDualDiamondMeshFrom(const std::shared_ptr<const IMesh>&);
+  void _buildDualDiamondMeshFrom(const IMesh&);
 
-  friend class DiamondDualMeshManager;
-  DiamondDualMeshBuilder(const std::shared_ptr<const IMesh>&);
+  friend class DualMeshManager;
+  DiamondDualMeshBuilder(const IMesh&);
 
  public:
   ~DiamondDualMeshBuilder() = default;
diff --git a/src/mesh/DiamondDualMeshManager.cpp b/src/mesh/DiamondDualMeshManager.cpp
deleted file mode 100644
index 0619736753f2b49a4229c60492ea6fd4c0de7212..0000000000000000000000000000000000000000
--- a/src/mesh/DiamondDualMeshManager.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-#include <mesh/DiamondDualMeshManager.hpp>
-
-#include <mesh/Connectivity.hpp>
-#include <mesh/DiamondDualMeshBuilder.hpp>
-#include <mesh/Mesh.hpp>
-#include <utils/Exceptions.hpp>
-#include <utils/PugsAssert.hpp>
-
-#include <sstream>
-
-DiamondDualMeshManager* DiamondDualMeshManager::m_instance{nullptr};
-
-void
-DiamondDualMeshManager::create()
-{
-  Assert(m_instance == nullptr, "DiamondDualMeshManager is already created");
-  m_instance = new DiamondDualMeshManager;
-}
-
-void
-DiamondDualMeshManager::destroy()
-{
-  Assert(m_instance != nullptr, "DiamondDualMeshManager was not created!");
-
-  if (m_instance->m_mesh_to_diamond_dual_mesh_map.size() > 0) {
-    std::stringstream error;
-    error << ": some meshes are still registered\n";
-    for (const auto& i_mesh_data : m_instance->m_mesh_to_diamond_dual_mesh_map) {
-      error << " - mesh " << rang::fgB::magenta << i_mesh_data.first << rang::style::reset << '\n';
-    }
-    throw UnexpectedError(error.str());
-  }
-  delete m_instance;
-  m_instance = nullptr;
-}
-
-void
-DiamondDualMeshManager::deleteMesh(const IMesh* p_mesh)
-{
-  m_mesh_to_diamond_dual_mesh_map.erase(p_mesh);
-}
-
-template <size_t Dimension>
-std::shared_ptr<const Mesh<Connectivity<Dimension>>>
-DiamondDualMeshManager::getDiamondDualMesh(std::shared_ptr<const Mesh<Connectivity<Dimension>>> mesh)
-{
-  const IMesh* p_mesh = mesh.get();
-
-  if (auto i_mesh_data = m_mesh_to_diamond_dual_mesh_map.find(p_mesh);
-      i_mesh_data != m_mesh_to_diamond_dual_mesh_map.end()) {
-    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(i_mesh_data->second);
-  } else {
-    DiamondDualMeshBuilder builder{mesh};
-
-    m_mesh_to_diamond_dual_mesh_map[p_mesh] = builder.mesh();
-    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(builder.mesh());
-  }
-}
-
-template std::shared_ptr<const Mesh<Connectivity<1>>> DiamondDualMeshManager::getDiamondDualMesh(
-  std::shared_ptr<const Mesh<Connectivity<1>>>);
-template std::shared_ptr<const Mesh<Connectivity<2>>> DiamondDualMeshManager::getDiamondDualMesh(
-  std::shared_ptr<const Mesh<Connectivity<2>>>);
-template std::shared_ptr<const Mesh<Connectivity<3>>> DiamondDualMeshManager::getDiamondDualMesh(
-  std::shared_ptr<const Mesh<Connectivity<3>>>);
diff --git a/src/mesh/DiamondDualMeshManager.hpp b/src/mesh/DiamondDualMeshManager.hpp
deleted file mode 100644
index 9f802eb96d13704235f50e82ad75e84ba9be8a64..0000000000000000000000000000000000000000
--- a/src/mesh/DiamondDualMeshManager.hpp
+++ /dev/null
@@ -1,49 +0,0 @@
-#ifndef DIAMOND_DUAL_MESH_MANAGER_HPP
-#define DIAMOND_DUAL_MESH_MANAGER_HPP
-
-#include <mesh/IMesh.hpp>
-#include <utils/PugsAssert.hpp>
-#include <utils/PugsMacros.hpp>
-
-#include <memory>
-#include <unordered_map>
-
-template <size_t Dimension>
-class Connectivity;
-
-template <typename ConnectivityType>
-class Mesh;
-
-class DiamondDualMeshManager
-{
- private:
-  std::unordered_map<const IMesh*, std::shared_ptr<const IMesh>> m_mesh_to_diamond_dual_mesh_map;
-
-  static DiamondDualMeshManager* m_instance;
-
-  DiamondDualMeshManager(const DiamondDualMeshManager&) = delete;
-  DiamondDualMeshManager(DiamondDualMeshManager&&)      = delete;
-
-  DiamondDualMeshManager()  = default;
-  ~DiamondDualMeshManager() = default;
-
- public:
-  static void create();
-  static void destroy();
-
-  PUGS_INLINE
-  static DiamondDualMeshManager&
-  instance()
-  {
-    Assert(m_instance != nullptr, "DiamondDualMeshManager was not created!");
-    return *m_instance;
-  }
-
-  void deleteMesh(const IMesh*);
-
-  template <size_t Dimension>
-  std::shared_ptr<const Mesh<Connectivity<Dimension>>> getDiamondDualMesh(
-    std::shared_ptr<const Mesh<Connectivity<Dimension>>>);
-};
-
-#endif   // DIAMOND_DUAL_MESH_MANAGER_HPP
diff --git a/src/mesh/Dual1DConnectivityBuilder.cpp b/src/mesh/Dual1DConnectivityBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..39dea9ddb6ee6c3c6f26660600765e1d9e779b60
--- /dev/null
+++ b/src/mesh/Dual1DConnectivityBuilder.cpp
@@ -0,0 +1,224 @@
+#include <mesh/Dual1DConnectivityBuilder.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ConnectivityDescriptor.hpp>
+#include <mesh/ConnectivityDispatcher.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/PrimalToDual1DConnectivityDataMapper.hpp>
+#include <mesh/RefId.hpp>
+#include <utils/Array.hpp>
+#include <utils/Messenger.hpp>
+
+#include <vector>
+
+void
+Dual1DConnectivityBuilder::_buildConnectivityDescriptor(const Connectivity<1>& primal_connectivity,
+                                                        ConnectivityDescriptor& dual_descriptor)
+{
+  const size_t primal_number_of_nodes = primal_connectivity.numberOfNodes();
+  const size_t primal_number_of_cells = primal_connectivity.numberOfCells();
+
+  const size_t dual_number_of_nodes = 2 * primal_number_of_nodes - primal_number_of_cells;
+
+  const size_t dual_number_of_cells = primal_number_of_nodes;
+  const size_t number_of_kept_nodes = 2 * (dual_number_of_nodes - dual_number_of_cells);
+
+  const auto& primal_node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
+  size_t next_kept_node_id               = 0;
+
+  dual_descriptor.node_number_vector.resize(dual_number_of_nodes);
+
+  const auto& primal_node_number = primal_connectivity.nodeNumber();
+
+  for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
+    const auto& primal_node_cell_list = primal_node_to_cell_matrix[primal_node_id];
+    if (primal_node_cell_list.size() == 1) {
+      dual_descriptor.node_number_vector[next_kept_node_id++] = primal_node_number[primal_node_id];
+    }
+  }
+
+  const size_t primal_number_of_kept_nodes = next_kept_node_id;
+
+  const auto& primal_cell_number = primal_connectivity.cellNumber();
+
+  const size_t cell_number_shift = max(primal_node_number) + 1;
+
+  for (CellId primal_cell_id = 0; primal_cell_id < primal_number_of_cells; ++primal_cell_id) {
+    dual_descriptor.node_number_vector[primal_number_of_kept_nodes + primal_cell_id] =
+      primal_cell_number[primal_cell_id] + cell_number_shift;
+  }
+
+  Assert(number_of_kept_nodes == next_kept_node_id, "unexpected number of kept nodes");
+
+  dual_descriptor.cell_number_vector.resize(dual_number_of_cells);
+  for (NodeId primal_node_id = 0; primal_node_id < primal_number_of_nodes; ++primal_node_id) {
+    dual_descriptor.cell_number_vector[primal_node_id] = primal_node_number[primal_node_id];
+  }
+
+  dual_descriptor.cell_type_vector = std::vector<CellType>(dual_number_of_cells, CellType::Line);
+
+  dual_descriptor.cell_to_node_vector.resize(dual_number_of_cells);
+
+  const auto& primal_node_local_number_in_their_cells = primal_connectivity.nodeLocalNumbersInTheirCells();
+
+  {
+    size_t next_kept_node_id = 0;
+    for (NodeId i_node = 0; i_node < primal_connectivity.numberOfNodes(); ++i_node) {
+      const size_t& i_dual_cell         = i_node;
+      const auto& primal_node_cell_list = primal_node_to_cell_matrix[i_node];
+      dual_descriptor.cell_to_node_vector[i_dual_cell].resize(2);
+      if (primal_node_cell_list.size() == 1) {
+        const auto i_node_in_cell = primal_node_local_number_in_their_cells(i_node, 0);
+
+        dual_descriptor.cell_to_node_vector[i_dual_cell][i_node_in_cell] = next_kept_node_id++;
+        dual_descriptor.cell_to_node_vector[i_dual_cell][1 - i_node_in_cell] =
+          number_of_kept_nodes + primal_node_cell_list[0];
+      } else {
+        const auto i0 = primal_node_local_number_in_their_cells(i_node, 0);
+
+        dual_descriptor.cell_to_node_vector[i_dual_cell][0] = number_of_kept_nodes + primal_node_cell_list[1 - i0];
+        dual_descriptor.cell_to_node_vector[i_dual_cell][1] = number_of_kept_nodes + primal_node_cell_list[i0];
+      }
+    }
+  }
+}
+
+void
+Dual1DConnectivityBuilder::_buildConnectivityFrom(const IConnectivity& i_primal_connectivity)
+{
+  using ConnectivityType = Connectivity<1>;
+
+  const ConnectivityType& primal_connectivity = dynamic_cast<const ConnectivityType&>(i_primal_connectivity);
+
+  ConnectivityDescriptor dual_descriptor;
+
+  this->_buildConnectivityDescriptor(primal_connectivity, dual_descriptor);
+
+  {
+    const std::unordered_map<unsigned int, NodeId> node_to_id_map = [&] {
+      std::unordered_map<unsigned int, NodeId> node_to_id_map;
+      for (size_t i_node = 0; i_node < dual_descriptor.node_number_vector.size(); ++i_node) {
+        node_to_id_map[dual_descriptor.node_number_vector[i_node]] = i_node;
+      }
+      return node_to_id_map;
+    }();
+
+    for (size_t i_node_list = 0; i_node_list < primal_connectivity.template numberOfRefItemList<ItemType::node>();
+         ++i_node_list) {
+      const auto& primal_ref_node_list = primal_connectivity.template refItemList<ItemType::node>(i_node_list);
+      const auto& primal_node_list     = primal_ref_node_list.list();
+
+      const std::vector<NodeId> dual_node_list = [&]() {
+        std::vector<NodeId> dual_node_list;
+
+        for (size_t i_primal_node = 0; i_primal_node < primal_node_list.size(); ++i_primal_node) {
+          auto primal_node_id    = primal_node_list[i_primal_node];
+          const auto i_dual_node = node_to_id_map.find(primal_connectivity.nodeNumber()[primal_node_id]);
+          if (i_dual_node != node_to_id_map.end()) {
+            dual_node_list.push_back(i_dual_node->second);
+          }
+        }
+
+        return dual_node_list;
+      }();
+
+      if (parallel::allReduceOr(dual_node_list.size() > 0)) {
+        Array<NodeId> node_array(dual_node_list.size());
+        for (size_t i = 0; i < dual_node_list.size(); ++i) {
+          node_array[i] = dual_node_list[i];
+        }
+        dual_descriptor.addRefItemList(RefNodeList{primal_ref_node_list.refId(), node_array});
+      }
+    }
+  }
+
+  const size_t primal_number_of_nodes = primal_connectivity.numberOfNodes();
+  const size_t primal_number_of_cells = primal_connectivity.numberOfCells();
+
+  dual_descriptor.node_owner_vector.resize(dual_descriptor.node_number_vector.size());
+
+  {
+    const auto& node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
+    const auto& primal_node_owner   = primal_connectivity.nodeOwner();
+    size_t next_kept_node_id        = 0;
+    for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
+      if (node_to_cell_matrix[primal_node_id].size() == 1) {
+        dual_descriptor.node_owner_vector[next_kept_node_id++] = primal_node_owner[primal_node_id];
+      }
+    }
+    const size_t number_of_kept_nodes = next_kept_node_id;
+    const auto& primal_cell_owner     = primal_connectivity.cellOwner();
+    for (CellId primal_cell_id = 0; primal_cell_id < primal_number_of_cells; ++primal_cell_id) {
+      dual_descriptor.node_owner_vector[number_of_kept_nodes + primal_cell_id] = primal_cell_owner[primal_cell_id];
+    }
+  }
+
+  {
+    dual_descriptor.cell_owner_vector.resize(dual_descriptor.cell_number_vector.size());
+    const auto& primal_node_owner = primal_connectivity.nodeOwner();
+    for (NodeId primal_node_id = 0; primal_node_id < primal_number_of_nodes; ++primal_node_id) {
+      dual_descriptor.cell_owner_vector[primal_node_id] = primal_node_owner[primal_node_id];
+    }
+  }
+
+  m_connectivity = ConnectivityType::build(dual_descriptor);
+
+  {
+    const auto& node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
+
+    NodeId dual_node_id            = 0;
+    m_primal_node_to_dual_node_map = [&]() {
+      std::vector<std::pair<NodeId, NodeId>> primal_node_to_dual_node_vector;
+      for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
+        if (node_to_cell_matrix[primal_node_id].size() == 1) {
+          primal_node_to_dual_node_vector.push_back(std::make_pair(primal_node_id, dual_node_id++));
+        }
+      }
+      return convert_to_array(primal_node_to_dual_node_vector);
+    }();
+
+    m_primal_cell_to_dual_node_map = [&]() {
+      CellIdToNodeIdMap primal_cell_to_dual_node_map{primal_number_of_cells};
+      for (CellId primal_cell_id = 0; primal_cell_id < primal_cell_to_dual_node_map.size(); ++primal_cell_id) {
+        primal_cell_to_dual_node_map[primal_cell_id] = std::make_pair(primal_cell_id, dual_node_id++);
+      }
+      return primal_cell_to_dual_node_map;
+    }();
+
+    m_primal_node_to_dual_cell_map = [&]() {
+      NodeIdToCellIdMap primal_node_to_dual_cell_map{primal_connectivity.numberOfFaces()};
+      for (size_t id = 0; id < primal_node_to_dual_cell_map.size(); ++id) {
+        const CellId dual_cell_id   = id;
+        const NodeId primal_node_id = id;
+
+        primal_node_to_dual_cell_map[id] = std::make_pair(primal_node_id, dual_cell_id);
+      }
+      return primal_node_to_dual_cell_map;
+    }();
+  }
+
+  m_mapper =
+    std::make_shared<PrimalToDual1DConnectivityDataMapper>(primal_connectivity,
+                                                           dynamic_cast<const ConnectivityType&>(*m_connectivity),
+                                                           m_primal_node_to_dual_node_map,
+                                                           m_primal_cell_to_dual_node_map,
+                                                           m_primal_node_to_dual_cell_map);
+}
+
+Dual1DConnectivityBuilder::Dual1DConnectivityBuilder(const IConnectivity& connectivity)
+{
+  // LCOV_EXCL_START
+  if (parallel::size() > 1) {
+    throw NotImplementedError("Construction of diamond dual mesh is not implemented in parallel");
+  }
+  // LCOV_EXCL_STOP
+
+  if (connectivity.dimension() == 1) {
+    this->_buildConnectivityFrom(connectivity);
+  } else {
+    // LCOV_EXCL_START
+    throw UnexpectedError("invalid connectivity dimension: " + std::to_string(connectivity.dimension()));
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/mesh/Dual1DConnectivityBuilder.hpp b/src/mesh/Dual1DConnectivityBuilder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bcc9ff041750d636f0827b2166b0a23d534af173
--- /dev/null
+++ b/src/mesh/Dual1DConnectivityBuilder.hpp
@@ -0,0 +1,39 @@
+#ifndef DUAL_1D_CONNECTIVITY_BUILDER_HPP
+#define DUAL_1D_CONNECTIVITY_BUILDER_HPP
+
+#include <mesh/ConnectivityBuilderBase.hpp>
+#include <mesh/IPrimalToDualConnectivityDataMapper.hpp>
+#include <mesh/ItemIdToItemIdMap.hpp>
+
+#include <memory>
+
+template <size_t>
+class Connectivity;
+class ConnectivityDescriptor;
+
+class Dual1DConnectivityBuilder : public ConnectivityBuilderBase
+{
+  NodeIdToNodeIdMap m_primal_node_to_dual_node_map;
+  CellIdToNodeIdMap m_primal_cell_to_dual_node_map;
+  NodeIdToCellIdMap m_primal_node_to_dual_cell_map;
+
+  std::shared_ptr<IPrimalToDualConnectivityDataMapper> m_mapper;
+
+  void _buildConnectivityDescriptor(const Connectivity<1>&, ConnectivityDescriptor&);
+
+  void _buildConnectivityFrom(const IConnectivity&);
+
+  friend class DualConnectivityManager;
+  Dual1DConnectivityBuilder(const IConnectivity&);
+
+ public:
+  std::shared_ptr<IPrimalToDualConnectivityDataMapper>
+  mapper() const
+  {
+    return m_mapper;
+  }
+
+  ~Dual1DConnectivityBuilder() = default;
+};
+
+#endif   // DUAL_1D_CONNECTIVITY_BUILDER_HPP
diff --git a/src/mesh/Dual1DMeshBuilder.cpp b/src/mesh/Dual1DMeshBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ed558e2fc3c6c82861f93bc4364eb8dddf4000be
--- /dev/null
+++ b/src/mesh/Dual1DMeshBuilder.cpp
@@ -0,0 +1,52 @@
+#include <mesh/Dual1DMeshBuilder.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
+#include <mesh/PrimalToDual1DConnectivityDataMapper.hpp>
+
+void
+Dual1DMeshBuilder::_buildDual1DMeshFrom(const IMesh& i_mesh)
+{
+  using ConnectivityType = Connectivity<1>;
+  using MeshType         = Mesh<Connectivity<1>>;
+
+  const MeshType& primal_mesh = dynamic_cast<const MeshType&>(i_mesh);
+
+  DualConnectivityManager& manager = DualConnectivityManager::instance();
+
+  std::shared_ptr<const ConnectivityType> p_dual_connectivity =
+    manager.getDiamondDualConnectivity(primal_mesh.connectivity());
+
+  const ConnectivityType& dual_connectivity = *p_dual_connectivity;
+
+  const NodeValue<const TinyVector<1>> primal_xr = primal_mesh.xr();
+
+  MeshData<1>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
+  const CellValue<const TinyVector<1>> primal_xj = primal_mesh_data.xj();
+
+  std::shared_ptr primal_to_dual_1d_connectivity_data_mapper =
+    manager.getPrimalToDual1DConnectivityDataMapper(primal_mesh.connectivity());
+
+  NodeValue<TinyVector<1>> dual_xr{dual_connectivity};
+  primal_to_dual_1d_connectivity_data_mapper->toDualNode(primal_xr, primal_xj, dual_xr);
+
+  m_mesh = std::make_shared<MeshType>(p_dual_connectivity, dual_xr);
+}
+
+Dual1DMeshBuilder::Dual1DMeshBuilder(const IMesh& i_mesh)
+{
+  std::cout << "building Dual1DMesh\n";
+
+  if (i_mesh.dimension() == 1) {
+    this->_buildDual1DMeshFrom(i_mesh);
+  } else {
+    // LCOV_EXCL_START
+    throw UnexpectedError("invalid mesh dimension: " + std::to_string(i_mesh.dimension()));
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/mesh/Dual1DMeshBuilder.hpp b/src/mesh/Dual1DMeshBuilder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2b271aeebdfc9c8147ee7259610e344b9bdaae8
--- /dev/null
+++ b/src/mesh/Dual1DMeshBuilder.hpp
@@ -0,0 +1,20 @@
+#ifndef DUAL_1D_MESH_BUILDER_HPP
+#define DUAL_1D_MESH_BUILDER_HPP
+
+#include <mesh/MeshBuilderBase.hpp>
+
+#include <memory>
+
+class Dual1DMeshBuilder : public MeshBuilderBase
+{
+ private:
+  void _buildDual1DMeshFrom(const IMesh&);
+
+  friend class DualMeshManager;
+  Dual1DMeshBuilder(const IMesh&);
+
+ public:
+  ~Dual1DMeshBuilder() = default;
+};
+
+#endif   // DUAL_1D_MESH_BUILDER_HPP
diff --git a/src/mesh/DualConnectivityManager.cpp b/src/mesh/DualConnectivityManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fefc4bde5fd9e4675c2eed70eae409ee1a94a32d
--- /dev/null
+++ b/src/mesh/DualConnectivityManager.cpp
@@ -0,0 +1,221 @@
+#include <utils/PugsAssert.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/DiamondDualConnectivityBuilder.hpp>
+#include <mesh/Dual1DConnectivityBuilder.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/MedianDualConnectivityBuilder.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
+#include <mesh/PrimalToDual1DConnectivityDataMapper.hpp>
+#include <mesh/PrimalToMedianDualConnectivityDataMapper.hpp>
+#include <utils/Exceptions.hpp>
+
+#include <sstream>
+
+DualConnectivityManager* DualConnectivityManager::m_instance{nullptr};
+
+void
+DualConnectivityManager::create()
+{
+  Assert(m_instance == nullptr, "DualConnectivityManager is already created");
+  m_instance = new DualConnectivityManager;
+}
+
+void
+DualConnectivityManager::destroy()
+{
+  Assert(m_instance != nullptr, "DualConnectivityManager was not created!");
+
+  // LCOV_EXCL_START
+  if (m_instance->m_primal_to_dual_connectivity_info_map.size() > 0) {
+    std::stringstream error;
+    error << ": some connectivities are still registered\n";
+    for (const auto& [key, diamond_dual_connectivity_info] : m_instance->m_primal_to_dual_connectivity_info_map) {
+      error << " - connectivity " << rang::fgB::magenta << key.second << rang::style::reset << ": " << name(key.first)
+            << " dual connectivity of " << rang::fgB::yellow << diamond_dual_connectivity_info.dualConnectivity().get()
+            << rang::style::reset << '\n';
+    }
+    throw UnexpectedError(error.str());
+    // LCOV_EXCL_STOP
+  }
+  delete m_instance;
+  m_instance = nullptr;
+}
+
+void
+DualConnectivityManager::deleteConnectivity(const IConnectivity* p_connectivity)
+{
+  bool has_removed = false;
+  do {
+    has_removed = false;
+    for (const auto& [key, dual_connectivity] : m_primal_to_dual_connectivity_info_map) {
+      const auto& [type, p_parent_connectivity] = key;
+      if (p_connectivity == p_parent_connectivity) {
+        m_primal_to_dual_connectivity_info_map.erase(key);
+        has_removed = true;
+        break;
+      }
+    }
+  } while (has_removed);
+}
+
+template <typename DualConnectivityBuilderType>
+DualConnectivityManager::DualConnectivityInfo
+DualConnectivityManager::_buildDualConnectivity(const Key& key, const IConnectivity& connectivity)
+{
+  DualConnectivityBuilderType builder{connectivity};
+  DualConnectivityInfo connectivity_info{builder.connectivity(), builder.mapper()};
+  m_primal_to_dual_connectivity_info_map[key] = connectivity_info;
+  return connectivity_info;
+}
+
+DualConnectivityManager::DualConnectivityInfo
+DualConnectivityManager::_getDualConnectivityInfo(const DualMeshType& type, const IConnectivity& connectivity)
+{
+  const IConnectivity* p_connectivity = &connectivity;
+
+  auto key = std::make_pair(type, p_connectivity);
+  if (auto i_connectivity = m_primal_to_dual_connectivity_info_map.find(key);
+      i_connectivity != m_primal_to_dual_connectivity_info_map.end()) {
+    return i_connectivity->second;
+  } else {
+    switch (type) {
+    case DualMeshType::Diamond: {
+      return this->_buildDualConnectivity<DiamondDualConnectivityBuilder>(key, connectivity);
+    }
+    case DualMeshType::Dual1D: {
+      return this->_buildDualConnectivity<Dual1DConnectivityBuilder>(key, connectivity);
+    }
+    case DualMeshType::Median: {
+      return this->_buildDualConnectivity<MedianDualConnectivityBuilder>(key, connectivity);
+    }
+      // LCOV_EXCL_START
+    default: {
+      throw UnexpectedError("invalid dual mesh type");
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+}
+
+std::shared_ptr<const Connectivity<1>>
+DualConnectivityManager::getDual1DConnectivity(const Connectivity<1>& connectivity)
+{
+  return std::dynamic_pointer_cast<const Connectivity<1>>(
+    this->_getDualConnectivityInfo(DualMeshType::Dual1D, connectivity).dualConnectivity());
+}
+
+std::shared_ptr<const PrimalToDual1DConnectivityDataMapper>
+DualConnectivityManager::getPrimalToDual1DConnectivityDataMapper(const Connectivity<1>& connectivity)
+{
+  auto i_data_mapper =
+    this->_getDualConnectivityInfo(DualMeshType::Dual1D, connectivity).connectivityToDualConnectivityDataMapper();
+  auto dual_1d_data_mapper = std::dynamic_pointer_cast<const PrimalToDual1DConnectivityDataMapper>(i_data_mapper);
+
+  if (dual_1d_data_mapper.use_count() > 0) {
+    return dual_1d_data_mapper;
+  } else {
+    // LCOV_EXCL_START
+    throw UnexpectedError("invalid connectivity data mapper type");
+    // LCOV_EXCL_STOP
+  }
+}
+
+template <size_t Dimension>
+std::shared_ptr<const Connectivity<Dimension>>
+DualConnectivityManager::getDiamondDualConnectivity(const Connectivity<Dimension>& connectivity)
+{
+  auto dual_mesh_type = (connectivity.dimension() == 1) ? DualMeshType::Dual1D : DualMeshType::Diamond;
+
+  return std::dynamic_pointer_cast<const Connectivity<Dimension>>(
+    this->_getDualConnectivityInfo(dual_mesh_type, connectivity).dualConnectivity());
+}
+
+std::shared_ptr<const PrimalToDual1DConnectivityDataMapper>
+DualConnectivityManager::getPrimalToDiamondDualConnectivityDataMapper(const Connectivity<1>& connectivity)
+{
+  return this->getPrimalToDual1DConnectivityDataMapper(connectivity);
+}
+
+template <size_t Dimension>
+std::shared_ptr<const PrimalToDiamondDualConnectivityDataMapper<Dimension>>
+DualConnectivityManager::getPrimalToDiamondDualConnectivityDataMapper(const Connectivity<Dimension>& connectivity)
+{
+  static_assert((Dimension == 2) or (Dimension == 3));
+  auto i_data_mapper =
+    this->_getDualConnectivityInfo(DualMeshType::Diamond, connectivity).connectivityToDualConnectivityDataMapper();
+  auto diamond_data_mapper =
+    std::dynamic_pointer_cast<const PrimalToDiamondDualConnectivityDataMapper<Dimension>>(i_data_mapper);
+
+  if (diamond_data_mapper.use_count() > 0) {
+    return diamond_data_mapper;
+  } else {
+    // LCOV_EXCL_START
+    throw UnexpectedError("invalid connectivity data mapper type");
+    // LCOV_EXCL_STOP
+  }
+}
+
+template <size_t Dimension>
+std::shared_ptr<const Connectivity<Dimension>>
+DualConnectivityManager::getMedianDualConnectivity(const Connectivity<Dimension>& connectivity)
+{
+  auto dual_mesh_type = (connectivity.dimension() == 1) ? DualMeshType::Dual1D : DualMeshType::Median;
+
+  return std::dynamic_pointer_cast<const Connectivity<Dimension>>(
+    this->_getDualConnectivityInfo(dual_mesh_type, connectivity).dualConnectivity());
+}
+
+std::shared_ptr<const PrimalToDual1DConnectivityDataMapper>
+DualConnectivityManager::getPrimalToMedianDualConnectivityDataMapper(const Connectivity<1>& connectivity)
+{
+  return this->getPrimalToDual1DConnectivityDataMapper(connectivity);
+}
+
+template <size_t Dimension>
+std::shared_ptr<const PrimalToMedianDualConnectivityDataMapper<Dimension>>
+DualConnectivityManager::getPrimalToMedianDualConnectivityDataMapper(const Connectivity<Dimension>& connectivity)
+{
+  auto i_data_mapper =
+    this->_getDualConnectivityInfo(DualMeshType::Median, connectivity).connectivityToDualConnectivityDataMapper();
+  auto data_mapper =
+    std::dynamic_pointer_cast<const PrimalToMedianDualConnectivityDataMapper<Dimension>>(i_data_mapper);
+
+  if (data_mapper.use_count() > 0) {
+    return data_mapper;
+  } else {
+    // LCOV_EXCL_START
+    throw UnexpectedError("invalid connectivity data mapper type");
+    // LCOV_EXCL_STOP
+  }
+}
+
+template std::shared_ptr<const Connectivity<1>> DualConnectivityManager::getDiamondDualConnectivity(
+  const Connectivity<1>& connectivity);
+
+template std::shared_ptr<const Connectivity<2>> DualConnectivityManager::getDiamondDualConnectivity(
+  const Connectivity<2>& connectivity);
+
+template std::shared_ptr<const Connectivity<3>> DualConnectivityManager::getDiamondDualConnectivity(
+  const Connectivity<3>& connectivity);
+
+template std::shared_ptr<const PrimalToDiamondDualConnectivityDataMapper<2>>
+DualConnectivityManager::getPrimalToDiamondDualConnectivityDataMapper(const Connectivity<2>&);
+
+template std::shared_ptr<const PrimalToDiamondDualConnectivityDataMapper<3>>
+DualConnectivityManager::getPrimalToDiamondDualConnectivityDataMapper(const Connectivity<3>&);
+
+template std::shared_ptr<const Connectivity<1>> DualConnectivityManager::getMedianDualConnectivity(
+  const Connectivity<1>& connectivity);
+
+template std::shared_ptr<const Connectivity<2>> DualConnectivityManager::getMedianDualConnectivity(
+  const Connectivity<2>& connectivity);
+
+template std::shared_ptr<const Connectivity<3>> DualConnectivityManager::getMedianDualConnectivity(
+  const Connectivity<3>& connectivity);
+
+template std::shared_ptr<const PrimalToMedianDualConnectivityDataMapper<2>>
+DualConnectivityManager::getPrimalToMedianDualConnectivityDataMapper(const Connectivity<2>&);
+
+template std::shared_ptr<const PrimalToMedianDualConnectivityDataMapper<3>>
+DualConnectivityManager::getPrimalToMedianDualConnectivityDataMapper(const Connectivity<3>&);
diff --git a/src/mesh/DualConnectivityManager.hpp b/src/mesh/DualConnectivityManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6389c5d459ac677295c09fcfce730cec2bd2f525
--- /dev/null
+++ b/src/mesh/DualConnectivityManager.hpp
@@ -0,0 +1,126 @@
+#ifndef DUAL_CONNECTIVITY_MANAGER_HPP
+#define DUAL_CONNECTIVITY_MANAGER_HPP
+
+#include <mesh/DualMeshType.hpp>
+#include <mesh/IConnectivity.hpp>
+#include <mesh/IPrimalToDualConnectivityDataMapper.hpp>
+
+#include <memory>
+#include <unordered_map>
+
+template <size_t Dimension>
+class Connectivity;
+
+template <size_t Dimension>
+class PrimalToDiamondDualConnectivityDataMapper;
+
+template <size_t Dimension>
+class PrimalToMedianDualConnectivityDataMapper;
+
+class PrimalToDual1DConnectivityDataMapper;
+
+class DualConnectivityManager
+{
+ private:
+  class DualConnectivityInfo
+  {
+   private:
+    std::shared_ptr<const IConnectivity> m_dual_connectivity;
+    std::shared_ptr<const IPrimalToDualConnectivityDataMapper> m_primal_to_dual_connectivity_data_mapper;
+
+   public:
+    PUGS_INLINE
+    std::shared_ptr<const IConnectivity>
+    dualConnectivity() const
+    {
+      return m_dual_connectivity;
+    }
+
+    PUGS_INLINE
+    std::shared_ptr<const IPrimalToDualConnectivityDataMapper>
+    connectivityToDualConnectivityDataMapper()
+    {
+      return m_primal_to_dual_connectivity_data_mapper;
+    }
+
+    DualConnectivityInfo& operator=(const DualConnectivityInfo&) = default;
+    DualConnectivityInfo& operator=(DualConnectivityInfo&&) = delete;
+
+    DualConnectivityInfo()                            = default;
+    DualConnectivityInfo(const DualConnectivityInfo&) = default;
+    DualConnectivityInfo(DualConnectivityInfo&&)      = default;
+
+    DualConnectivityInfo(
+      const std::shared_ptr<const IConnectivity>& dual_connectivity,
+      const std::shared_ptr<const IPrimalToDualConnectivityDataMapper>& primal_to_dual_connectivity_data_mapper)
+      : m_dual_connectivity{dual_connectivity},
+        m_primal_to_dual_connectivity_data_mapper{primal_to_dual_connectivity_data_mapper}
+    {}
+
+    ~DualConnectivityInfo() = default;
+  };
+
+  using Key = std::pair<DualMeshType, const IConnectivity*>;
+  struct HashKey
+  {
+    size_t
+    operator()(const Key& key) const
+    {
+      return (std::hash<typename Key::first_type>()(key.first)) ^ (std::hash<typename Key::second_type>()(key.second));
+    }
+  };
+
+  template <typename DualConnectivityBuilderType>
+  DualConnectivityInfo _buildDualConnectivity(const Key& key, const IConnectivity& connectivity);
+
+  DualConnectivityInfo _getDualConnectivityInfo(const DualMeshType& type, const IConnectivity& connectivity);
+
+  std::unordered_map<Key, DualConnectivityInfo, HashKey> m_primal_to_dual_connectivity_info_map;
+
+  static DualConnectivityManager* m_instance;
+
+  DualConnectivityManager(const DualConnectivityManager&) = delete;
+  DualConnectivityManager(DualConnectivityManager&&)      = delete;
+
+  DualConnectivityManager()  = default;
+  ~DualConnectivityManager() = default;
+
+ public:
+  static void create();
+  static void destroy();
+
+  PUGS_INLINE
+  static DualConnectivityManager&
+  instance()
+  {
+    Assert(m_instance != nullptr, "DualConnectivityManager was not created!");
+    return *m_instance;
+  }
+
+  void deleteConnectivity(const IConnectivity*);
+
+  std::shared_ptr<const Connectivity<1>> getDual1DConnectivity(const Connectivity<1>&);
+
+  std::shared_ptr<const PrimalToDual1DConnectivityDataMapper> getPrimalToDual1DConnectivityDataMapper(
+    const Connectivity<1>&);
+
+  template <size_t Dimension>
+  std::shared_ptr<const Connectivity<Dimension>> getMedianDualConnectivity(const Connectivity<Dimension>&);
+
+  std::shared_ptr<const PrimalToDual1DConnectivityDataMapper>   // special 1D case
+  getPrimalToMedianDualConnectivityDataMapper(const Connectivity<1>&);
+  template <size_t Dimension>
+  std::shared_ptr<const PrimalToMedianDualConnectivityDataMapper<Dimension>>
+  getPrimalToMedianDualConnectivityDataMapper(const Connectivity<Dimension>&);
+
+  template <size_t Dimension>
+  std::shared_ptr<const Connectivity<Dimension>> getDiamondDualConnectivity(const Connectivity<Dimension>&);
+
+  std::shared_ptr<const PrimalToDual1DConnectivityDataMapper>   // special 1D case
+  getPrimalToDiamondDualConnectivityDataMapper(const Connectivity<1>&);
+  template <size_t Dimension>
+  std::shared_ptr<const PrimalToDiamondDualConnectivityDataMapper<Dimension>>
+  getPrimalToDiamondDualConnectivityDataMapper(const Connectivity<Dimension>&);
+};
+
+#endif   // DUAL_CONNECTIVITY_MANAGER_HPP
diff --git a/src/mesh/DualMeshManager.cpp b/src/mesh/DualMeshManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..86384b6ad66d651b952a8ef1f1f86fd74a7b9176
--- /dev/null
+++ b/src/mesh/DualMeshManager.cpp
@@ -0,0 +1,130 @@
+#include <mesh/DualMeshManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/DiamondDualMeshBuilder.hpp>
+#include <mesh/Dual1DMeshBuilder.hpp>
+#include <mesh/MedianDualMeshBuilder.hpp>
+#include <mesh/Mesh.hpp>
+#include <utils/Exceptions.hpp>
+#include <utils/PugsAssert.hpp>
+
+#include <sstream>
+
+DualMeshManager* DualMeshManager::m_instance{nullptr};
+
+void
+DualMeshManager::create()
+{
+  Assert(m_instance == nullptr, "DualMeshManager is already created");
+  m_instance = new DualMeshManager;
+}
+
+void
+DualMeshManager::destroy()
+{
+  Assert(m_instance != nullptr, "DualMeshManager was not created!");
+
+  if (m_instance->m_mesh_to_dual_mesh_map.size() > 0) {
+    // LCOV_EXCL_START
+    std::stringstream error;
+    error << ": some meshes are still registered\n";
+    for (const auto& [key, parent_mesh] : m_instance->m_mesh_to_dual_mesh_map) {
+      error << " - mesh " << rang::fgB::magenta << key.second << rang::style::reset << ": " << name(key.first)
+            << " dual mesh of " << rang::fgB::yellow << parent_mesh.get() << rang::style::reset << '\n';
+    }
+    throw UnexpectedError(error.str());
+    // LCOV_EXCL_STOP
+  }
+  delete m_instance;
+  m_instance = nullptr;
+}
+
+void
+DualMeshManager::deleteMesh(const IMesh* p_mesh)
+{
+  bool has_removed = false;
+  do {
+    has_removed = false;
+    for (const auto& [key, dual_mesh] : m_mesh_to_dual_mesh_map) {
+      const auto& [type, p_parent_mesh] = key;
+      if (p_mesh == p_parent_mesh) {
+        m_mesh_to_dual_mesh_map.erase(key);
+        has_removed = true;
+        break;
+      }
+    }
+  } while (has_removed);
+}
+
+std::shared_ptr<const Mesh<Connectivity<1>>>
+DualMeshManager::getDual1DMesh(const Mesh<Connectivity<1>>& mesh)
+{
+  const IMesh* p_mesh = &mesh;
+
+  auto key = std::make_pair(DualMeshType::Dual1D, p_mesh);
+  if (auto i_mesh_data = m_mesh_to_dual_mesh_map.find(key); i_mesh_data != m_mesh_to_dual_mesh_map.end()) {
+    return std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(i_mesh_data->second);
+  } else {
+    Dual1DMeshBuilder builder{mesh};
+
+    m_mesh_to_dual_mesh_map[key] = builder.mesh();
+    return std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(builder.mesh());
+  }
+}
+
+template <>
+std::shared_ptr<const Mesh<Connectivity<1>>>
+DualMeshManager::getMedianDualMesh(const Mesh<Connectivity<1>>& mesh)
+{
+  return this->getDual1DMesh(mesh);
+}
+
+template <size_t Dimension>
+std::shared_ptr<const Mesh<Connectivity<Dimension>>>
+DualMeshManager::getMedianDualMesh(const Mesh<Connectivity<Dimension>>& mesh)
+{
+  static_assert(Dimension > 1);
+  const IMesh* p_mesh = &mesh;
+
+  auto key = std::make_pair(DualMeshType::Median, p_mesh);
+  if (auto i_mesh_data = m_mesh_to_dual_mesh_map.find(key); i_mesh_data != m_mesh_to_dual_mesh_map.end()) {
+    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(i_mesh_data->second);
+  } else {
+    MedianDualMeshBuilder builder{mesh};
+
+    m_mesh_to_dual_mesh_map[key] = builder.mesh();
+    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(builder.mesh());
+  }
+}
+
+template std::shared_ptr<const Mesh<Connectivity<2>>> DualMeshManager::getMedianDualMesh(const Mesh<Connectivity<2>>&);
+template std::shared_ptr<const Mesh<Connectivity<3>>> DualMeshManager::getMedianDualMesh(const Mesh<Connectivity<3>>&);
+
+template <>
+std::shared_ptr<const Mesh<Connectivity<1>>>
+DualMeshManager::getDiamondDualMesh(const Mesh<Connectivity<1>>& mesh)
+{
+  return this->getDual1DMesh(mesh);
+}
+
+template <size_t Dimension>
+std::shared_ptr<const Mesh<Connectivity<Dimension>>>
+DualMeshManager::getDiamondDualMesh(const Mesh<Connectivity<Dimension>>& mesh)
+{
+  static_assert(Dimension > 1);
+
+  const IMesh* p_mesh = &mesh;
+
+  auto key = std::make_pair(DualMeshType::Diamond, p_mesh);
+  if (auto i_mesh_data = m_mesh_to_dual_mesh_map.find(key); i_mesh_data != m_mesh_to_dual_mesh_map.end()) {
+    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(i_mesh_data->second);
+  } else {
+    DiamondDualMeshBuilder builder{mesh};
+
+    m_mesh_to_dual_mesh_map[key] = builder.mesh();
+    return std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(builder.mesh());
+  }
+}
+
+template std::shared_ptr<const Mesh<Connectivity<2>>> DualMeshManager::getDiamondDualMesh(const Mesh<Connectivity<2>>&);
+template std::shared_ptr<const Mesh<Connectivity<3>>> DualMeshManager::getDiamondDualMesh(const Mesh<Connectivity<3>>&);
diff --git a/src/mesh/DualMeshManager.hpp b/src/mesh/DualMeshManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b6358b7010d6e8a2487f53697e143640da79235
--- /dev/null
+++ b/src/mesh/DualMeshManager.hpp
@@ -0,0 +1,64 @@
+#ifndef DUAL_MESH_MANAGER_HPP
+#define DUAL_MESH_MANAGER_HPP
+
+#include <mesh/DualMeshType.hpp>
+#include <mesh/IMesh.hpp>
+#include <utils/PugsAssert.hpp>
+#include <utils/PugsMacros.hpp>
+
+#include <memory>
+#include <unordered_map>
+
+template <size_t Dimension>
+class Connectivity;
+
+template <typename ConnectivityType>
+class Mesh;
+
+class DualMeshManager
+{
+ private:
+  using Key = std::pair<DualMeshType, const IMesh*>;
+  struct HashKey
+  {
+    size_t
+    operator()(const Key& key) const
+    {
+      return (std::hash<typename Key::first_type>()(key.first)) ^ (std::hash<typename Key::second_type>()(key.second));
+    }
+  };
+
+  std::unordered_map<Key, std::shared_ptr<const IMesh>, HashKey> m_mesh_to_dual_mesh_map;
+
+  static DualMeshManager* m_instance;
+
+  DualMeshManager(const DualMeshManager&) = delete;
+  DualMeshManager(DualMeshManager&&)      = delete;
+
+  DualMeshManager()  = default;
+  ~DualMeshManager() = default;
+
+ public:
+  static void create();
+  static void destroy();
+
+  PUGS_INLINE
+  static DualMeshManager&
+  instance()
+  {
+    Assert(m_instance != nullptr, "DualMeshManager was not created!");
+    return *m_instance;
+  }
+
+  void deleteMesh(const IMesh*);
+
+  std::shared_ptr<const Mesh<Connectivity<1>>> getDual1DMesh(const Mesh<Connectivity<1>>&);
+
+  template <size_t Dimension>
+  std::shared_ptr<const Mesh<Connectivity<Dimension>>> getDiamondDualMesh(const Mesh<Connectivity<Dimension>>&);
+
+  template <size_t Dimension>
+  std::shared_ptr<const Mesh<Connectivity<Dimension>>> getMedianDualMesh(const Mesh<Connectivity<Dimension>>&);
+};
+
+#endif   // DUAL_MESH_MANAGER_HPP
diff --git a/src/mesh/DualMeshType.hpp b/src/mesh/DualMeshType.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..967d6a453aea88ae9a364dd8e879b97a4334e83e
--- /dev/null
+++ b/src/mesh/DualMeshType.hpp
@@ -0,0 +1,36 @@
+#ifndef DUAL_MESH_TYPE_HPP
+#define DUAL_MESH_TYPE_HPP
+
+#include <utils/Exceptions.hpp>
+#include <utils/PugsMacros.hpp>
+
+#include <string>
+
+enum class DualMeshType
+{
+  Diamond,
+  Dual1D,
+  Median
+};
+
+PUGS_INLINE
+std::string
+name(DualMeshType type)
+{
+  switch (type) {
+  case DualMeshType::Diamond: {
+    return "diamond";
+  }
+  case DualMeshType::Dual1D: {
+    return "dual 1d";
+  }
+  case DualMeshType::Median: {
+    return "median";
+  }
+  default: {
+    throw UnexpectedError("unexpected dual mesh type");
+  }
+  }
+}
+
+#endif   // DUAL_MESH_TYPE_HPP
diff --git a/src/mesh/GmshReader.cpp b/src/mesh/GmshReader.cpp
index 3a83ef5c3f49d068fbfd8e62d4ff002df50a8845..15b34e1613586a38385d7a4051103b0713dafef2 100644
--- a/src/mesh/GmshReader.cpp
+++ b/src/mesh/GmshReader.cpp
@@ -9,7 +9,6 @@
 #include <mesh/ItemValueUtils.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/RefItemList.hpp>
-#include <utils/ArrayUtils.hpp>
 #include <utils/Exceptions.hpp>
 
 #include <rang.hpp>
@@ -65,6 +64,22 @@ GmshConnectivityBuilder<1>::GmshConnectivityBuilder(const GmshReader::GmshData&
     descriptor.addRefItemList(RefNodeList(physical_ref_id.refId(), point_list));
   }
 
+  std::map<unsigned int, std::vector<unsigned int>> ref_cells_map;
+  for (unsigned int j = 0; j < gmsh_data.__edges_ref.size(); ++j) {
+    const unsigned int elem_number = j;
+    const unsigned int& ref        = gmsh_data.__edges_ref[j];
+    ref_cells_map[ref].push_back(elem_number);
+  }
+
+  for (const auto& ref_cell_list : ref_cells_map) {
+    Array<CellId> cell_list(ref_cell_list.second.size());
+    for (size_t j = 0; j < ref_cell_list.second.size(); ++j) {
+      cell_list[j] = ref_cell_list.second[j];
+    }
+    const GmshReader::PhysicalRefId& physical_ref_id = gmsh_data.m_physical_ref_map.at(ref_cell_list.first);
+    descriptor.addRefItemList(RefCellList(physical_ref_id.refId(), cell_list));
+  }
+
   descriptor.cell_owner_vector.resize(nb_cells);
   std::fill(descriptor.cell_owner_vector.begin(), descriptor.cell_owner_vector.end(), parallel::rank());
 
@@ -106,9 +121,9 @@ GmshConnectivityBuilder<2>::GmshConnectivityBuilder(const GmshReader::GmshData&
   }
 
   std::map<unsigned int, std::vector<unsigned int>> ref_cells_map;
-  for (unsigned int r = 0; r < gmsh_data.__triangles_ref.size(); ++r) {
-    const unsigned int elem_number = gmsh_data.__triangles_ref[r];
-    const unsigned int& ref        = gmsh_data.__triangles_ref[r];
+  for (unsigned int j = 0; j < gmsh_data.__triangles_ref.size(); ++j) {
+    const unsigned int elem_number = j;
+    const unsigned int& ref        = gmsh_data.__triangles_ref[j];
     ref_cells_map[ref].push_back(elem_number);
   }
 
@@ -249,9 +264,20 @@ GmshConnectivityBuilder<3>::GmshConnectivityBuilder(const GmshReader::GmshData&
     descriptor.cell_number_vector[jh] = gmsh_data.__hexahedra_number[j];
   }
 
+  const size_t nb_prisms = gmsh_data.__prisms.size();
+  for (size_t j = 0; j < nb_prisms; ++j) {
+    const size_t jp = nb_tetrahedra + nb_hexahedra + j;
+    descriptor.cell_to_node_vector[jp].resize(6);
+    for (int r = 0; r < 6; ++r) {
+      descriptor.cell_to_node_vector[jp][r] = gmsh_data.__prisms[j][r];
+    }
+    descriptor.cell_type_vector[jp]   = CellType::Prism;
+    descriptor.cell_number_vector[jp] = gmsh_data.__prisms_number[j];
+  }
+
   const size_t nb_pyramids = gmsh_data.__pyramids.size();
   for (size_t j = 0; j < nb_pyramids; ++j) {
-    const size_t jh = nb_tetrahedra + nb_hexahedra + j;
+    const size_t jh = nb_tetrahedra + nb_hexahedra + nb_prisms + j;
     descriptor.cell_to_node_vector[jh].resize(5);
     for (int r = 0; r < 5; ++r) {
       descriptor.cell_to_node_vector[jh][r] = gmsh_data.__pyramids[j][r];
@@ -261,9 +287,9 @@ GmshConnectivityBuilder<3>::GmshConnectivityBuilder(const GmshReader::GmshData&
   }
 
   std::map<unsigned int, std::vector<unsigned int>> ref_cells_map;
-  for (unsigned int r = 0; r < gmsh_data.__tetrahedra_ref.size(); ++r) {
-    const unsigned int elem_number = gmsh_data.__tetrahedra_ref[r];
-    const unsigned int& ref        = gmsh_data.__tetrahedra_ref[r];
+  for (unsigned int j = 0; j < gmsh_data.__tetrahedra_ref.size(); ++j) {
+    const unsigned int elem_number = j;
+    const unsigned int& ref        = gmsh_data.__tetrahedra_ref[j];
     ref_cells_map[ref].push_back(elem_number);
   }
 
@@ -273,8 +299,14 @@ GmshConnectivityBuilder<3>::GmshConnectivityBuilder(const GmshReader::GmshData&
     ref_cells_map[ref].push_back(elem_number);
   }
 
-  for (unsigned int j = 0; j < gmsh_data.__pyramids_ref.size(); ++j) {
+  for (unsigned int j = 0; j < gmsh_data.__prisms_ref.size(); ++j) {
     const size_t elem_number = nb_tetrahedra + nb_hexahedra + j;
+    const unsigned int& ref  = gmsh_data.__prisms_ref[j];
+    ref_cells_map[ref].push_back(elem_number);
+  }
+
+  for (unsigned int j = 0; j < gmsh_data.__pyramids_ref.size(); ++j) {
+    const size_t elem_number = nb_tetrahedra + nb_hexahedra + nb_prisms + j;
     const unsigned int& ref  = gmsh_data.__pyramids_ref[j];
     ref_cells_map[ref].push_back(elem_number);
   }
@@ -504,6 +536,8 @@ GmshReader::GmshReader(const std::string& filename) : m_filename(filename)
     __keywordList["$EndElements"]      = ENDELEMENTS;
     __keywordList["$PhysicalNames"]    = PHYSICALNAMES;
     __keywordList["$EndPhysicalNames"] = ENDPHYSICALNAMES;
+    __keywordList["$Periodic"]         = PERIODIC;
+    __keywordList["$EndPeriodic"]      = ENDPERIODIC;
 
     __numberOfPrimitiveNodes.resize(16);
     __numberOfPrimitiveNodes[0]  = 2;    // edge
@@ -533,7 +567,7 @@ GmshReader::GmshReader(const std::string& filename) : m_filename(filename)
     __primitivesNames[4]      = "hexahedra";
     __supportedPrimitives[4]  = true;
     __primitivesNames[5]      = "prisms";
-    __supportedPrimitives[5]  = false;
+    __supportedPrimitives[5]  = true;
     __primitivesNames[6]      = "pyramids";
     __supportedPrimitives[6]  = true;
     __primitivesNames[7]      = "second order edges";
@@ -719,6 +753,24 @@ GmshReader::__readPhysicalNames2_2()
   }
 }
 
+void
+GmshReader::__readPeriodic2_2()
+{
+  // This is just a compatibility reading, periodic information is not
+  // used
+  const int number_of_periodic = this->_getInteger();
+  for (int i = 0; i < number_of_periodic; ++i) {
+    [[maybe_unused]] const int dimension              = this->_getInteger();
+    [[maybe_unused]] const int id                     = this->_getInteger();
+    [[maybe_unused]] const int master_id              = this->_getInteger();
+    [[maybe_unused]] const int nb_corresponding_nodes = this->_getInteger();
+    for (int i_node = 0; i_node < nb_corresponding_nodes; ++i_node) {
+      [[maybe_unused]] const int node_id   = this->_getInteger();
+      [[maybe_unused]] const int master_id = this->_getInteger();
+    }
+  }
+}
+
 // std::shared_ptr<IConnectivity>
 // GmshReader::_buildConnectivity3D(const size_t nb_cells)
 // {
@@ -1027,6 +1079,12 @@ GmshReader::__proceedData()
           m_mesh_data.__hexahedra_number.resize(elementNumber[i]);
           break;
         }
+        case 5: {   // prism
+          m_mesh_data.__prisms = Array<GmshData::Prism>(elementNumber[i]);
+          m_mesh_data.__prisms_ref.resize(elementNumber[i]);
+          m_mesh_data.__prisms_number.resize(elementNumber[i]);
+          break;
+        }
         case 6: {   // pyramid
           m_mesh_data.__pyramids = Array<GmshData::Pyramid>(elementNumber[i]);
           m_mesh_data.__pyramids_ref.resize(elementNumber[i]);
@@ -1040,7 +1098,6 @@ GmshReader::__proceedData()
           break;
         }
           // Unsupported entities
-        case 5:    // prism
         case 7:    // second order edge
         case 8:    // second order triangle
         case 9:    // second order quadrangle
@@ -1170,6 +1227,24 @@ GmshReader::__proceedData()
       hexahedronNumber++;   // one more hexahedron
       break;
     }
+    case 5: {   // prism
+      int& prism_number = elementNumber[5];
+      const int a       = m_mesh_data.__verticesCorrepondance[elementVertices[0]];
+      const int b       = m_mesh_data.__verticesCorrepondance[elementVertices[1]];
+      const int c       = m_mesh_data.__verticesCorrepondance[elementVertices[2]];
+      const int d       = m_mesh_data.__verticesCorrepondance[elementVertices[3]];
+      const int e       = m_mesh_data.__verticesCorrepondance[elementVertices[4]];
+      const int f       = m_mesh_data.__verticesCorrepondance[elementVertices[5]];
+      if ((a < 0) or (b < 0) or (c < 0) or (d < 0) or (e < 0) or (f < 0)) {
+        throw NormalError("reading file '" + m_filename + "': error reading element " + std::to_string(i) +
+                          " [bad vertices definition]");
+      }
+      m_mesh_data.__prisms[prism_number]        = GmshData::Prism(a, b, c, d, e, f);
+      m_mesh_data.__prisms_ref[prism_number]    = m_mesh_data.__references[i];
+      m_mesh_data.__prisms_number[prism_number] = m_mesh_data.__elementNumber[i];
+      prism_number++;   // one more prism
+      break;
+    }
     case 6: {   // pyramid
       int& pyramid_number = elementNumber[6];
 
@@ -1182,7 +1257,7 @@ GmshReader::__proceedData()
         throw NormalError("reading file '" + m_filename + "': error reading element " + std::to_string(i) +
                           " [bad vertices definition]");
       }
-      m_mesh_data.__pyramids[pyramid_number]        = GmshData::Pyramid(d, c, b, a, e);
+      m_mesh_data.__pyramids[pyramid_number]        = GmshData::Pyramid(a, b, c, d, e);
       m_mesh_data.__pyramids_ref[pyramid_number]    = m_mesh_data.__references[i];
       m_mesh_data.__pyramids_number[pyramid_number] = m_mesh_data.__elementNumber[i];
       pyramid_number++;   // one more hexahedron
@@ -1198,7 +1273,6 @@ GmshReader::__proceedData()
       point_number++;
       break;
     }
-    case 5:      // prism
     case 7:      // second order edge
     case 8:      // second order triangle
     case 9:      // second order quadrangle
@@ -1353,7 +1427,15 @@ GmshReader::__readGmshFormat2_2()
     case PHYSICALNAMES: {
       this->__readPhysicalNames2_2();
       if (this->__nextKeyword().second != ENDPHYSICALNAMES) {
-        throw NormalError("reading file '" + m_filename + "': expecting $EndNodes, '" + kw.first + "' was found");
+        throw NormalError("reading file '" + m_filename + "': expecting $EndPhysicalNames, '" + kw.first +
+                          "' was found");
+      }
+      break;
+    }
+    case PERIODIC: {
+      this->__readPeriodic2_2();
+      if (this->__nextKeyword().second != ENDPERIODIC) {
+        throw NormalError("reading file '" + m_filename + "': expecting $EndPeriodic, '" + kw.first + "' was found");
       }
       break;
     }
diff --git a/src/mesh/GmshReader.hpp b/src/mesh/GmshReader.hpp
index 36e21508f10eda492ec1128816215d5a4d8c624d..860695ef57143b1b67575f54da95c12f8c7c2a01 100644
--- a/src/mesh/GmshReader.hpp
+++ b/src/mesh/GmshReader.hpp
@@ -109,6 +109,11 @@ class GmshReader : public MeshBuilderBase
     std::vector<int> __pyramids_ref;
     std::vector<int> __pyramids_number;
 
+    using Prism = TinyVector<6, unsigned int>;
+    Array<Prism> __prisms;
+    std::vector<int> __prisms_ref;
+    std::vector<int> __prisms_number;
+
     using Hexahedron = TinyVector<8, unsigned int>;
     Array<Hexahedron> __hexahedra;
     std::vector<int> __hexahedra_ref;
@@ -212,6 +217,8 @@ class GmshReader : public MeshBuilderBase
     ENDELEMENTS,
     PHYSICALNAMES,
     ENDPHYSICALNAMES,
+    PERIODIC,
+    ENDPERIODIC,
 
     Unknown,
     EndOfFile
@@ -268,6 +275,12 @@ class GmshReader : public MeshBuilderBase
    */
   void __readPhysicalNames2_2();
 
+  /**
+   * Reads periodic
+   *
+   */
+  void __readPeriodic2_2();
+
  public:
   GmshReader(const std::string& filename);
   ~GmshReader() = default;
diff --git a/src/mesh/IConnectivity.cpp b/src/mesh/IConnectivity.cpp
index 48cf57d55ebceb7818f755fd9214807a247f6575..bc83c0298d57c99bdcde1d5f7f560ba77a9d05df 100644
--- a/src/mesh/IConnectivity.cpp
+++ b/src/mesh/IConnectivity.cpp
@@ -1,10 +1,10 @@
 #include <mesh/IConnectivity.hpp>
 
-#include <mesh/DiamondDualConnectivityManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
 #include <mesh/SynchronizerManager.hpp>
 
 IConnectivity::~IConnectivity()
 {
   SynchronizerManager::instance().deleteConnectivitySynchronizer(this);
-  DiamondDualConnectivityManager::instance().deleteConnectivity(this);
+  DualConnectivityManager::instance().deleteConnectivity(this);
 }
diff --git a/src/mesh/IMesh.cpp b/src/mesh/IMesh.cpp
index f315f4b74b01eb371c42eb4bf029f81e93ae6016..e2a1739a8c6a928c0f29d6ef321d7c722f43ff65 100644
--- a/src/mesh/IMesh.cpp
+++ b/src/mesh/IMesh.cpp
@@ -1,10 +1,10 @@
 #include <mesh/IMesh.hpp>
 
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/MeshDataManager.hpp>
 
 IMesh::~IMesh()
 {
   MeshDataManager::instance().deleteMeshData(this);
-  DiamondDualMeshManager::instance().deleteMesh(this);
+  DualMeshManager::instance().deleteMesh(this);
 }
diff --git a/src/mesh/IPrimalToDualConnectivityDataMapper.hpp b/src/mesh/IPrimalToDualConnectivityDataMapper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..56bf51733c0105694c01a24ffb25fa01ccf973c6
--- /dev/null
+++ b/src/mesh/IPrimalToDualConnectivityDataMapper.hpp
@@ -0,0 +1,14 @@
+#ifndef I_PRIMAL_TO_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
+#define I_PRIMAL_TO_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
+
+class IPrimalToDualConnectivityDataMapper
+{
+ public:
+  IPrimalToDualConnectivityDataMapper(const IPrimalToDualConnectivityDataMapper&) = delete;
+  IPrimalToDualConnectivityDataMapper(IPrimalToDualConnectivityDataMapper&&)      = delete;
+
+  IPrimalToDualConnectivityDataMapper()          = default;
+  virtual ~IPrimalToDualConnectivityDataMapper() = default;
+};
+
+#endif   // I_PRIMAL_TO_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
diff --git a/src/mesh/ItemArray.hpp b/src/mesh/ItemArray.hpp
index 5a16861a37b5642ba10977d8c9e2d5ecd25fd194..3bd7de633659e81eb8fc178f275ee76a449048cf 100644
--- a/src/mesh/ItemArray.hpp
+++ b/src/mesh/ItemArray.hpp
@@ -36,6 +36,12 @@ class ItemArray
   // Allow const std:weak_ptr version to access our data
   friend ItemArray<std::add_const_t<DataType>, item_type, ConnectivityWeakPtr>;
 
+  // Allow non-const std:shared_ptr version to access our data
+  friend ItemArray<std::remove_const_t<DataType>, item_type, ConnectivitySharedPtr>;
+
+  // Allow non-const std:weak_ptr version to access our data
+  friend ItemArray<std::remove_const_t<DataType>, item_type, ConnectivityWeakPtr>;
+
  public:
   friend PUGS_INLINE ItemArray<std::remove_const_t<DataType>, item_type, ConnectivityPtr>
   copy(const ItemArray<DataType, item_type, ConnectivityPtr>& source)
diff --git a/src/mesh/ItemIdToItemIdMap.hpp b/src/mesh/ItemIdToItemIdMap.hpp
index 0e92eb13ce3943c4e2487081ee2e48fc3deac8ee..b2736ba6c3eb3da17dfc3ccf810e5eb5167b0704 100644
--- a/src/mesh/ItemIdToItemIdMap.hpp
+++ b/src/mesh/ItemIdToItemIdMap.hpp
@@ -7,6 +7,9 @@
 template <ItemType type1, ItemType type2>
 using ItemIdToItemIdMap = Array<std::pair<ItemIdT<type1>, ItemIdT<type2>>>;
 
+template <ItemType type1, ItemType type2>
+using ConstItemIdToItemIdMap = Array<const std::pair<ItemIdT<type1>, ItemIdT<type2>>>;
+
 using NodeIdToNodeIdMap = ItemIdToItemIdMap<ItemType::node, ItemType::node>;
 using NodeIdToEdgeIdMap = ItemIdToItemIdMap<ItemType::node, ItemType::edge>;
 using NodeIdToFaceIdMap = ItemIdToItemIdMap<ItemType::node, ItemType::face>;
@@ -27,4 +30,24 @@ using CellIdToEdgeIdMap = ItemIdToItemIdMap<ItemType::cell, ItemType::edge>;
 using CellIdToFaceIdMap = ItemIdToItemIdMap<ItemType::cell, ItemType::face>;
 using CellIdToCellIdMap = ItemIdToItemIdMap<ItemType::cell, ItemType::cell>;
 
+using ConstNodeIdToNodeIdMap = ConstItemIdToItemIdMap<ItemType::node, ItemType::node>;
+using ConstNodeIdToEdgeIdMap = ConstItemIdToItemIdMap<ItemType::node, ItemType::edge>;
+using ConstNodeIdToFaceIdMap = ConstItemIdToItemIdMap<ItemType::node, ItemType::face>;
+using ConstNodeIdToCellIdMap = ConstItemIdToItemIdMap<ItemType::node, ItemType::cell>;
+
+using ConstEdgeIdToNodeIdMap = ConstItemIdToItemIdMap<ItemType::edge, ItemType::node>;
+using ConstEdgeIdToEdgeIdMap = ConstItemIdToItemIdMap<ItemType::edge, ItemType::edge>;
+using ConstEdgeIdToFaceIdMap = ConstItemIdToItemIdMap<ItemType::edge, ItemType::face>;
+using ConstEdgeIdToCellIdMap = ConstItemIdToItemIdMap<ItemType::edge, ItemType::cell>;
+
+using ConstFaceIdToNodeIdMap = ConstItemIdToItemIdMap<ItemType::face, ItemType::node>;
+using ConstFaceIdToEdgeIdMap = ConstItemIdToItemIdMap<ItemType::face, ItemType::edge>;
+using ConstFaceIdToFaceIdMap = ConstItemIdToItemIdMap<ItemType::face, ItemType::face>;
+using ConstFaceIdToCellIdMap = ConstItemIdToItemIdMap<ItemType::face, ItemType::cell>;
+
+using ConstCellIdToNodeIdMap = ConstItemIdToItemIdMap<ItemType::cell, ItemType::node>;
+using ConstCellIdToEdgeIdMap = ConstItemIdToItemIdMap<ItemType::cell, ItemType::edge>;
+using ConstCellIdToFaceIdMap = ConstItemIdToItemIdMap<ItemType::cell, ItemType::face>;
+using ConstCellIdToCellIdMap = ConstItemIdToItemIdMap<ItemType::cell, ItemType::cell>;
+
 #endif   // ITEM_ID_TO_ITEM_ID_MAP_HPP
diff --git a/src/mesh/ItemType.hpp b/src/mesh/ItemType.hpp
index a63c5e76a31de169f84e2e89eda36aac540b19d3..c2f2da131e417e931cce8d3c40d91fd83ad19d45 100644
--- a/src/mesh/ItemType.hpp
+++ b/src/mesh/ItemType.hpp
@@ -71,6 +71,9 @@ struct ItemTypeId<1>
   }
 };
 
+template <ItemType item_type>
+inline constexpr bool is_node_in_1d_v = ItemTypeId<1>::itemTId(item_type) == ItemTypeId<1>::itemTId(ItemType::node);
+
 template <>
 struct ItemTypeId<2>
 {
@@ -99,6 +102,9 @@ struct ItemTypeId<2>
   }
 };
 
+template <ItemType item_type>
+inline constexpr bool is_face_in_2d_v = ItemTypeId<2>::itemTId(item_type) == ItemTypeId<2>::itemTId(ItemType::face);
+
 template <>
 struct ItemTypeId<3>
 {
diff --git a/src/mesh/ItemValue.hpp b/src/mesh/ItemValue.hpp
index ebc5f40511a0be6d1eeb519a0660b494d268f150..981ebcc196d4d637d9e775231fe14edc8a70a2f2 100644
--- a/src/mesh/ItemValue.hpp
+++ b/src/mesh/ItemValue.hpp
@@ -36,6 +36,12 @@ class ItemValue
   // Allow const std:weak_ptr version to access our data
   friend ItemValue<std::add_const_t<DataType>, item_type, ConnectivityWeakPtr>;
 
+  // Allow non-const std:shared_ptr version to access our data
+  friend ItemValue<std::remove_const_t<DataType>, item_type, ConnectivitySharedPtr>;
+
+  // Allow non-const std:weak_ptr version to access our data
+  friend ItemValue<std::remove_const_t<DataType>, item_type, ConnectivityWeakPtr>;
+
  public:
   friend PUGS_INLINE ItemValue<std::remove_const_t<DataType>, item_type, ConnectivityPtr>
   copy(const ItemValue<DataType, item_type, ConnectivityPtr>& source)
@@ -175,6 +181,13 @@ class ItemValue
   ~ItemValue() = default;
 };
 
+template <typename DataType, ItemType item_type, typename ConnectivityPtr>
+PUGS_INLINE size_t
+size(const ItemValue<DataType, item_type, ConnectivityPtr>& item_value)
+{
+  return item_value.numberOfItems();
+}
+
 template <typename DataType>
 using NodeValue = ItemValue<DataType, ItemType::node>;
 
diff --git a/src/mesh/LogicalConnectivityBuilder.cpp b/src/mesh/LogicalConnectivityBuilder.cpp
index bfeddbeca5471469ce5dee98382464a7cedc9746..db14ff1df24cede3afcc7d77e96950b185274546 100644
--- a/src/mesh/LogicalConnectivityBuilder.cpp
+++ b/src/mesh/LogicalConnectivityBuilder.cpp
@@ -42,31 +42,29 @@ LogicalConnectivityBuilder::_buildBoundaryNodeList(
 {
   const TinyVector<2, uint64_t> node_size{cell_size[0] + 1, cell_size[1] + 1};
 
-  const auto node_number = [&](const TinyVector<2, uint64_t> node_logic_id) {
-    return node_logic_id[0] * node_size[1] + node_logic_id[1];
-  };
+  const auto node_number = [&](const uint64_t i, const uint64_t j) { return i * node_size[1] + j; };
 
   {   // xminymin
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({0, 0});
+    boundary_nodes[0] = node_number(0, 0);
     descriptor.addRefItemList(RefNodeList{RefId{10, "XMINYMIN"}, boundary_nodes});
   }
 
   {   // xmaxymin
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({cell_size[0], 0});
+    boundary_nodes[0] = node_number(cell_size[0], 0);
     descriptor.addRefItemList(RefNodeList{RefId{11, "XMAXYMIN"}, boundary_nodes});
   }
 
   {   // xmaxymax
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({cell_size[0], cell_size[1]});
+    boundary_nodes[0] = node_number(cell_size[0], cell_size[1]);
     descriptor.addRefItemList(RefNodeList{RefId{12, "XMAXYMAX"}, boundary_nodes});
   }
 
   {   // xminymax
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({0, cell_size[1]});
+    boundary_nodes[0] = node_number(0, cell_size[1]);
     descriptor.addRefItemList(RefNodeList{RefId{13, "XMINYMAX"}, boundary_nodes});
   }
 }
@@ -78,55 +76,55 @@ LogicalConnectivityBuilder::_buildBoundaryNodeList(const TinyVector<3, uint64_t>
 {
   const TinyVector<3, uint64_t> node_size{cell_size[0] + 1, cell_size[1] + 1, cell_size[2] + 1};
 
-  const auto node_number = [&](const TinyVector<3, uint64_t>& node_logic_id) {
-    return (node_logic_id[0] * node_size[1] + node_logic_id[1]) * node_size[2] + node_logic_id[2];
+  const auto node_number = [&](const uint64_t i, const uint64_t j, const uint64_t k) {
+    return (i * node_size[1] + j) * node_size[2] + k;
   };
 
   {   // xminyminzmin
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({0, 0, 0});
+    boundary_nodes[0] = node_number(0, 0, 0);
     descriptor.addRefItemList(RefNodeList{RefId{10, "XMINYMINZMIN"}, boundary_nodes});
   }
 
   {   // xmaxyminzmin
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({cell_size[0], 0, 0});
+    boundary_nodes[0] = node_number(cell_size[0], 0, 0);
     descriptor.addRefItemList(RefNodeList{RefId{11, "XMAXYMINZMIN"}, boundary_nodes});
   }
 
   {   // xmaxymaxzmin
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({cell_size[0], cell_size[1], 0});
+    boundary_nodes[0] = node_number(cell_size[0], cell_size[1], 0);
     descriptor.addRefItemList(RefNodeList{RefId{12, "XMAXYMAXZMIN"}, boundary_nodes});
   }
 
   {   // xminymaxzmin
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({0, cell_size[1], 0});
+    boundary_nodes[0] = node_number(0, cell_size[1], 0);
     descriptor.addRefItemList(RefNodeList{RefId{13, "XMINYMAXZMIN"}, boundary_nodes});
   }
 
   {   // xminyminzmax
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({0, 0, cell_size[2]});
+    boundary_nodes[0] = node_number(0, 0, cell_size[2]);
     descriptor.addRefItemList(RefNodeList{RefId{14, "XMINYMINZMAX"}, boundary_nodes});
   }
 
   {   // xmaxyminzmax
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({cell_size[0], 0, cell_size[2]});
+    boundary_nodes[0] = node_number(cell_size[0], 0, cell_size[2]);
     descriptor.addRefItemList(RefNodeList{RefId{15, "XMAXYMINZMAX"}, boundary_nodes});
   }
 
   {   // xmaxymaxzmax
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({cell_size[0], cell_size[1], cell_size[2]});
+    boundary_nodes[0] = node_number(cell_size[0], cell_size[1], cell_size[2]);
     descriptor.addRefItemList(RefNodeList{RefId{16, "XMAXYMAXZMAX"}, boundary_nodes});
   }
 
   {   // xminymaxzmax
     Array<NodeId> boundary_nodes(1);
-    boundary_nodes[0] = node_number({0, cell_size[1], cell_size[2]});
+    boundary_nodes[0] = node_number(0, cell_size[1], cell_size[2]);
     descriptor.addRefItemList(RefNodeList{RefId{17, "XMINYMAXZMAX"}, boundary_nodes});
   }
 }
diff --git a/src/mesh/MedianDualConnectivityBuilder.cpp b/src/mesh/MedianDualConnectivityBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ed951bc374c4f639b89e20e56264f838dfcf625
--- /dev/null
+++ b/src/mesh/MedianDualConnectivityBuilder.cpp
@@ -0,0 +1,422 @@
+#include <mesh/MedianDualConnectivityBuilder.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ConnectivityDescriptor.hpp>
+#include <mesh/ConnectivityDispatcher.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/PrimalToMedianDualConnectivityDataMapper.hpp>
+#include <mesh/RefId.hpp>
+#include <utils/Array.hpp>
+#include <utils/Messenger.hpp>
+
+#include <vector>
+
+template <>
+void
+MedianDualConnectivityBuilder::_buildConnectivityDescriptor<2>(const Connectivity<2>& primal_connectivity,
+                                                               ConnectivityDescriptor& dual_descriptor)
+{
+  const size_t primal_number_of_nodes = primal_connectivity.numberOfNodes();
+  const size_t primal_number_of_faces = primal_connectivity.numberOfFaces();
+  const size_t primal_number_of_cells = primal_connectivity.numberOfCells();
+
+  const auto& primal_node_number = primal_connectivity.nodeNumber();
+  const auto& primal_face_number = primal_connectivity.faceNumber();
+  const auto& primal_cell_number = primal_connectivity.cellNumber();
+
+  const size_t primal_number_of_boundary_faces = [&] {
+    size_t number_of_boundary_faces        = 0;
+    const auto& primal_face_is_owned       = primal_connectivity.faceIsOwned();
+    const auto& primal_face_to_cell_matrix = primal_connectivity.faceToCellMatrix();
+
+    parallel_reduce(
+      primal_number_of_faces,
+      PUGS_LAMBDA(const FaceId face_id, size_t& number_of_boundary_faces) {
+        number_of_boundary_faces +=
+          (primal_face_is_owned[face_id] and (primal_face_to_cell_matrix[face_id].size() == 1));
+      },
+      number_of_boundary_faces);
+    return number_of_boundary_faces;
+  }();
+
+  const size_t primal_number_of_boundary_nodes = primal_number_of_boundary_faces;
+
+  const size_t dual_number_of_nodes = primal_number_of_cells + primal_number_of_faces + primal_number_of_boundary_nodes;
+  const size_t dual_number_of_cells = primal_number_of_nodes;
+
+  {
+    m_primal_node_to_dual_cell_map = NodeIdToCellIdMap{primal_number_of_nodes};
+    CellId dual_cell_id            = 0;
+    for (NodeId primal_node_id = 0; primal_node_id < primal_number_of_nodes; ++primal_node_id) {
+      m_primal_node_to_dual_cell_map[primal_node_id] = std::make_pair(primal_node_id, dual_cell_id++);
+    }
+  }
+
+  NodeValue<NodeId> node_to_dual_node_correpondance{primal_connectivity};
+  node_to_dual_node_correpondance.fill(std::numeric_limits<NodeId>::max());
+
+  {
+    NodeId dual_node_id = 0;
+
+    m_primal_cell_to_dual_node_map = CellIdToNodeIdMap{primal_number_of_cells};
+    for (CellId primal_cell_id = 0; primal_cell_id < primal_number_of_cells; ++primal_cell_id) {
+      m_primal_cell_to_dual_node_map[primal_cell_id] = std::make_pair(primal_cell_id, dual_node_id++);
+    }
+
+    m_primal_face_to_dual_node_map = FaceIdToNodeIdMap{primal_number_of_faces};
+    for (FaceId primal_face_id = 0; primal_face_id < primal_number_of_faces; ++primal_face_id) {
+      m_primal_face_to_dual_node_map[primal_face_id] = std::make_pair(primal_face_id, dual_node_id++);
+    }
+
+    const auto& primal_face_is_owned       = primal_connectivity.faceIsOwned();
+    const auto& primal_face_to_cell_matrix = primal_connectivity.faceToCellMatrix();
+    const auto& primal_face_to_node_matrix = primal_connectivity.faceToNodeMatrix();
+
+    m_primal_boundary_node_to_dual_node_map = NodeIdToNodeIdMap{primal_number_of_boundary_nodes};
+    m_primal_boundary_node_to_dual_node_map.fill(std::make_pair(1234, 5678));
+    size_t i_boundary_node = 0;
+    for (FaceId primal_face_id = 0; primal_face_id < primal_number_of_faces; ++primal_face_id) {
+      if (primal_face_is_owned[primal_face_id] and (primal_face_to_cell_matrix[primal_face_id].size() == 1)) {
+        const auto& primal_face_to_node_list = primal_face_to_node_matrix[primal_face_id];
+        for (size_t i_face_node = 0; i_face_node < primal_face_to_node_list.size(); ++i_face_node) {
+          const NodeId node_id = primal_face_to_node_list[i_face_node];
+          if (node_to_dual_node_correpondance[node_id] == std::numeric_limits<NodeId>::max()) {
+            node_to_dual_node_correpondance[node_id]                   = dual_node_id;
+            m_primal_boundary_node_to_dual_node_map[i_boundary_node++] = std::make_pair(node_id, dual_node_id++);
+          }
+        }
+      }
+    }
+    Assert(i_boundary_node == primal_number_of_boundary_nodes);
+  }
+
+  dual_descriptor.node_number_vector.resize(dual_number_of_nodes);
+  {
+    parallel_for(m_primal_cell_to_dual_node_map.size(), [&](size_t i) {
+      const auto [primal_cell_id, dual_node_id]        = m_primal_cell_to_dual_node_map[i];
+      dual_descriptor.node_number_vector[dual_node_id] = primal_cell_number[primal_cell_id];
+    });
+
+    const size_t face_number_shift = max(primal_cell_number) + 1;
+    parallel_for(primal_number_of_faces, [&](size_t i) {
+      const auto [primal_face_id, dual_node_id]        = m_primal_face_to_dual_node_map[i];
+      dual_descriptor.node_number_vector[dual_node_id] = primal_face_number[primal_face_id] + face_number_shift;
+    });
+
+    const size_t node_number_shift = face_number_shift + max(primal_face_number) + 1;
+    parallel_for(m_primal_boundary_node_to_dual_node_map.size(), [&](size_t i) {
+      const auto [primal_node_id, dual_node_id]        = m_primal_boundary_node_to_dual_node_map[i];
+      dual_descriptor.node_number_vector[dual_node_id] = primal_node_number[primal_node_id] + node_number_shift;
+    });
+  }
+
+  dual_descriptor.cell_number_vector.resize(dual_number_of_cells);
+  parallel_for(dual_number_of_cells, [&](size_t i) {
+    const auto [primal_node_id, dual_cell_id]        = m_primal_node_to_dual_cell_map[i];
+    dual_descriptor.cell_number_vector[dual_cell_id] = primal_node_number[primal_node_id];
+  });
+
+  dual_descriptor.cell_type_vector.resize(dual_number_of_cells);
+
+  const auto& primal_node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
+
+  parallel_for(primal_number_of_nodes, [&](NodeId node_id) {
+    const size_t i_dual_cell          = node_id;
+    const auto& primal_node_cell_list = primal_node_to_cell_matrix[node_id];
+
+    if (primal_node_cell_list.size() == 1) {
+      dual_descriptor.cell_type_vector[i_dual_cell] = CellType::Quadrangle;
+    } else {
+      dual_descriptor.cell_type_vector[i_dual_cell] = CellType::Polygon;
+    }
+  });
+
+  dual_descriptor.cell_to_node_vector.resize(dual_number_of_cells);
+  const auto& primal_cell_to_face_matrix               = primal_connectivity.cellToFaceMatrix();
+  const auto& primal_node_to_face_matrix               = primal_connectivity.nodeToFaceMatrix();
+  const auto& primal_face_to_cell_matrix               = primal_connectivity.faceToCellMatrix();
+  const auto& primal_face_to_node_matrix               = primal_connectivity.faceToNodeMatrix();
+  const auto& primal_cell_face_is_reversed             = primal_connectivity.cellFaceIsReversed();
+  const auto& primal_face_local_numbers_in_their_cells = primal_connectivity.faceLocalNumbersInTheirCells();
+
+  auto next_face = [&](const CellId cell_id, const FaceId face_id, const NodeId node_id) -> FaceId {
+    const auto& primal_cell_to_face_list = primal_cell_to_face_matrix[cell_id];
+    for (size_t i_face = 0; i_face < primal_cell_to_face_list.size(); ++i_face) {
+      const FaceId cell_face_id = primal_cell_to_face_list[i_face];
+      if (cell_face_id != face_id) {
+        const auto& face_node_list = primal_face_to_node_matrix[cell_face_id];
+        if ((face_node_list[0] == node_id) or (face_node_list[1] == node_id)) {
+          return cell_face_id;
+        }
+      }
+    }
+    // LCOV_EXCL_START
+    throw UnexpectedError("could not find next face");
+    // LCOV_EXCL_STOP
+  };
+
+  auto next_cell = [&](const CellId cell_id, const FaceId face_id) -> CellId {
+    const auto& primal_face_to_cell_list = primal_face_to_cell_matrix[face_id];
+    for (size_t i_cell = 0; i_cell < primal_face_to_cell_list.size(); ++i_cell) {
+      const CellId face_cell_id = primal_face_to_cell_list[i_cell];
+      if (face_cell_id != cell_id) {
+        return face_cell_id;
+      }
+    }
+    // LCOV_EXCL_START
+    throw UnexpectedError("could not find next face");
+    // LCOV_EXCL_STOP
+  };
+
+  parallel_for(primal_number_of_nodes, [&](NodeId node_id) {
+    const size_t i_dual_cell             = node_id;
+    const auto& primal_node_to_cell_list = primal_node_to_cell_matrix[node_id];
+    const auto& primal_node_to_face_list = primal_node_to_face_matrix[node_id];
+
+    auto& dual_cell_node_list = dual_descriptor.cell_to_node_vector[i_dual_cell];
+
+    if (primal_node_to_cell_list.size() != primal_node_to_face_list.size()) {
+      // boundary cell
+      dual_cell_node_list.reserve(1 + primal_node_to_cell_list.size() + primal_node_to_face_list.size());
+
+      auto [face_id, cell_id] = [&]() -> std::pair<FaceId, CellId> {
+        for (size_t i_face = 0; i_face < primal_node_to_face_list.size(); ++i_face) {
+          const FaceId face_id = primal_node_to_face_list[i_face];
+          if (primal_face_to_cell_matrix[face_id].size() > 1) {
+            continue;
+          }
+
+          const CellId cell_id        = primal_face_to_cell_matrix[face_id][0];
+          const size_t i_face_in_cell = primal_face_local_numbers_in_their_cells(face_id, 0);
+
+          if (primal_face_to_node_matrix[face_id][primal_cell_face_is_reversed(cell_id, i_face_in_cell)] == node_id) {
+            return std::make_pair(face_id, cell_id);
+          }
+        }
+        // LCOV_EXCL_START
+        throw UnexpectedError("cannot find first face");
+        // LCOV_EXCL_STOP
+      }();
+
+      dual_cell_node_list.push_back(m_primal_face_to_dual_node_map[face_id].second);
+      dual_cell_node_list.push_back(m_primal_cell_to_dual_node_map[cell_id].second);
+
+      face_id = next_face(cell_id, face_id, node_id);
+
+      while (primal_face_to_cell_matrix[face_id].size() > 1) {
+        dual_cell_node_list.push_back(m_primal_face_to_dual_node_map[face_id].second);
+        cell_id = next_cell(cell_id, face_id);
+        dual_cell_node_list.push_back(m_primal_cell_to_dual_node_map[cell_id].second);
+        face_id = next_face(cell_id, face_id, node_id);
+      }
+      dual_cell_node_list.push_back(m_primal_face_to_dual_node_map[face_id].second);
+      dual_cell_node_list.push_back(node_to_dual_node_correpondance[node_id]);
+
+      Assert(dual_cell_node_list.size() == 1 + primal_node_to_cell_list.size() + primal_node_to_face_list.size());
+    } else {
+      // inner cell
+      dual_cell_node_list.reserve(primal_node_to_cell_list.size() + primal_node_to_face_list.size());
+
+      auto [face_id, cell_id] = [&]() -> std::pair<FaceId, CellId> {
+        const FaceId face_id = primal_node_to_face_list[0];
+
+        for (size_t i_face_cell = 0; i_face_cell < primal_face_to_cell_matrix[face_id].size(); ++i_face_cell) {
+          const CellId cell_id        = primal_face_to_cell_matrix[face_id][i_face_cell];
+          const size_t i_face_in_cell = primal_face_local_numbers_in_their_cells(face_id, i_face_cell);
+
+          if (primal_face_to_node_matrix[face_id][primal_cell_face_is_reversed(cell_id, i_face_in_cell)] == node_id) {
+            return std::make_pair(face_id, cell_id);
+          }
+        }
+        // LCOV_EXCL_START
+        throw UnexpectedError("could not find first face/cell couple");
+        // LCOV_EXCL_STOP
+      }();
+
+      const FaceId first_face_id = face_id;
+      do {
+        dual_cell_node_list.push_back(m_primal_face_to_dual_node_map[face_id].second);
+        dual_cell_node_list.push_back(m_primal_cell_to_dual_node_map[cell_id].second);
+
+        face_id = next_face(cell_id, face_id, node_id);
+        cell_id = next_cell(cell_id, face_id);
+      } while (face_id != first_face_id);
+    }
+  });
+}
+
+template <>
+void
+MedianDualConnectivityBuilder::_buildConnectivityFrom<2>(const IConnectivity& i_primal_connectivity)
+{
+  using ConnectivityType = Connectivity<2>;
+
+  const ConnectivityType& primal_connectivity = dynamic_cast<const ConnectivityType&>(i_primal_connectivity);
+
+  ConnectivityDescriptor dual_descriptor;
+
+  this->_buildConnectivityDescriptor(primal_connectivity, dual_descriptor);
+
+  ConnectivityBuilderBase::_computeCellFaceAndFaceNodeConnectivities<2>(dual_descriptor);
+
+  {
+    const std::unordered_map<unsigned int, NodeId> primal_boundary_node_id_to_dual_node_id_map = [&] {
+      std::unordered_map<unsigned int, NodeId> node_to_id_map;
+      for (size_t i_node = 0; i_node < m_primal_boundary_node_to_dual_node_map.size(); ++i_node) {
+        auto [primal_node_id, dual_node_id] = m_primal_boundary_node_to_dual_node_map[i_node];
+        node_to_id_map[primal_node_id]      = dual_node_id;
+      }
+      return node_to_id_map;
+    }();
+
+    for (size_t i_node_list = 0; i_node_list < primal_connectivity.template numberOfRefItemList<ItemType::node>();
+         ++i_node_list) {
+      const auto& primal_ref_node_list = primal_connectivity.template refItemList<ItemType::node>(i_node_list);
+      const auto& primal_node_list     = primal_ref_node_list.list();
+
+      const std::vector<NodeId> dual_node_list = [&]() {
+        std::vector<NodeId> dual_node_list;
+
+        for (size_t i_primal_node = 0; i_primal_node < primal_node_list.size(); ++i_primal_node) {
+          auto primal_node_id = primal_node_list[i_primal_node];
+          const auto i_dual_node =
+            primal_boundary_node_id_to_dual_node_id_map.find(primal_connectivity.nodeNumber()[primal_node_id]);
+          if (i_dual_node != primal_boundary_node_id_to_dual_node_id_map.end()) {
+            dual_node_list.push_back(i_dual_node->second);
+          }
+        }
+
+        return dual_node_list;
+      }();
+
+      if (parallel::allReduceOr(dual_node_list.size() > 0)) {
+        dual_descriptor.addRefItemList(RefNodeList{primal_ref_node_list.refId(), convert_to_array(dual_node_list)});
+      }
+    }
+  }
+
+  using Face = ConnectivityFace<2>;
+
+  const std::unordered_map<Face, FaceId, typename Face::Hash> face_to_id_map = [&] {
+    std::unordered_map<Face, FaceId, typename Face::Hash> face_to_id_map;
+    for (FaceId l = 0; l < dual_descriptor.face_to_node_vector.size(); ++l) {
+      const auto& node_vector = dual_descriptor.face_to_node_vector[l];
+
+      face_to_id_map[Face(node_vector, dual_descriptor.node_number_vector)] = l;
+    }
+    return face_to_id_map;
+  }();
+
+  for (size_t i_face_list = 0; i_face_list < primal_connectivity.template numberOfRefItemList<ItemType::face>();
+       ++i_face_list) {
+    const auto& primal_ref_face_list = primal_connectivity.template refItemList<ItemType::face>(i_face_list);
+    const auto& primal_face_list     = primal_ref_face_list.list();
+
+    const std::vector<FaceId> boundary_dual_face_id_list = [&]() {
+      std::vector<NodeId> bounday_face_dual_node_id_list(primal_face_list.size());
+      for (size_t i_face = 0; i_face < primal_face_list.size(); ++i_face) {
+        bounday_face_dual_node_id_list[i_face] = m_primal_face_to_dual_node_map[primal_face_list[i_face]].second;
+      }
+
+      std::vector<bool> is_dual_node_from_boundary_face(dual_descriptor.node_number_vector.size(), false);
+      for (size_t i_face = 0; i_face < bounday_face_dual_node_id_list.size(); ++i_face) {
+        is_dual_node_from_boundary_face[bounday_face_dual_node_id_list[i_face]] = true;
+      }
+
+      std::vector<bool> is_dual_node_from_boundary_node(dual_descriptor.node_number_vector.size(), false);
+      for (size_t i_node = 0; i_node < m_primal_boundary_node_to_dual_node_map.size(); ++i_node) {
+        is_dual_node_from_boundary_node[m_primal_boundary_node_to_dual_node_map[i_node].second] = true;
+      }
+
+      std::vector<FaceId> dual_face_list;
+      dual_face_list.reserve(2 * primal_face_list.size());
+      for (size_t i_dual_face = 0; i_dual_face < dual_descriptor.face_to_node_vector.size(); ++i_dual_face) {
+        const NodeId dual_node_0 = dual_descriptor.face_to_node_vector[i_dual_face][0];
+        const NodeId dual_node_1 = dual_descriptor.face_to_node_vector[i_dual_face][1];
+
+        if ((is_dual_node_from_boundary_face[dual_node_0] and is_dual_node_from_boundary_node[dual_node_1]) or
+            (is_dual_node_from_boundary_node[dual_node_0] and is_dual_node_from_boundary_face[dual_node_1])) {
+          dual_face_list.push_back(i_dual_face);
+        }
+      }
+      return dual_face_list;
+    }();
+
+    if (parallel::allReduceOr(boundary_dual_face_id_list.size() > 0)) {
+      dual_descriptor.addRefItemList(
+        RefFaceList{primal_ref_face_list.refId(), convert_to_array(boundary_dual_face_id_list)});
+    }
+  }
+
+  const size_t primal_number_of_nodes = primal_connectivity.numberOfNodes();
+  const size_t primal_number_of_cells = primal_connectivity.numberOfCells();
+
+  dual_descriptor.node_owner_vector.resize(dual_descriptor.node_number_vector.size());
+
+  const auto& primal_node_owner = primal_connectivity.nodeOwner();
+  for (NodeId primal_node_id = 0; primal_node_id < primal_connectivity.numberOfNodes(); ++primal_node_id) {
+    dual_descriptor.node_owner_vector[primal_node_id] = primal_node_owner[primal_node_id];
+  }
+  const auto& primal_cell_owner = primal_connectivity.cellOwner();
+  for (CellId primal_cell_id = 0; primal_cell_id < primal_number_of_cells; ++primal_cell_id) {
+    dual_descriptor.node_owner_vector[primal_number_of_nodes + primal_cell_id] = primal_cell_owner[primal_cell_id];
+  }
+
+  dual_descriptor.cell_owner_vector.resize(dual_descriptor.cell_number_vector.size());
+  for (NodeId primal_node_id = 0; primal_node_id < primal_number_of_nodes; ++primal_node_id) {
+    dual_descriptor.cell_owner_vector[primal_node_id] = primal_node_owner[primal_node_id];
+  }
+
+  {
+    std::vector<int> face_cell_owner(dual_descriptor.face_number_vector.size());
+    std::fill(std::begin(face_cell_owner), std::end(face_cell_owner), parallel::size());
+
+    for (size_t i_cell = 0; i_cell < dual_descriptor.cell_to_face_vector.size(); ++i_cell) {
+      const auto& cell_face_list = dual_descriptor.cell_to_face_vector[i_cell];
+      for (size_t i_face = 0; i_face < cell_face_list.size(); ++i_face) {
+        const size_t face_id     = cell_face_list[i_face];
+        face_cell_owner[face_id] = std::min(face_cell_owner[face_id], dual_descriptor.cell_number_vector[i_cell]);
+      }
+    }
+
+    dual_descriptor.face_owner_vector.resize(face_cell_owner.size());
+    for (size_t i_face = 0; i_face < face_cell_owner.size(); ++i_face) {
+      dual_descriptor.face_owner_vector[i_face] = dual_descriptor.cell_owner_vector[face_cell_owner[i_face]];
+    }
+  }
+
+  m_connectivity = ConnectivityType::build(dual_descriptor);
+
+  const ConnectivityType& dual_connectivity = dynamic_cast<const ConnectivityType&>(*m_connectivity);
+
+  m_mapper = std::make_shared<PrimalToMedianDualConnectivityDataMapper<2>>(primal_connectivity, dual_connectivity,
+                                                                           m_primal_boundary_node_to_dual_node_map,
+                                                                           m_primal_face_to_dual_node_map,
+                                                                           m_primal_cell_to_dual_node_map,
+                                                                           m_primal_node_to_dual_cell_map);
+}
+
+MedianDualConnectivityBuilder::MedianDualConnectivityBuilder(const IConnectivity& connectivity)
+{
+  // LCOV_EXCL_START
+  if (parallel::size() > 1) {
+    throw NotImplementedError("Construction of median dual mesh is not implemented in parallel");
+  }
+  // LCOV_EXCL_STOP
+  switch (connectivity.dimension()) {
+  case 2: {
+    this->_buildConnectivityFrom<2>(connectivity);
+    break;
+  }
+  case 3: {
+    throw NotImplementedError("median dual connectivity");
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("invalid connectivity dimension: " + std::to_string(connectivity.dimension()));
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/mesh/MedianDualConnectivityBuilder.hpp b/src/mesh/MedianDualConnectivityBuilder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..71efff92a83a86b143afd61ca86610a8396dab09
--- /dev/null
+++ b/src/mesh/MedianDualConnectivityBuilder.hpp
@@ -0,0 +1,43 @@
+#ifndef MEDIAN_DUAL_CONNECTIVITY_BUILDER_HPP
+#define MEDIAN_DUAL_CONNECTIVITY_BUILDER_HPP
+
+#include <mesh/ConnectivityBuilderBase.hpp>
+#include <mesh/IPrimalToDualConnectivityDataMapper.hpp>
+#include <mesh/ItemIdToItemIdMap.hpp>
+
+#include <memory>
+
+template <size_t>
+class Connectivity;
+class ConnectivityDescriptor;
+
+class MedianDualConnectivityBuilder : public ConnectivityBuilderBase
+{
+ private:
+  FaceIdToNodeIdMap m_primal_face_to_dual_node_map;
+  CellIdToNodeIdMap m_primal_cell_to_dual_node_map;
+  NodeIdToNodeIdMap m_primal_boundary_node_to_dual_node_map;
+  NodeIdToCellIdMap m_primal_node_to_dual_cell_map;
+
+  std::shared_ptr<IPrimalToDualConnectivityDataMapper> m_mapper;
+
+  template <size_t Dimension>
+  void _buildConnectivityDescriptor(const Connectivity<Dimension>&, ConnectivityDescriptor&);
+
+  template <size_t Dimension>
+  void _buildConnectivityFrom(const IConnectivity&);
+
+  friend class DualConnectivityManager;
+  MedianDualConnectivityBuilder(const IConnectivity&);
+
+ public:
+  std::shared_ptr<IPrimalToDualConnectivityDataMapper>
+  mapper() const
+  {
+    return m_mapper;
+  }
+
+  ~MedianDualConnectivityBuilder() = default;
+};
+
+#endif   // MEDIAN_DUAL_CONNECTIVITY_BUILDER_HPP
diff --git a/src/mesh/MedianDualMeshBuilder.cpp b/src/mesh/MedianDualMeshBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aae78311ed7ab548714f7f6d90a96196632dcbb9
--- /dev/null
+++ b/src/mesh/MedianDualMeshBuilder.cpp
@@ -0,0 +1,61 @@
+#include <mesh/MedianDualMeshBuilder.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+#include <mesh/PrimalToMedianDualConnectivityDataMapper.hpp>
+
+template <size_t Dimension>
+void
+MedianDualMeshBuilder::_buildMedianDualMeshFrom(const IMesh& i_mesh)
+{
+  static_assert(Dimension > 1);
+
+  using ConnectivityType = Connectivity<Dimension>;
+  using MeshType         = Mesh<Connectivity<Dimension>>;
+
+  const MeshType& primal_mesh = dynamic_cast<const MeshType&>(i_mesh);
+
+  DualConnectivityManager& manager = DualConnectivityManager::instance();
+
+  std::shared_ptr<const ConnectivityType> p_dual_connectivity =
+    manager.getMedianDualConnectivity(primal_mesh.connectivity());
+
+  const ConnectivityType& dual_connectivity = *p_dual_connectivity;
+
+  const NodeValue<const TinyVector<Dimension>> primal_xr = primal_mesh.xr();
+
+  MeshData<Dimension>& primal_mesh_data                  = MeshDataManager::instance().getMeshData(primal_mesh);
+  const CellValue<const TinyVector<Dimension>> primal_xj = primal_mesh_data.xj();
+  const FaceValue<const TinyVector<Dimension>> primal_xl = primal_mesh_data.xl();
+
+  std::shared_ptr primal_to_dual_connectivity_data_mapper =
+    manager.getPrimalToMedianDualConnectivityDataMapper(primal_mesh.connectivity());
+
+  NodeValue<TinyVector<Dimension>> dual_xr{dual_connectivity};
+  primal_to_dual_connectivity_data_mapper->toDualNode(primal_xr, primal_xl, primal_xj, dual_xr);
+
+  m_mesh = std::make_shared<MeshType>(p_dual_connectivity, dual_xr);
+}
+
+MedianDualMeshBuilder::MedianDualMeshBuilder(const IMesh& i_mesh)
+{
+  switch (i_mesh.dimension()) {
+  case 2: {
+    this->_buildMedianDualMeshFrom<2>(i_mesh);
+    break;
+  }
+  case 3: {
+    throw NotImplementedError("median dual mesh");
+    break;
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("invalid mesh dimension: " + std::to_string(i_mesh.dimension()));
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/mesh/MedianDualMeshBuilder.hpp b/src/mesh/MedianDualMeshBuilder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6f8f1842c83bd272dd685984eb6dd91f84b0da8c
--- /dev/null
+++ b/src/mesh/MedianDualMeshBuilder.hpp
@@ -0,0 +1,21 @@
+#ifndef MEDIAN_DUAL_MESH_BUILDER_HPP
+#define MEDIAN_DUAL_MESH_BUILDER_HPP
+
+#include <mesh/MeshBuilderBase.hpp>
+
+#include <memory>
+
+class MedianDualMeshBuilder : public MeshBuilderBase
+{
+ private:
+  template <size_t Dimension>
+  void _buildMedianDualMeshFrom(const IMesh&);
+
+  friend class DualMeshManager;
+  MedianDualMeshBuilder(const IMesh&);
+
+ public:
+  ~MedianDualMeshBuilder() = default;
+};
+
+#endif   // MEDIAN_DUAL_MESH_BUILDER_HPP
diff --git a/src/mesh/MeshData.hpp b/src/mesh/MeshData.hpp
index 4a3ab6939b02dffef76dbdda5e99273c6653bce9..596ab38bd3713686e3b69b96e7b854b170bca5e0 100644
--- a/src/mesh/MeshData.hpp
+++ b/src/mesh/MeshData.hpp
@@ -365,8 +365,8 @@ class MeshData : public IMeshData
       NodeValuePerCell<Rd> Cjr(m_mesh.connectivity());
       parallel_for(
         m_mesh.numberOfCells(), PUGS_LAMBDA(CellId j) {
-          Cjr(j, 0) = -1;
-          Cjr(j, 1) = 1;
+          Cjr(j, 0) = Rd{-1};
+          Cjr(j, 1) = Rd{1};
         });
       m_Cjr = Cjr;
     } else if constexpr (Dimension == 2) {
diff --git a/src/mesh/MeshNodeBoundary.cpp b/src/mesh/MeshNodeBoundary.cpp
index 6a54afb554c1d1454bd01934d2dd08f4c023d62f..56ee047545d51e20f11e7b0421dae839615bf2cc 100644
--- a/src/mesh/MeshNodeBoundary.cpp
+++ b/src/mesh/MeshNodeBoundary.cpp
@@ -189,27 +189,34 @@ MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>
     throw NormalError(ost.str());
   }
 
-  Kokkos::vector<unsigned int> node_ids;
-  // not enough but should reduce significantly the number of resizing
-  node_ids.reserve(Dimension * face_list.size());
-  const auto& face_to_node_matrix = mesh.connectivity().faceToNodeMatrix();
-
-  for (size_t l = 0; l < face_list.size(); ++l) {
-    const FaceId face_number = face_list[l];
-    const auto& face_nodes   = face_to_node_matrix[face_number];
-
-    for (size_t r = 0; r < face_nodes.size(); ++r) {
-      node_ids.push_back(face_nodes[r]);
+  if constexpr (Dimension > 1) {
+    Kokkos::vector<unsigned int> node_ids;
+    // not enough but should reduce significantly the number of resizing
+    node_ids.reserve(Dimension * face_list.size());
+    const auto& face_to_node_matrix = mesh.connectivity().faceToNodeMatrix();
+
+    for (size_t l = 0; l < face_list.size(); ++l) {
+      const FaceId face_number = face_list[l];
+      const auto& face_nodes   = face_to_node_matrix[face_number];
+
+      for (size_t r = 0; r < face_nodes.size(); ++r) {
+        node_ids.push_back(face_nodes[r]);
+      }
     }
+    std::sort(node_ids.begin(), node_ids.end());
+    auto last = std::unique(node_ids.begin(), node_ids.end());
+    node_ids.resize(std::distance(node_ids.begin(), last));
+
+    Array<NodeId> node_list(node_ids.size());
+    parallel_for(
+      node_ids.size(), PUGS_LAMBDA(int r) { node_list[r] = node_ids[r]; });
+    m_node_list = node_list;
+  } else {
+    Array<NodeId> node_list(face_list.size());
+    parallel_for(
+      face_list.size(), PUGS_LAMBDA(int r) { node_list[r] = static_cast<FaceId::base_type>(face_list[r]); });
+    m_node_list = node_list;
   }
-  std::sort(node_ids.begin(), node_ids.end());
-  auto last = std::unique(node_ids.begin(), node_ids.end());
-  node_ids.resize(std::distance(node_ids.begin(), last));
-
-  Array<NodeId> node_list(node_ids.size());
-  parallel_for(
-    node_ids.size(), PUGS_LAMBDA(int r) { node_list[r] = node_ids[r]; });
-  m_node_list = node_list;
 }
 
 template <size_t Dimension>
@@ -217,30 +224,43 @@ MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>
                                               const RefEdgeList& ref_edge_list)
   : m_boundary_name(ref_edge_list.refId().tagName())
 {
-  const auto& edge_to_face_matrix = mesh.connectivity().edgeToFaceMatrix();
-  const auto& face_to_cell_matrix = mesh.connectivity().faceToCellMatrix();
-
-  auto edge_is_owned = mesh.connectivity().edgeIsOwned();
-
   const Array<const EdgeId>& edge_list = ref_edge_list.list();
+  const auto& edge_is_owned            = mesh.connectivity().edgeIsOwned();
 
   bool is_bad = false;
-  parallel_for(edge_list.size(), [=, &is_bad](int l) {
-    const EdgeId edge_id = edge_list[l];
-    if (edge_is_owned[edge_id]) {
-      const auto& edge_faces             = edge_to_face_matrix[edge_id];
-      bool is_connected_to_boundary_face = false;
-      for (size_t i_edge_face = 0; i_edge_face < edge_faces.size(); ++i_edge_face) {
-        const FaceId edge_face_id = edge_faces[i_edge_face];
-        if (face_to_cell_matrix[edge_face_id].size() == 1) {
-          is_connected_to_boundary_face = true;
+
+  if constexpr ((Dimension == 1) or (Dimension == 2)) {
+    const auto& edge_to_cell_matrix = mesh.connectivity().edgeToCellMatrix();
+    parallel_for(edge_list.size(), [=, &is_bad](int l) {
+      const EdgeId edge_id = edge_list[l];
+      if (edge_is_owned[edge_id]) {
+        if (edge_to_cell_matrix[edge_id].size() != 1) {
+          is_bad = true;
         }
       }
-      if (not is_connected_to_boundary_face) {
-        is_bad = true;
+    });
+  } else {
+    static_assert(Dimension == 3);
+    const auto& edge_to_face_matrix = mesh.connectivity().edgeToFaceMatrix();
+    const auto& face_to_cell_matrix = mesh.connectivity().faceToCellMatrix();
+
+    parallel_for(edge_list.size(), [=, &is_bad](int l) {
+      const EdgeId edge_id = edge_list[l];
+      if (edge_is_owned[edge_id]) {
+        const auto& edge_faces             = edge_to_face_matrix[edge_id];
+        bool is_connected_to_boundary_face = false;
+        for (size_t i_edge_face = 0; i_edge_face < edge_faces.size(); ++i_edge_face) {
+          const FaceId edge_face_id = edge_faces[i_edge_face];
+          if (face_to_cell_matrix[edge_face_id].size() == 1) {
+            is_connected_to_boundary_face = true;
+          }
+        }
+        if (not is_connected_to_boundary_face) {
+          is_bad = true;
+        }
       }
-    }
-  });
+    });
+  }
 
   if (parallel::allReduceOr(is_bad)) {
     std::ostringstream ost;
@@ -249,26 +269,33 @@ MeshNodeBoundary<Dimension>::MeshNodeBoundary(const Mesh<Connectivity<Dimension>
     throw NormalError(ost.str());
   }
 
-  Kokkos::vector<unsigned int> node_ids;
-  node_ids.reserve(2 * edge_list.size());
-  const auto& edge_to_node_matrix = mesh.connectivity().edgeToNodeMatrix();
+  if constexpr (Dimension > 1) {
+    const auto& edge_to_node_matrix = mesh.connectivity().edgeToNodeMatrix();
+    Kokkos::vector<unsigned int> node_ids;
+    node_ids.reserve(2 * edge_list.size());
 
-  for (size_t l = 0; l < edge_list.size(); ++l) {
-    const EdgeId edge_number = edge_list[l];
-    const auto& edge_nodes   = edge_to_node_matrix[edge_number];
+    for (size_t l = 0; l < edge_list.size(); ++l) {
+      const EdgeId edge_number = edge_list[l];
+      const auto& edge_nodes   = edge_to_node_matrix[edge_number];
 
-    for (size_t r = 0; r < edge_nodes.size(); ++r) {
-      node_ids.push_back(edge_nodes[r]);
+      for (size_t r = 0; r < edge_nodes.size(); ++r) {
+        node_ids.push_back(edge_nodes[r]);
+      }
     }
+    std::sort(node_ids.begin(), node_ids.end());
+    auto last = std::unique(node_ids.begin(), node_ids.end());
+    node_ids.resize(std::distance(node_ids.begin(), last));
+
+    Array<NodeId> node_list(node_ids.size());
+    parallel_for(
+      node_ids.size(), PUGS_LAMBDA(int r) { node_list[r] = node_ids[r]; });
+    m_node_list = node_list;
+  } else {
+    Array<NodeId> node_list(edge_list.size());
+    parallel_for(
+      edge_list.size(), PUGS_LAMBDA(int r) { node_list[r] = static_cast<EdgeId::base_type>(edge_list[r]); });
+    m_node_list = node_list;
   }
-  std::sort(node_ids.begin(), node_ids.end());
-  auto last = std::unique(node_ids.begin(), node_ids.end());
-  node_ids.resize(std::distance(node_ids.begin(), last));
-
-  Array<NodeId> node_list(node_ids.size());
-  parallel_for(
-    node_ids.size(), PUGS_LAMBDA(int r) { node_list[r] = node_ids[r]; });
-  m_node_list = node_list;
 }
 
 template <size_t Dimension>
diff --git a/src/mesh/MeshRelaxer.cpp b/src/mesh/MeshRelaxer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bda3cc7d17680bf8e2b43596d2e9ad2c7d9759ed
--- /dev/null
+++ b/src/mesh/MeshRelaxer.cpp
@@ -0,0 +1,59 @@
+#include <mesh/MeshRelaxer.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+
+template <typename ConnectivityType>
+std::shared_ptr<const Mesh<ConnectivityType>>
+MeshRelaxer::_relax(const Mesh<ConnectivityType>& source_mesh,
+                    const Mesh<ConnectivityType>& destination_mesh,
+                    const double& theta) const
+{
+  if (source_mesh.shared_connectivity() == destination_mesh.shared_connectivity()) {
+    const ConnectivityType& connectivity = source_mesh.connectivity();
+    NodeValue<TinyVector<ConnectivityType::Dimension>> theta_xr{connectivity};
+    const NodeValue<const TinyVector<ConnectivityType::Dimension>> source_xr      = source_mesh.xr();
+    const NodeValue<const TinyVector<ConnectivityType::Dimension>> destination_xr = destination_mesh.xr();
+
+    const double one_minus_theta = 1 - theta;
+    parallel_for(
+      connectivity.numberOfNodes(), PUGS_LAMBDA(const NodeId node_id) {
+        theta_xr[node_id] = one_minus_theta * source_xr[node_id] + theta * destination_xr[node_id];
+      });
+
+    return std::make_shared<Mesh<ConnectivityType>>(source_mesh.shared_connectivity(), theta_xr);
+  } else {
+    throw NormalError("relaxed meshes must share the same connectivity");
+  }
+}
+
+std::shared_ptr<const IMesh>
+MeshRelaxer::relax(const std::shared_ptr<const IMesh>& p_source_mesh,
+                   const std::shared_ptr<const IMesh>& p_destination_mesh,
+                   const double& theta) const
+{
+  if (p_source_mesh->dimension() != p_destination_mesh->dimension()) {
+    throw NormalError("incompatible mesh dimensions");
+  } else {
+    switch (p_source_mesh->dimension()) {
+    case 1: {
+      using MeshType = Mesh<Connectivity<1>>;
+      return this->_relax(dynamic_cast<const MeshType&>(*p_source_mesh),
+                          dynamic_cast<const MeshType&>(*p_destination_mesh), theta);
+    }
+    case 2: {
+      using MeshType = Mesh<Connectivity<2>>;
+      return this->_relax(dynamic_cast<const MeshType&>(*p_source_mesh),
+                          dynamic_cast<const MeshType&>(*p_destination_mesh), theta);
+    }
+    case 3: {
+      using MeshType = Mesh<Connectivity<3>>;
+      return this->_relax(dynamic_cast<const MeshType&>(*p_source_mesh),
+                          dynamic_cast<const MeshType&>(*p_destination_mesh), theta);
+    }
+    default: {
+      throw UnexpectedError("invalid mesh dimension");
+    }
+    }
+  }
+}
diff --git a/src/mesh/MeshRelaxer.hpp b/src/mesh/MeshRelaxer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..96846cbadfefe61902199e927c99ecf96aaf176a
--- /dev/null
+++ b/src/mesh/MeshRelaxer.hpp
@@ -0,0 +1,27 @@
+#ifndef MESH_RELAXER_HPP
+#define MESH_RELAXER_HPP
+
+class IMesh;
+
+template <typename ConnectivityType>
+class Mesh;
+
+#include <memory>
+
+class MeshRelaxer
+{
+ private:
+  template <typename ConnectivityType>
+  std::shared_ptr<const Mesh<ConnectivityType>> _relax(const Mesh<ConnectivityType>& source_mesh,
+                                                       const Mesh<ConnectivityType>& destination_mesh,
+                                                       const double& theta) const;
+
+ public:
+  std::shared_ptr<const IMesh> relax(const std::shared_ptr<const IMesh>& p_source_mesh,
+                                     const std::shared_ptr<const IMesh>& p_destination_mesh,
+                                     const double& theta) const;
+  MeshRelaxer()  = default;
+  ~MeshRelaxer() = default;
+};
+
+#endif   // MESH_RELAXER_HPP
diff --git a/src/mesh/MeshTransformer.cpp b/src/mesh/MeshTransformer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..167c4f337cd5c48147594623f3ae436874a47a22
--- /dev/null
+++ b/src/mesh/MeshTransformer.cpp
@@ -0,0 +1,49 @@
+#include <mesh/MeshTransformer.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+
+#include <language/utils/EvaluateAtPoints.hpp>
+
+template <typename OutputType, typename InputType>
+class MeshTransformer::MeshTransformation<OutputType(InputType)>
+{
+  static constexpr size_t Dimension = OutputType::Dimension;
+
+ public:
+  static inline std::shared_ptr<Mesh<Connectivity<Dimension>>>
+  transform(const FunctionSymbolId& function_symbol_id, std::shared_ptr<const IMesh> p_mesh)
+  {
+    using MeshType             = Mesh<Connectivity<Dimension>>;
+    const MeshType& given_mesh = dynamic_cast<const MeshType&>(*p_mesh);
+
+    NodeValue<OutputType> xr(given_mesh.connectivity());
+    NodeValue<const InputType> given_xr = given_mesh.xr();
+    EvaluateAtPoints<OutputType(InputType)>::evaluateTo(function_symbol_id, given_xr, xr);
+
+    return std::make_shared<MeshType>(given_mesh.shared_connectivity(), xr);
+  }
+};
+
+std::shared_ptr<const IMesh>
+MeshTransformer::transform(const FunctionSymbolId& function_id, std::shared_ptr<const IMesh> p_mesh)
+
+{
+  switch (p_mesh->dimension()) {
+  case 1: {
+    using TransformT = TinyVector<1>(TinyVector<1>);
+    return MeshTransformation<TransformT>::transform(function_id, p_mesh);
+  }
+  case 2: {
+    using TransformT = TinyVector<2>(TinyVector<2>);
+    return MeshTransformation<TransformT>::transform(function_id, p_mesh);
+  }
+  case 3: {
+    using TransformT = TinyVector<3>(TinyVector<3>);
+    return MeshTransformation<TransformT>::transform(function_id, p_mesh);
+  }
+  default: {
+    throw UnexpectedError("invalid mesh dimension");
+  }
+  }
+}
diff --git a/src/mesh/MeshTransformer.hpp b/src/mesh/MeshTransformer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ce0a4d47eab0557004234d22ea5331a0d2b0f793
--- /dev/null
+++ b/src/mesh/MeshTransformer.hpp
@@ -0,0 +1,26 @@
+#ifndef MESH_TRANSFORMER_HPP
+#define MESH_TRANSFORMER_HPP
+
+class IMesh;
+
+template <typename ConnectivityType>
+class Mesh;
+
+class FunctionSymbolId;
+
+#include <memory>
+
+class MeshTransformer
+{
+  template <typename T>
+  class MeshTransformation;
+
+ public:
+  std::shared_ptr<const IMesh> transform(const FunctionSymbolId& function_symbol_id,
+                                         std::shared_ptr<const IMesh> p_mesh);
+
+  MeshTransformer()  = default;
+  ~MeshTransformer() = default;
+};
+
+#endif   // MESH_TRANSFORMER_HPP
diff --git a/src/mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp b/src/mesh/PrimalToDiamondDualConnectivityDataMapper.hpp
similarity index 61%
rename from src/mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp
rename to src/mesh/PrimalToDiamondDualConnectivityDataMapper.hpp
index 8d35d2ff9541fbfab7e8e6e30976bd5f88970f56..ce695a4d3eb4f261677c1e5f2d2ff191890efdc1 100644
--- a/src/mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp
+++ b/src/mesh/PrimalToDiamondDualConnectivityDataMapper.hpp
@@ -1,32 +1,26 @@
-#ifndef CONNECTIVITY_TO_DIAMOND_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
-#define CONNECTIVITY_TO_DIAMOND_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
+#ifndef PRIMAL_TO_DIAMOND_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
+#define PRIMAL_TO_DIAMOND_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
 
 #include <mesh/Connectivity.hpp>
+#include <mesh/IPrimalToDualConnectivityDataMapper.hpp>
 #include <mesh/ItemIdToItemIdMap.hpp>
 #include <mesh/ItemValue.hpp>
 #include <utils/Array.hpp>
 #include <utils/PugsAssert.hpp>
 
-class IConnectivityToDiamondDualConnectivityDataMapper
-{
- public:
-  IConnectivityToDiamondDualConnectivityDataMapper(const IConnectivityToDiamondDualConnectivityDataMapper&) = delete;
-  IConnectivityToDiamondDualConnectivityDataMapper(IConnectivityToDiamondDualConnectivityDataMapper&&)      = delete;
-
-  IConnectivityToDiamondDualConnectivityDataMapper()          = default;
-  virtual ~IConnectivityToDiamondDualConnectivityDataMapper() = default;
-};
-
 template <size_t Dimension>
-class ConnectivityToDiamondDualConnectivityDataMapper : public IConnectivityToDiamondDualConnectivityDataMapper
+class PrimalToDiamondDualConnectivityDataMapper : public IPrimalToDualConnectivityDataMapper
 {
+  static_assert(Dimension == 2 or Dimension == 3,
+                "primal to diamond dual connectivity mapper is defined in dimension 2 or 3");
+
  private:
   const IConnectivity* m_primal_connectivity;
   const IConnectivity* m_dual_connectivity;
 
-  NodeIdToNodeIdMap m_primal_node_to_dual_node_map;
-  CellIdToNodeIdMap m_primal_cell_to_dual_node_map;
-  FaceIdToCellIdMap m_primal_face_to_dual_cell_map;
+  ConstNodeIdToNodeIdMap m_primal_node_to_dual_node_map;
+  ConstCellIdToNodeIdMap m_primal_cell_to_dual_node_map;
+  ConstFaceIdToCellIdMap m_primal_face_to_dual_cell_map;
 
  public:
   template <typename OriginDataType1, typename OriginDataType2, typename DestinationDataType>
@@ -39,9 +33,12 @@ class ConnectivityToDiamondDualConnectivityDataMapper : public IConnectivityToDi
     static_assert(std::is_same_v<std::remove_const_t<OriginDataType1>, DestinationDataType>, "incompatible types");
     static_assert(std::is_same_v<std::remove_const_t<OriginDataType2>, DestinationDataType>, "incompatible types");
 
-    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get());
-    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get());
-    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get());
+    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal CellValue");
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual NodeValue");
 
     parallel_for(
       m_primal_node_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
@@ -68,9 +65,12 @@ class ConnectivityToDiamondDualConnectivityDataMapper : public IConnectivityToDi
     static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType1>, "incompatible types");
     static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType2>, "incompatible types");
 
-    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get());
-    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get());
-    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get());
+    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal CellValue");
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual NodeValue");
 
     parallel_for(
       m_primal_node_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
@@ -86,62 +86,69 @@ class ConnectivityToDiamondDualConnectivityDataMapper : public IConnectivityToDi
       });
   }
 
-  template <typename OriginDataType, typename DestinationDataType>
+  template <typename OriginDataType, typename DestinationDataType, ItemType origin_face_type>
   void
-  toDualCell(const FaceValue<OriginDataType>& primal_face_value,
+  toDualCell(const ItemValue<OriginDataType, origin_face_type>& primal_face_value,
              const CellValue<DestinationDataType>& dual_cell_value) const
   {
     static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
     static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType>, "incompatible types");
 
-    Assert(m_primal_connectivity == primal_face_value.connectivity_ptr().get());
-    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get());
+    static_assert(((Dimension == 3) and (origin_face_type == ItemType::face)) or (is_face_in_2d_v<origin_face_type>),
+                  "invalid destination face type");
 
-    parallel_for(
-      m_primal_face_to_dual_cell_map.size(), PUGS_LAMBDA(size_t i) {
-        const auto [primal_face_id, dual_cell_id] = m_primal_face_to_dual_cell_map[i];
+    Assert(m_primal_connectivity == primal_face_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal FaceValue");
+    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual CellValue");
 
-        dual_cell_value[dual_cell_id] = primal_face_value[primal_face_id];
-      });
+    using OriginFaceId = ItemIdT<origin_face_type>;
 
     parallel_for(
-      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+      m_primal_face_to_dual_cell_map.size(), PUGS_LAMBDA(size_t i) {
         const auto [primal_face_id, dual_cell_id] = m_primal_face_to_dual_cell_map[i];
 
-        dual_cell_value[dual_cell_id] = primal_face_value[primal_face_id];
+        const OriginFaceId origin_face_id = static_cast<typename OriginFaceId::base_type>(primal_face_id);
+
+        dual_cell_value[dual_cell_id] = primal_face_value[origin_face_id];
       });
   }
 
-  template <typename OriginDataType, typename DestinationDataType>
+  template <typename OriginDataType, typename DestinationDataType, ItemType destination_face_type>
   void
   fromDualCell(const CellValue<DestinationDataType>& dual_cell_value,
-               const FaceValue<OriginDataType>& primal_face_value) const
+               const ItemValue<OriginDataType, destination_face_type>& primal_face_value) const
   {
     static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
     static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType>, "incompatible types");
 
-    Assert(m_primal_connectivity == primal_face_value.connectivity_ptr().get());
-    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get());
+    Assert(m_primal_connectivity == primal_face_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal FaceValue");
+    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual CellValue");
+
+    static_assert(((Dimension == 3) and (destination_face_type == ItemType::face)) or
+                    (is_face_in_2d_v<destination_face_type>),
+                  "invalid destination face type");
+
+    using DestinationFaceId = ItemIdT<destination_face_type>;
 
     parallel_for(
       m_primal_face_to_dual_cell_map.size(), PUGS_LAMBDA(size_t i) {
         const auto [primal_face_id, dual_cell_id] = m_primal_face_to_dual_cell_map[i];
 
-        primal_face_value[primal_face_id] = dual_cell_value[dual_cell_id];
-      });
+        const DestinationFaceId destination_face_id =
+          static_cast<typename DestinationFaceId::base_type>(primal_face_id);
 
-    parallel_for(
-      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
-        const auto [primal_face_id, dual_cell_id] = m_primal_face_to_dual_cell_map[i];
-        primal_face_value[primal_face_id]         = dual_cell_value[dual_cell_id];
+        primal_face_value[destination_face_id] = dual_cell_value[dual_cell_id];
       });
   }
 
-  ConnectivityToDiamondDualConnectivityDataMapper(const Connectivity<Dimension>& primal_connectivity,
-                                                  const Connectivity<Dimension>& dual_connectivity,
-                                                  const NodeIdToNodeIdMap& primal_node_to_dual_node_map,
-                                                  const CellIdToNodeIdMap& primal_cell_to_dual_node_map,
-                                                  const FaceIdToCellIdMap& primal_face_to_dual_cell_map)
+  PrimalToDiamondDualConnectivityDataMapper(const Connectivity<Dimension>& primal_connectivity,
+                                            const Connectivity<Dimension>& dual_connectivity,
+                                            const ConstNodeIdToNodeIdMap& primal_node_to_dual_node_map,
+                                            const ConstCellIdToNodeIdMap& primal_cell_to_dual_node_map,
+                                            const ConstFaceIdToCellIdMap& primal_face_to_dual_cell_map)
     : m_primal_connectivity{&primal_connectivity},
       m_dual_connectivity{&dual_connectivity},
       m_primal_node_to_dual_node_map{primal_node_to_dual_node_map},
@@ -150,4 +157,4 @@ class ConnectivityToDiamondDualConnectivityDataMapper : public IConnectivityToDi
   {}
 };
 
-#endif   // CONNECTIVITY_TO_DIAMOND_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
+#endif   // PRIMAL_TO_DIAMOND_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
diff --git a/src/mesh/PrimalToDual1DConnectivityDataMapper.hpp b/src/mesh/PrimalToDual1DConnectivityDataMapper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..723025ca793cac1b558a18673a8179298c0ac330
--- /dev/null
+++ b/src/mesh/PrimalToDual1DConnectivityDataMapper.hpp
@@ -0,0 +1,184 @@
+#ifndef PRIMAL_TO_DUAL_1D_CONNECTIVITY_DATA_MAPPER_HPP
+#define PRIMAL_TO_DUAL_1D_CONNECTIVITY_DATA_MAPPER_HPP
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/IPrimalToDualConnectivityDataMapper.hpp>
+#include <mesh/ItemIdToItemIdMap.hpp>
+#include <mesh/ItemValue.hpp>
+#include <utils/Array.hpp>
+#include <utils/PugsAssert.hpp>
+
+class PrimalToDual1DConnectivityDataMapper : public IPrimalToDualConnectivityDataMapper
+{
+ private:
+  const IConnectivity* m_primal_connectivity;
+  const IConnectivity* m_dual_connectivity;
+
+  ConstNodeIdToNodeIdMap m_primal_node_to_dual_node_map;
+  ConstCellIdToNodeIdMap m_primal_cell_to_dual_node_map;
+  ConstNodeIdToCellIdMap m_primal_node_to_dual_cell_map;
+
+ public:
+  template <typename OriginDataType1,
+            typename OriginDataType2,
+            typename DestinationDataType,
+            ItemType origin_node_type,
+            ItemType destination_node_type>
+  void
+  toDualNode(const ItemValue<OriginDataType1, origin_node_type>& primal_node_value,
+             const CellValue<OriginDataType2>& primal_cell_value,
+             const ItemValue<DestinationDataType, destination_node_type>& dual_node_value) const
+  {
+    static_assert(is_node_in_1d_v<origin_node_type>, "invalid origin node type");
+    static_assert(is_node_in_1d_v<destination_node_type>, "invalid destination node type");
+
+    static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType1>, DestinationDataType>, "incompatible types");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType2>, DestinationDataType>, "incompatible types");
+
+    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal CellValue");
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual NodeValue");
+
+    using OriginNodeId      = ItemIdT<origin_node_type>;
+    using DestinationNodeId = ItemIdT<destination_node_type>;
+
+    parallel_for(
+      m_primal_node_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_node_id, dual_node_id] = m_primal_node_to_dual_node_map[i];
+
+        DestinationNodeId destination_node_id = static_cast<typename DestinationNodeId::base_type>(dual_node_id);
+        OriginNodeId origin_node_id           = static_cast<typename OriginNodeId::base_type>(primal_node_id);
+
+        dual_node_value[destination_node_id] = primal_node_value[origin_node_id];
+      });
+
+    parallel_for(
+      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_cell_id, dual_node_id] = m_primal_cell_to_dual_node_map[i];
+
+        DestinationNodeId destination_node_id = static_cast<typename DestinationNodeId::base_type>(dual_node_id);
+
+        dual_node_value[destination_node_id] = primal_cell_value[primal_cell_id];
+      });
+  }
+
+  template <typename OriginDataType,
+            typename DestinationDataType1,
+            typename DestinationDataType2,
+            ItemType origin_node_type,
+            ItemType destination_node_type>
+  void
+  fromDualNode(const ItemValue<OriginDataType, origin_node_type>& dual_node_value,
+               const ItemValue<DestinationDataType1, destination_node_type>& primal_node_value,
+               const CellValue<DestinationDataType2>& primal_cell_value) const
+  {
+    static_assert(is_node_in_1d_v<origin_node_type>, "invalid origin node type");
+    static_assert(is_node_in_1d_v<destination_node_type>, "invalid destination node type");
+
+    static_assert(not std::is_const_v<DestinationDataType1>, "destination data type must not be constant");
+    static_assert(not std::is_const_v<DestinationDataType2>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType1>, "incompatible types");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType2>, "incompatible types");
+
+    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal CellValue");
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual NodeValue");
+
+    using OriginNodeId      = ItemIdT<origin_node_type>;
+    using DestinationNodeId = ItemIdT<destination_node_type>;
+
+    parallel_for(
+      m_primal_node_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_node_id, dual_node_id] = m_primal_node_to_dual_node_map[i];
+
+        DestinationNodeId destination_node_id = static_cast<typename DestinationNodeId::base_type>(primal_node_id);
+        OriginNodeId origin_node_id           = static_cast<typename OriginNodeId::base_type>(dual_node_id);
+
+        primal_node_value[destination_node_id] = dual_node_value[origin_node_id];
+      });
+
+    parallel_for(
+      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_cell_id, dual_node_id] = m_primal_cell_to_dual_node_map[i];
+
+        OriginNodeId origin_node_id = static_cast<typename OriginNodeId::base_type>(dual_node_id);
+
+        primal_cell_value[primal_cell_id] = dual_node_value[origin_node_id];
+      });
+  }
+
+  template <typename OriginDataType, typename DestinationDataType, ItemType origin_node_type>
+  void
+  toDualCell(const ItemValue<OriginDataType, origin_node_type>& primal_node_value,
+             const CellValue<DestinationDataType>& dual_cell_value) const
+  {
+    static_assert(is_node_in_1d_v<origin_node_type>, "invalid origin node type");
+
+    static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType>, "incompatible types");
+
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual CellValue");
+    using OriginNodeId = ItemIdT<origin_node_type>;
+
+    parallel_for(
+      m_primal_node_to_dual_cell_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_node_id, dual_cell_id] = m_primal_node_to_dual_cell_map[i];
+
+        const OriginNodeId origin_node_id = static_cast<typename OriginNodeId::base_type>(primal_node_id);
+
+        dual_cell_value[dual_cell_id] = primal_node_value[origin_node_id];
+      });
+  }
+
+  template <typename OriginDataType, typename DestinationDataType, ItemType destination_node_type>
+  void
+  fromDualCell(const CellValue<DestinationDataType>& dual_cell_value,
+               const ItemValue<OriginDataType, destination_node_type>& primal_node_value) const
+  {
+    static_assert(is_node_in_1d_v<destination_node_type>, "invalid destination node type");
+
+    static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType>, "incompatible types");
+
+    using DestinationNodeId = ItemIdT<destination_node_type>;
+
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual CellValue");
+
+    parallel_for(
+      m_primal_node_to_dual_cell_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_node_id, dual_cell_id] = m_primal_node_to_dual_cell_map[i];
+
+        const DestinationNodeId destination_node_id =
+          static_cast<typename DestinationNodeId::base_type>(primal_node_id);
+
+        primal_node_value[destination_node_id] = dual_cell_value[dual_cell_id];
+      });
+  }
+
+  PrimalToDual1DConnectivityDataMapper(const Connectivity<1>& primal_connectivity,
+                                       const Connectivity<1>& dual_connectivity,
+                                       const ConstNodeIdToNodeIdMap& primal_node_to_dual_node_map,
+                                       const ConstCellIdToNodeIdMap& primal_cell_to_dual_node_map,
+                                       const ConstNodeIdToCellIdMap& primal_node_to_dual_cell_map)
+    : m_primal_connectivity{&primal_connectivity},
+      m_dual_connectivity{&dual_connectivity},
+      m_primal_node_to_dual_node_map{primal_node_to_dual_node_map},
+      m_primal_cell_to_dual_node_map{primal_cell_to_dual_node_map},
+      m_primal_node_to_dual_cell_map{primal_node_to_dual_cell_map}
+  {}
+};
+
+#endif   // PRIMAL_TO_DUAL_1D_CONNECTIVITY_DATA_MAPPER_HPP
diff --git a/src/mesh/PrimalToMedianDualConnectivityDataMapper.hpp b/src/mesh/PrimalToMedianDualConnectivityDataMapper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8f11d66520d975b9c98e336751f5097cec1e4a96
--- /dev/null
+++ b/src/mesh/PrimalToMedianDualConnectivityDataMapper.hpp
@@ -0,0 +1,196 @@
+#ifndef PRIMAL_TO_MEDIAN_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
+#define PRIMAL_TO_MEDIAN_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/IPrimalToDualConnectivityDataMapper.hpp>
+#include <mesh/ItemIdToItemIdMap.hpp>
+#include <mesh/ItemValue.hpp>
+#include <utils/Array.hpp>
+#include <utils/PugsAssert.hpp>
+
+template <size_t Dimension>
+class PrimalToMedianDualConnectivityDataMapper : public IPrimalToDualConnectivityDataMapper
+{
+  static_assert(Dimension == 2 or Dimension == 3,
+                "primal to median dual connectivity mapper is defined in dimension 2 or 3");
+
+ private:
+  const IConnectivity* m_primal_connectivity;
+  const IConnectivity* m_dual_connectivity;
+
+  ConstNodeIdToNodeIdMap m_primal_boundary_node_to_dual_node_map;
+  ConstFaceIdToNodeIdMap m_primal_face_to_dual_node_map;
+  ConstCellIdToNodeIdMap m_primal_cell_to_dual_node_map;
+  ConstNodeIdToCellIdMap m_primal_node_to_dual_cell_map;
+
+ public:
+  template <typename OriginDataType1,
+            typename OriginDataType2,
+            typename OriginDataType3,
+            typename DestinationDataType,
+            ItemType origin_face_type>
+  void
+  toDualNode(const NodeValue<OriginDataType1>& primal_node_value,
+             const ItemValue<OriginDataType2, origin_face_type>& primal_face_value,
+             const CellValue<OriginDataType3>& primal_cell_value,
+             const NodeValue<DestinationDataType>& dual_node_value) const
+  {
+    static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType1>, DestinationDataType>, "incompatible types");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType2>, DestinationDataType>, "incompatible types");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType3>, DestinationDataType>, "incompatible types");
+
+    static_assert(((Dimension == 3) and (origin_face_type == ItemType::face)) or (is_face_in_2d_v<origin_face_type>),
+                  "invalid destination face type");
+
+    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal CellValue");
+    Assert(m_primal_connectivity == primal_face_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal FaceValue");
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual NodeValue");
+
+    parallel_for(
+      m_primal_boundary_node_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_node_id, dual_node_id] = m_primal_boundary_node_to_dual_node_map[i];
+
+        dual_node_value[dual_node_id] = primal_node_value[primal_node_id];
+      });
+
+    using OriginFaceId = ItemIdT<origin_face_type>;
+
+    parallel_for(
+      m_primal_face_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_face_id, dual_node_id] = m_primal_face_to_dual_node_map[i];
+
+        const OriginFaceId origin_face_id = static_cast<typename OriginFaceId::base_type>(primal_face_id);
+
+        dual_node_value[dual_node_id] = primal_face_value[origin_face_id];
+      });
+
+    parallel_for(
+      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_cell_id, dual_node_id] = m_primal_cell_to_dual_node_map[i];
+
+        dual_node_value[dual_node_id] = primal_cell_value[primal_cell_id];
+      });
+  }
+
+  template <typename OriginDataType,
+            typename DestinationDataType1,
+            typename DestinationDataType2,
+            typename DestinationDataType3,
+            ItemType destination_face_type>
+  void
+  fromDualNode(const NodeValue<OriginDataType>& dual_node_value,
+               const NodeValue<DestinationDataType1>& primal_node_value,
+               const ItemValue<DestinationDataType2, destination_face_type>& primal_face_value,
+               const CellValue<DestinationDataType3>& primal_cell_value) const
+  {
+    static_assert(not std::is_const_v<DestinationDataType1>, "destination data type must not be constant");
+    static_assert(not std::is_const_v<DestinationDataType2>, "destination data type must not be constant");
+    static_assert(not std::is_const_v<DestinationDataType3>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType1>, "incompatible types");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType2>, "incompatible types");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType3>, "incompatible types");
+
+    static_assert(((Dimension == 3) and (destination_face_type == ItemType::face)) or
+                    (is_face_in_2d_v<destination_face_type>),
+                  "invalid destination face type");
+
+    Assert(m_primal_connectivity == primal_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal CellValue");
+    Assert(m_primal_connectivity == primal_face_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal FaceValue");
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual NodeValue");
+
+    parallel_for(
+      m_primal_boundary_node_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_node_id, dual_node_id] = m_primal_boundary_node_to_dual_node_map[i];
+
+        primal_node_value[primal_node_id] = dual_node_value[dual_node_id];
+      });
+
+    using DestinationFaceId = ItemIdT<destination_face_type>;
+
+    parallel_for(
+      m_primal_face_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_face_id, dual_node_id] = m_primal_face_to_dual_node_map[i];
+
+        const DestinationFaceId destination_face_id =
+          static_cast<typename DestinationFaceId::base_type>(primal_face_id);
+
+        primal_face_value[destination_face_id] = dual_node_value[dual_node_id];
+      });
+
+    parallel_for(
+      m_primal_cell_to_dual_node_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_cell_id, dual_node_id] = m_primal_cell_to_dual_node_map[i];
+
+        primal_cell_value[primal_cell_id] = dual_node_value[dual_node_id];
+      });
+  }
+
+  template <typename OriginDataType, typename DestinationDataType>
+  void
+  toDualCell(const NodeValue<OriginDataType>& primal_node_value,
+             const CellValue<DestinationDataType>& dual_cell_value) const
+  {
+    static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType>, "incompatible types");
+
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual CellValue");
+
+    parallel_for(
+      m_primal_node_to_dual_cell_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_node_id, dual_cell_id] = m_primal_node_to_dual_cell_map[i];
+
+        dual_cell_value[dual_cell_id] = primal_node_value[primal_node_id];
+      });
+  }
+
+  template <typename OriginDataType, typename DestinationDataType>
+  void
+  fromDualCell(const CellValue<DestinationDataType>& dual_cell_value,
+               const NodeValue<OriginDataType>& primal_node_value) const
+  {
+    static_assert(not std::is_const_v<DestinationDataType>, "destination data type must not be constant");
+    static_assert(std::is_same_v<std::remove_const_t<OriginDataType>, DestinationDataType>, "incompatible types");
+
+    Assert(m_primal_connectivity == primal_node_value.connectivity_ptr().get(),
+           "unexpected connectivity for primal NodeValue");
+    Assert(m_dual_connectivity == dual_cell_value.connectivity_ptr().get(),
+           "unexpected connectivity for dual CellValue");
+
+    parallel_for(
+      m_primal_node_to_dual_cell_map.size(), PUGS_LAMBDA(size_t i) {
+        const auto [primal_face_id, dual_cell_id] = m_primal_node_to_dual_cell_map[i];
+
+        primal_node_value[primal_face_id] = dual_cell_value[dual_cell_id];
+      });
+  }
+
+  PrimalToMedianDualConnectivityDataMapper(const Connectivity<Dimension>& primal_connectivity,
+                                           const Connectivity<Dimension>& dual_connectivity,
+                                           const ConstNodeIdToNodeIdMap& primal_boundary_to_dual_node_map,
+                                           const ConstFaceIdToNodeIdMap& primal_face_to_dual_node_map,
+                                           const ConstCellIdToNodeIdMap& primal_cell_to_dual_node_map,
+                                           const ConstNodeIdToCellIdMap& primal_node_to_dual_cell_map)
+    : m_primal_connectivity{&primal_connectivity},
+      m_dual_connectivity{&dual_connectivity},
+      m_primal_boundary_node_to_dual_node_map{primal_boundary_to_dual_node_map},
+      m_primal_face_to_dual_node_map{primal_face_to_dual_node_map},
+      m_primal_cell_to_dual_node_map{primal_cell_to_dual_node_map},
+      m_primal_node_to_dual_cell_map{primal_node_to_dual_cell_map}
+  {}
+};
+
+#endif   // PRIMAL_TO_MEDIAN_DUAL_CONNECTIVITY_DATA_MAPPER_HPP
diff --git a/src/mesh/SubItemArrayPerItem.hpp b/src/mesh/SubItemArrayPerItem.hpp
index 9fd488bc5e6ebe3ea189368de8eeb67073e590ec..f2103789e9a49c92b921fe2f8b24b3bdaf8f69ec 100644
--- a/src/mesh/SubItemArrayPerItem.hpp
+++ b/src/mesh/SubItemArrayPerItem.hpp
@@ -42,15 +42,13 @@ class SubItemArrayPerItem
   // Allow const std:weak_ptr version to access our data
   friend SubItemArrayPerItem<std::add_const_t<DataType>, ItemOfItem, ConnectivityWeakPtr>;
 
-  // Allow const std:shared_ptr version to access our data
+  // Allow non-const std:shared_ptr version to access our data
   friend SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivitySharedPtr>;
 
-  // Allow const std:weak_ptr version to access our data
+  // Allow non-const std:weak_ptr version to access our data
   friend SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityWeakPtr>;
 
  public:
-  using ToShared = SubItemArrayPerItem<DataType, ItemOfItem, ConnectivitySharedPtr>;
-
   friend PUGS_INLINE SubItemArrayPerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr>
   copy(SubItemArrayPerItem<DataType, ItemOfItem, ConnectivityPtr>& source)
   {
@@ -142,7 +140,7 @@ class SubItemArrayPerItem
   void
   fill(const DataType& data) const noexcept
   {
-    static_assert(not std::is_const_v<DataType>, "Cannot modify ItemValue of const");
+    static_assert(not std::is_const_v<DataType>, "Cannot modify SubItemArrayPerItem of const");
     m_values.fill(data);
   }
 
diff --git a/src/mesh/SubItemValuePerItem.hpp b/src/mesh/SubItemValuePerItem.hpp
index b86b075ecbb44ff0f95a1a28b1f8f2468d98b2e3..defdc8fc9711c6be265e022e0bb0c710d078226d 100644
--- a/src/mesh/SubItemValuePerItem.hpp
+++ b/src/mesh/SubItemValuePerItem.hpp
@@ -41,15 +41,13 @@ class SubItemValuePerItem
   // Allow const std:weak_ptr version to access our data
   friend SubItemValuePerItem<std::add_const_t<DataType>, ItemOfItem, ConnectivityWeakPtr>;
 
-  // Allow const std:shared_ptr version to access our data
+  // Allow non-const std:shared_ptr version to access our data
   friend SubItemValuePerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivitySharedPtr>;
 
-  // Allow const std:weak_ptr version to access our data
+  // Allow non-const std:weak_ptr version to access our data
   friend SubItemValuePerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityWeakPtr>;
 
  public:
-  using ToShared = SubItemValuePerItem<DataType, ItemOfItem, ConnectivitySharedPtr>;
-
   friend PUGS_INLINE SubItemValuePerItem<std::remove_const_t<DataType>, ItemOfItem, ConnectivityPtr>
   copy(SubItemValuePerItem<DataType, ItemOfItem, ConnectivityPtr>& source)
   {
diff --git a/src/mesh/Synchronizer.hpp b/src/mesh/Synchronizer.hpp
index adf127f238fc201a11ba5b503aed4b926e772a4d..dc0773ace208cc83e863df0ffada54e1db33f936 100644
--- a/src/mesh/Synchronizer.hpp
+++ b/src/mesh/Synchronizer.hpp
@@ -7,9 +7,13 @@
 #include <utils/Exceptions.hpp>
 #include <utils/Messenger.hpp>
 
+#include <utils/pugs_config.hpp>
+
 #include <iostream>
 #include <map>
 
+#ifdef PUGS_HAS_MPI
+
 class Synchronizer
 {
   template <ItemType item_type>
@@ -289,4 +293,28 @@ class Synchronizer
   }
 };
 
+#else   // PUGS_HAS_MPI
+
+class Synchronizer
+{
+ public:
+  template <typename DataType, ItemType item_type, typename ConnectivityPtr>
+  PUGS_INLINE void
+  synchronize(ItemValue<DataType, item_type, ConnectivityPtr>&)
+  {}
+
+  template <typename DataType, ItemType item_type, typename ConnectivityPtr>
+  PUGS_INLINE void
+  synchronize(ItemArray<DataType, item_type, ConnectivityPtr>&)
+  {}
+
+  PUGS_INLINE
+  Synchronizer()
+  {
+    ;
+  }
+};
+
+#endif   // PUGS_HAS_MPI
+
 #endif   // SYNCHRONIZER_HPP
diff --git a/src/mesh/SynchronizerManager.cpp b/src/mesh/SynchronizerManager.cpp
index b1301bd88a341cae40c348e1239f673636f5b818..a72191fd3bfc001c38e577384115bffaa441633c 100644
--- a/src/mesh/SynchronizerManager.cpp
+++ b/src/mesh/SynchronizerManager.cpp
@@ -18,6 +18,7 @@ SynchronizerManager::destroy()
   Assert(m_instance != nullptr, "SynchronizerManager was not created!");
 
   if (m_instance->m_connectivity_synchronizer_map.size() > 0) {
+    // LCOV_EXCL_START
     std::stringstream error;
     error << ": some connectivities are still registered\n";
     for (const auto& i_connectivity_synchronizer : m_instance->m_connectivity_synchronizer_map) {
@@ -25,6 +26,7 @@ SynchronizerManager::destroy()
             << '\n';
     }
     throw UnexpectedError(error.str());
+    // LCOV_EXCL_STOP
   }
   delete m_instance;
   m_instance = nullptr;
diff --git a/src/output/VTKWriter.cpp b/src/output/VTKWriter.cpp
index d01476cac3da67c1400efef7e3603e9bb91e096d..7b407256ac7cfac5e354330a99a081b73e822540 100644
--- a/src/output/VTKWriter.cpp
+++ b/src/output/VTKWriter.cpp
@@ -356,7 +356,6 @@ VTKWriter::_write(const std::shared_ptr<const MeshType>& mesh,
   // Adding basic mesh information
   output_named_item_data_set.add(NamedItemData{"cell_number", mesh->connectivity().cellNumber()});
   output_named_item_data_set.add(NamedItemData{"node_number", mesh->connectivity().nodeNumber()});
-  output_named_item_data_set.add(NamedItemData{"cell_center", MeshDataManager::instance().getMeshData(*mesh).xj()});
 
   if (parallel::rank() == 0) {   // write PVTK file
     std::ofstream fout(_getFilenamePVTU());
@@ -492,6 +491,10 @@ VTKWriter::_write(const std::shared_ptr<const MeshType>& mesh,
             types[j] = 9;
             break;
           }
+          case CellType::Polygon: {
+            types[j] = 7;
+            break;
+          }
           case CellType::Tetrahedron: {
             types[j] = 10;
             break;
diff --git a/src/scheme/AcousticSolver.cpp b/src/scheme/AcousticSolver.cpp
index e5171badb11fa509a020025ad38e7e7bbd96281d..098b146e55efe52ffc7297b8ebb841206d9e3d43 100644
--- a/src/scheme/AcousticSolver.cpp
+++ b/src/scheme/AcousticSolver.cpp
@@ -8,11 +8,13 @@
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <scheme/DiscreteFunctionUtils.hpp>
+#include <scheme/ExternalBoundaryConditionDescriptor.hpp>
 #include <scheme/FixedBoundaryConditionDescriptor.hpp>
 #include <scheme/IBoundaryConditionDescriptor.hpp>
 #include <scheme/IDiscreteFunction.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
 #include <scheme/SymmetryBoundaryConditionDescriptor.hpp>
+#include <utils/Socket.hpp>
 
 #include <variant>
 #include <vector>
@@ -75,9 +77,13 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
   class PressureBoundaryCondition;
   class SymmetryBoundaryCondition;
   class VelocityBoundaryCondition;
+  class ExternalFSIVelocityBoundaryCondition;
 
-  using BoundaryCondition = std::
-    variant<FixedBoundaryCondition, PressureBoundaryCondition, SymmetryBoundaryCondition, VelocityBoundaryCondition>;
+  using BoundaryCondition = std::variant<FixedBoundaryCondition,
+                                         PressureBoundaryCondition,
+                                         SymmetryBoundaryCondition,
+                                         VelocityBoundaryCondition,
+                                         ExternalFSIVelocityBoundaryCondition>;
 
   using BoundaryConditionList = std::vector<BoundaryCondition>;
 
@@ -254,6 +260,18 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
         bc_list.emplace_back(FixedBoundaryCondition(getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor())));
         break;
       }
+      case IBoundaryConditionDescriptor::Type::external: {
+        if constexpr (Dimension == 1) {
+          const ExternalBoundaryConditionDescriptor& extern_bc_descriptor =
+            dynamic_cast<const ExternalBoundaryConditionDescriptor&>(*bc_descriptor);
+          bc_list.emplace_back(
+            ExternalFSIVelocityBoundaryCondition(mesh, getMeshNodeBoundary(mesh, bc_descriptor->boundaryDescriptor()),
+                                                 extern_bc_descriptor.socket()));
+        } else {
+          throw NotImplementedError("external FSI velocity not implemented for dimension > 1");
+        }
+        break;
+      }
       case IBoundaryConditionDescriptor::Type::dirichlet: {
         const DirichletBoundaryConditionDescriptor& dirichlet_bc_descriptor =
           dynamic_cast<const DirichletBoundaryConditionDescriptor&>(*bc_descriptor);
@@ -320,6 +338,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
   void _applyPressureBC(const MeshType& mesh, NodeValue<Rd>& br);
   void _applySymmetryBC(NodeValue<Rdxd>& Ar, NodeValue<Rd>& br);
   void _applyVelocityBC(NodeValue<Rdxd>& Ar, NodeValue<Rd>& br);
+  void _applyExternalVelocityBC(const DiscreteScalarFunction& p, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br);
 
   void
   _applyBoundaryConditions(const MeshType& mesh, NodeValue<Rdxd>& Ar, NodeValue<Rd>& br)
@@ -380,6 +399,7 @@ class AcousticSolverHandler::AcousticSolver final : public AcousticSolverHandler
     NodeValue<Rd> br   = this->_computeBr(mesh, Ajr, u, p);
 
     this->_applyBoundaryConditions(mesh, Ar, br);
+    this->_applyExternalVelocityBC(p, Ar, br);
 
     synchronize(Ar);
     synchronize(br);
@@ -610,6 +630,34 @@ AcousticSolverHandler::AcousticSolver<Dimension>::_applyVelocityBC(NodeValue<Rdx
   }
 }
 
+template <size_t Dimension>
+void
+AcousticSolverHandler::AcousticSolver<Dimension>::_applyExternalVelocityBC(const DiscreteScalarFunction& p,
+                                                                           NodeValue<Rdxd>& Ar,
+                                                                           NodeValue<Rd>& br)
+{
+  for (const auto& boundary_condition : m_boundary_condition_list) {
+    std::visit(
+      [&](auto&& bc) {
+        using T = std::decay_t<decltype(bc)>;
+        if constexpr (std::is_same_v<ExternalFSIVelocityBoundaryCondition, T>) {
+          const auto& node_list  = bc.nodeList();
+          const auto& value_list = bc.valueList(p);
+
+          parallel_for(
+            node_list.size(), PUGS_LAMBDA(size_t i_node) {
+              NodeId node_id    = node_list[i_node];
+              const auto& value = value_list[i_node];
+
+              Ar[node_id] = identity;
+              br[node_id] = value;
+            });
+        }
+      },
+      boundary_condition);
+  }
+}
+
 template <size_t Dimension>
 class AcousticSolverHandler::AcousticSolver<Dimension>::FixedBoundaryCondition
 {
@@ -685,6 +733,55 @@ class AcousticSolverHandler::AcousticSolver<1>::PressureBoundaryCondition
   ~PressureBoundaryCondition() = default;
 };
 
+template <size_t Dimension>
+class AcousticSolverHandler::AcousticSolver<Dimension>::ExternalFSIVelocityBoundaryCondition
+{
+ private:
+  const ItemToItemMatrix<ItemType::node, ItemType::cell> m_node_to_cell_matrix;
+  const MeshNodeBoundary<Dimension> m_mesh_node_boundary;
+  const Array<TinyVector<Dimension>> m_value_list;
+  const std::shared_ptr<const Socket> m_socket;
+
+ public:
+  const Array<const NodeId>&
+  nodeList() const
+  {
+    return m_mesh_node_boundary.nodeList();
+  }
+
+  Array<const TinyVector<Dimension>>
+  valueList(const DiscreteScalarFunction& p) const
+  {
+    if (parallel::size() > 1) {
+      throw NotImplementedError("Parallelism is not supported");
+    }
+    if (m_value_list.size() != 1) {
+      throw NotImplementedError("Non connected boundaries are not supported");
+    }
+
+    const CellId cell_id = m_node_to_cell_matrix[m_mesh_node_boundary.nodeList()[0]][0];
+
+    write(*m_socket, p[cell_id]);
+    read(*m_socket, m_value_list[0]);
+    return m_value_list;
+  }
+
+  ExternalFSIVelocityBoundaryCondition(const Mesh<Connectivity<Dimension>>& mesh,
+                                       const MeshNodeBoundary<Dimension>& mesh_node_boundary,
+                                       const std::shared_ptr<const Socket>& socket)
+    : m_node_to_cell_matrix{mesh.connectivity().nodeToCellMatrix()},
+      m_mesh_node_boundary{mesh_node_boundary},
+      m_value_list(mesh_node_boundary.nodeList().size()),
+      m_socket{socket}
+  {
+    if constexpr (Dimension > 1) {
+      throw NotImplementedError("external FSI velocity bc in dimension > 1");
+    }
+  }
+
+  ~ExternalFSIVelocityBoundaryCondition() = default;
+};
+
 template <size_t Dimension>
 class AcousticSolverHandler::AcousticSolver<Dimension>::VelocityBoundaryCondition
 {
diff --git a/src/scheme/CMakeLists.txt b/src/scheme/CMakeLists.txt
index 5e2ec3fca2ea941776e87963ed67f9da8c5a4d05..25e1405a5e9d2f9603a5f50a6abf04350596c58b 100644
--- a/src/scheme/CMakeLists.txt
+++ b/src/scheme/CMakeLists.txt
@@ -3,8 +3,10 @@
 add_library(
   PugsScheme
   AcousticSolver.cpp
+  DiscreteFunctionIntegrator.cpp
   DiscreteFunctionInterpoler.cpp
   DiscreteFunctionUtils.cpp
+  DiscreteFunctionVectorIntegrator.cpp
   DiscreteFunctionVectorInterpoler.cpp
   ScalarDiamondScheme.cpp
-   VectorDiamondScheme.cpp)
+  VectorDiamondScheme.cpp)
diff --git a/src/scheme/CellIntegrator.hpp b/src/scheme/CellIntegrator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6960901d335f894e00a86239772f33f3abc50c67
--- /dev/null
+++ b/src/scheme/CellIntegrator.hpp
@@ -0,0 +1,574 @@
+#ifndef CELL_INTEGRATOR_HPP
+#define CELL_INTEGRATOR_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/CubeTransformation.hpp>
+#include <geometry/LineTransformation.hpp>
+#include <geometry/PrismTransformation.hpp>
+#include <geometry/PyramidTransformation.hpp>
+#include <geometry/SquareTransformation.hpp>
+#include <geometry/TetrahedronTransformation.hpp>
+#include <geometry/TriangleTransformation.hpp>
+#include <mesh/CellType.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemId.hpp>
+#include <utils/Array.hpp>
+
+#include <functional>
+
+class CellIntegrator
+{
+ private:
+  template <size_t Dimension>
+  class AllCells
+  {
+   private:
+    const Connectivity<Dimension>& m_connectivity;
+
+   public:
+    using index_type = CellId;
+
+    PUGS_INLINE
+    CellId
+    cellId(const CellId cell_id) const
+    {
+      return cell_id;
+    }
+
+    PUGS_INLINE
+    size_t
+    size() const
+    {
+      return m_connectivity.numberOfCells();
+    }
+
+    PUGS_INLINE
+    AllCells(const Connectivity<Dimension>& connectivity) : m_connectivity{connectivity} {}
+  };
+
+  template <template <typename T> typename ArrayT>
+  class CellList
+  {
+   private:
+    const ArrayT<CellId>& m_cell_list;
+
+   public:
+    using index_type = typename ArrayT<CellId>::index_type;
+
+    PUGS_INLINE
+    CellId
+    cellId(const index_type index) const
+    {
+      return m_cell_list[index];
+    }
+
+    PUGS_INLINE
+    size_t
+    size() const
+    {
+      return m_cell_list.size();
+    }
+
+    PUGS_INLINE
+    CellList(const ArrayT<CellId>& cell_list) : m_cell_list{cell_list} {}
+  };
+
+  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  static PUGS_INLINE void
+  _tensorialIntegrateTo(std::function<OutputType(InputType)> function,
+                        const IQuadratureDescriptor& quadrature_descriptor,
+                        const MeshType& mesh,
+                        const ListTypeT& cell_list,
+                        OutputArrayT& value)
+  {
+    constexpr size_t Dimension = MeshType::Dimension;
+
+    static_assert(std::is_same_v<TinyVector<Dimension>, std::decay_t<InputType>>, "invalid input data type");
+    static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>,
+                  "invalid output data type");
+
+    using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space;
+    Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens;
+
+    if constexpr (std::is_arithmetic_v<OutputType>) {
+      value.fill(0);
+    } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) {
+      value.fill(zero);
+    } else {
+      static_assert(std::is_same_v<OutputType, double>, "unexpected output type");
+    }
+
+    const auto& connectivity = mesh.connectivity();
+
+    const auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+    const auto cell_type           = connectivity.cellType();
+    const auto xr                  = mesh.xr();
+
+    auto invalid_cell = std::make_pair(false, CellId{});
+
+    const auto qf = [&] {
+      if constexpr (Dimension == 1) {
+        return QuadratureManager::instance().getLineFormula(quadrature_descriptor);
+      } else if constexpr (Dimension == 2) {
+        return QuadratureManager::instance().getSquareFormula(quadrature_descriptor);
+      } else {
+        static_assert(Dimension == 3);
+        return QuadratureManager::instance().getCubeFormula(quadrature_descriptor);
+      }
+    }();
+
+    parallel_for(cell_list.size(), [=, &invalid_cell, &qf, &cell_list](typename ListTypeT::index_type index) {
+      const CellId cell_id = cell_list.cellId(index);
+
+      const auto cell_to_node = cell_to_node_matrix[cell_id];
+
+      if constexpr (Dimension == 1) {
+        switch (cell_type[cell_id]) {
+        case CellType::Line: {
+          const LineTransformation<1> T(xr[cell_to_node[0]], xr[cell_to_node[1]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant() * function(T(xi));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else if constexpr (Dimension == 2) {
+        switch (cell_type[cell_id]) {
+        case CellType::Triangle: {
+          const SquareTransformation<2> T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[2]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+          }
+          break;
+        }
+        case CellType::Quadrangle: {
+          const SquareTransformation<2> T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[3]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else {
+        static_assert(Dimension == 3);
+
+        switch (cell_type[cell_id]) {
+        case CellType::Tetrahedron: {
+          const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                     xr[cell_to_node[2]],   //
+                                     xr[cell_to_node[3]], xr[cell_to_node[3]], xr[cell_to_node[3]],
+                                     xr[cell_to_node[3]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+          }
+          break;
+        }
+        case CellType::Pyramid: {
+          if (cell_to_node.size() == 5) {
+            const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],   //
+                                       xr[cell_to_node[3]],                                             //
+                                       xr[cell_to_node[4]], xr[cell_to_node[4]], xr[cell_to_node[4]],
+                                       xr[cell_to_node[4]]);
+
+            for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+              const auto xi = qf.point(i_point);
+              value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+            }
+          } else {
+            // LCOV_EXCL_START
+            throw NotImplementedError("integration on pyramid with non-quadrangular base (number of nodes " +
+                                      std::to_string(cell_to_node.size()) + ")");
+            // LCOV_EXCL_STOP
+          }
+          break;
+        }
+        case CellType::Diamond: {
+          if (cell_to_node.size() == 5) {
+            {   // top tetrahedron
+              const CubeTransformation T0(xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                          xr[cell_to_node[3]],   //
+                                          xr[cell_to_node[4]], xr[cell_to_node[4]], xr[cell_to_node[4]],
+                                          xr[cell_to_node[4]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                value[index] += qf.weight(i_point) * T0.jacobianDeterminant(xi) * function(T0(xi));
+              }
+            }
+            {   // bottom tetrahedron
+              const CubeTransformation T1(xr[cell_to_node[3]], xr[cell_to_node[3]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[1]],   //
+                                          xr[cell_to_node[0]], xr[cell_to_node[0]], xr[cell_to_node[0]],
+                                          xr[cell_to_node[0]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                value[index] += qf.weight(i_point) * T1.jacobianDeterminant(xi) * function(T1(xi));
+              }
+            }
+          } else if (cell_to_node.size() == 6) {
+            {   // top pyramid
+              const CubeTransformation T0(xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                          xr[cell_to_node[4]],   //
+                                          xr[cell_to_node[5]], xr[cell_to_node[5]], xr[cell_to_node[5]],
+                                          xr[cell_to_node[5]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                value[index] += qf.weight(i_point) * T0.jacobianDeterminant(xi) * function(T0(xi));
+              }
+            }
+            {   // bottom pyramid
+              const CubeTransformation T1(xr[cell_to_node[4]], xr[cell_to_node[3]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[1]],   //
+                                          xr[cell_to_node[0]], xr[cell_to_node[0]], xr[cell_to_node[0]],
+                                          xr[cell_to_node[0]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                value[index] += qf.weight(i_point) * T1.jacobianDeterminant(xi) * function(T1(xi));
+              }
+            }
+          } else {
+            // LCOV_EXCL_START
+            throw NotImplementedError("integration on diamond with non-quadrangular base (" +
+                                      std::to_string(cell_to_node.size()) + ")");
+            // LCOV_EXCL_STOP
+          }
+          break;
+        }
+        case CellType::Prism: {
+          const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]],   //
+                                     xr[cell_to_node[2]], xr[cell_to_node[2]],   //
+                                     xr[cell_to_node[3]], xr[cell_to_node[4]],   //
+                                     xr[cell_to_node[5]], xr[cell_to_node[5]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+          }
+          break;
+        }
+        case CellType::Hexahedron: {
+          const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                     xr[cell_to_node[4]], xr[cell_to_node[5]], xr[cell_to_node[6]],
+                                     xr[cell_to_node[7]]);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+    });
+
+    // LCOV_EXCL_START
+    if (invalid_cell.first) {
+      std::ostringstream os;
+      os << "invalid cell type for integration: " << name(cell_type[invalid_cell.second]);
+      throw UnexpectedError(os.str());
+    }
+    // LCOV_EXCL_STOP
+  }
+
+  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  static PUGS_INLINE void
+  _directIntegrateTo(std::function<OutputType(InputType)> function,
+                     const IQuadratureDescriptor& quadrature_descriptor,
+                     const MeshType& mesh,
+                     const ListTypeT& cell_list,
+                     OutputArrayT& value)
+  {
+    Assert(not quadrature_descriptor.isTensorial());
+
+    constexpr size_t Dimension = MeshType::Dimension;
+
+    static_assert(std::is_same_v<TinyVector<Dimension>, std::decay_t<InputType>>, "invalid input data type");
+    static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>,
+                  "invalid output data type");
+
+    if constexpr (std::is_arithmetic_v<OutputType>) {
+      value.fill(0);
+    } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) {
+      value.fill(zero);
+    } else {
+      static_assert(std::is_same_v<OutputType, double>, "unexpected output type");
+    }
+
+    const auto& connectivity = mesh.connectivity();
+
+    const auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+    const auto cell_type           = connectivity.cellType();
+    const auto xr                  = mesh.xr();
+
+    auto invalid_cell = std::make_pair(false, CellId{});
+
+    parallel_for(cell_list.size(), [=, &invalid_cell, &quadrature_descriptor,
+                                    &cell_list](const typename ListTypeT::index_type& index) {
+      const CellId cell_id = cell_list.cellId(index);
+
+      const auto cell_to_node = cell_to_node_matrix[cell_id];
+
+      if constexpr (Dimension == 1) {
+        switch (cell_type[cell_id]) {
+        case CellType::Line: {
+          const LineTransformation<1> T(xr[cell_to_node[0]], xr[cell_to_node[1]]);
+          const auto qf = QuadratureManager::instance().getLineFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant() * function(T(xi));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else if constexpr (Dimension == 2) {
+        switch (cell_type[cell_id]) {
+        case CellType::Triangle: {
+          const TriangleTransformation<2> T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]]);
+          const auto qf = QuadratureManager::instance().getTriangleFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant() * function(T(xi));
+          }
+          break;
+        }
+        case CellType::Quadrangle: {
+          const SquareTransformation<2> T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                          xr[cell_to_node[3]]);
+          const auto qf = QuadratureManager::instance().getSquareFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      } else {
+        static_assert(Dimension == 3);
+
+        switch (cell_type[cell_id]) {
+        case CellType::Tetrahedron: {
+          const TetrahedronTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]],   //
+                                            xr[cell_to_node[2]], xr[cell_to_node[3]]);
+          const auto qf = QuadratureManager::instance().getTetrahedronFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant() * function(T(xi));
+          }
+          break;
+        }
+        case CellType::Pyramid: {
+          if (cell_to_node.size() == 5) {
+            const PyramidTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],   //
+                                          xr[cell_to_node[3]], xr[cell_to_node[4]]);
+            const auto qf = QuadratureManager::instance().getPyramidFormula(quadrature_descriptor);
+
+            for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+              const auto xi = qf.point(i_point);
+              value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+            }
+          } else {
+            // LCOV_EXCL_START
+            throw NotImplementedError("integration on pyramid with non-quadrangular base");
+            // LCOV_EXCL_STOP
+          }
+          break;
+        }
+        case CellType::Diamond: {
+          if (cell_to_node.size() == 5) {
+            const auto qf = QuadratureManager::instance().getTetrahedronFormula(quadrature_descriptor);
+            {   // top tetrahedron
+              const TetrahedronTransformation T0(xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],   //
+                                                 xr[cell_to_node[4]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                value[index] += qf.weight(i_point) * T0.jacobianDeterminant() * function(T0(xi));
+              }
+            }
+            {   // bottom tetrahedron
+              const TetrahedronTransformation T1(xr[cell_to_node[3]], xr[cell_to_node[2]], xr[cell_to_node[1]],   //
+                                                 xr[cell_to_node[0]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                value[index] += qf.weight(i_point) * T1.jacobianDeterminant() * function(T1(xi));
+              }
+            }
+          } else if (cell_to_node.size() == 6) {
+            const auto qf = QuadratureManager::instance().getPyramidFormula(quadrature_descriptor);
+            {   // top pyramid
+              const PyramidTransformation T0(xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                             xr[cell_to_node[4]], xr[cell_to_node[5]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                value[index] += qf.weight(i_point) * T0.jacobianDeterminant(xi) * function(T0(xi));
+              }
+            }
+            {   // bottom pyramid
+              const PyramidTransformation T1(xr[cell_to_node[4]], xr[cell_to_node[3]], xr[cell_to_node[2]],
+                                             xr[cell_to_node[1]], xr[cell_to_node[0]]);
+
+              for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                const auto xi = qf.point(i_point);
+                value[index] += qf.weight(i_point) * T1.jacobianDeterminant(xi) * function(T1(xi));
+              }
+            }
+          } else {
+            // LCOV_EXCL_START
+            throw NotImplementedError("integration on diamond with non-quadrangular base (" +
+                                      std::to_string(cell_to_node.size()) + ")");
+            // LCOV_EXCL_STOP
+          }
+          break;
+        }
+        case CellType::Prism: {
+          const PrismTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]],
+                                      xr[cell_to_node[3]], xr[cell_to_node[4]], xr[cell_to_node[5]]);
+          const auto qf = QuadratureManager::instance().getPrismFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+          }
+          break;
+        }
+        case CellType::Hexahedron: {
+          const CubeTransformation T(xr[cell_to_node[0]], xr[cell_to_node[1]], xr[cell_to_node[2]], xr[cell_to_node[3]],
+                                     xr[cell_to_node[4]], xr[cell_to_node[5]], xr[cell_to_node[6]],
+                                     xr[cell_to_node[7]]);
+          const auto qf = QuadratureManager::instance().getCubeFormula(quadrature_descriptor);
+
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.jacobianDeterminant(xi) * function(T(xi));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_cell = std::make_pair(true, cell_id);
+          break;
+        }
+          // LCOV_EXCL_STOP
+        }
+      }
+    });
+
+    // LCOV_EXCL_START
+    if (invalid_cell.first) {
+      std::ostringstream os;
+      os << "invalid cell type for integration: " << name(cell_type[invalid_cell.second]);
+      throw UnexpectedError(os.str());
+    }
+    // LCOV_EXCL_STOP
+  }
+
+ public:
+  template <typename MeshType, typename OutputArrayT, typename OutputType, typename InputType>
+  static PUGS_INLINE void
+  integrateTo(const std::function<OutputType(InputType)>& function,
+              const IQuadratureDescriptor& quadrature_descriptor,
+              const MeshType& mesh,
+              OutputArrayT& value)
+  {
+    constexpr size_t Dimension = MeshType::Dimension;
+
+    if (quadrature_descriptor.isTensorial()) {
+      _tensorialIntegrateTo<MeshType, OutputArrayT>(function, quadrature_descriptor, mesh,
+                                                    AllCells<Dimension>{mesh.connectivity()}, value);
+    } else {
+      _directIntegrateTo<MeshType, OutputArrayT>(function, quadrature_descriptor, mesh,
+                                                 AllCells<Dimension>{mesh.connectivity()}, value);
+    }
+  }
+
+  template <typename MeshType, typename OutputArrayT, typename FunctionType>
+  static PUGS_INLINE void
+  integrateTo(const FunctionType& function,
+              const IQuadratureDescriptor& quadrature_descriptor,
+              const MeshType& mesh,
+              OutputArrayT& value)
+  {
+    integrateTo(std::function(function), quadrature_descriptor, mesh, value);
+  }
+
+  template <typename MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT>
+  static PUGS_INLINE ArrayT<OutputType>
+  integrate(const std::function<OutputType(InputType)>& function,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const ArrayT<CellId>& cell_list)
+  {
+    ArrayT<OutputType> value(size(cell_list));
+    if (quadrature_descriptor.isTensorial()) {
+      _tensorialIntegrateTo<MeshType, ArrayT<OutputType>>(function, quadrature_descriptor, mesh, CellList{cell_list},
+                                                          value);
+    } else {
+      _directIntegrateTo<MeshType, ArrayT<OutputType>>(function, quadrature_descriptor, mesh, CellList{cell_list},
+                                                       value);
+    }
+
+    return value;
+  }
+
+  template <typename MeshType, typename FunctionType, template <typename DataType> typename ArrayT>
+  static PUGS_INLINE auto
+  integrate(const FunctionType& function,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const ArrayT<CellId>& cell_list)
+  {
+    return integrate(std::function(function), quadrature_descriptor, mesh, cell_list);
+  }
+};
+
+#endif   // CELL_INTEGRATOR_HPP
diff --git a/src/scheme/DiscreteFunctionIntegrator.cpp b/src/scheme/DiscreteFunctionIntegrator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..91cf921df2764ad13f4470687fc4cc5f4d6bced1
--- /dev/null
+++ b/src/scheme/DiscreteFunctionIntegrator.cpp
@@ -0,0 +1,132 @@
+#include <scheme/DiscreteFunctionIntegrator.hpp>
+
+#include <language/utils/IntegrateCellValue.hpp>
+#include <scheme/DiscreteFunctionP0.hpp>
+#include <utils/Exceptions.hpp>
+
+template <size_t Dimension, typename DataType, typename ValueType>
+std::shared_ptr<IDiscreteFunction>
+DiscreteFunctionIntegrator::_integrate() const
+{
+  using MeshType       = Mesh<Connectivity<Dimension>>;
+  std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(m_mesh);
+
+  if constexpr (std::is_same_v<DataType, ValueType>) {
+    return std::make_shared<
+      DiscreteFunctionP0<Dimension, ValueType>>(mesh,
+                                                IntegrateCellValue<DataType(TinyVector<Dimension>)>::template integrate<
+                                                  MeshType>(m_function_id, *m_quadrature_descriptor, *mesh));
+  } else {
+    static_assert(std::is_convertible_v<DataType, ValueType>);
+
+    CellValue<DataType> cell_data =
+      IntegrateCellValue<DataType(TinyVector<Dimension>)>::template integrate<MeshType>(m_function_id,
+                                                                                        *m_quadrature_descriptor,
+                                                                                        *mesh);
+
+    CellValue<ValueType> cell_value{mesh->connectivity()};
+
+    parallel_for(
+      mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_data[cell_id]; });
+
+    return std::make_shared<DiscreteFunctionP0<Dimension, ValueType>>(mesh, cell_value);
+  }
+}
+
+template <size_t Dimension>
+std::shared_ptr<IDiscreteFunction>
+DiscreteFunctionIntegrator::_integrate() const
+{
+  const auto& function_descriptor = m_function_id.descriptor();
+  Assert(function_descriptor.domainMappingNode().children[1]->m_data_type == ASTNodeDataType::typename_t);
+
+  const ASTNodeDataType& data_type = function_descriptor.domainMappingNode().children[1]->m_data_type.contentType();
+
+  switch (data_type) {
+  case ASTNodeDataType::bool_t: {
+    return this->_integrate<Dimension, bool, double>();
+  }
+  case ASTNodeDataType::unsigned_int_t: {
+    return this->_integrate<Dimension, uint64_t, double>();
+  }
+  case ASTNodeDataType::int_t: {
+    return this->_integrate<Dimension, int64_t, double>();
+  }
+  case ASTNodeDataType::double_t: {
+    return this->_integrate<Dimension, double>();
+  }
+  case ASTNodeDataType::vector_t: {
+    switch (data_type.dimension()) {
+    case 1: {
+      return this->_integrate<Dimension, TinyVector<1>>();
+    }
+    case 2: {
+      return this->_integrate<Dimension, TinyVector<2>>();
+    }
+    case 3: {
+      return this->_integrate<Dimension, TinyVector<3>>();
+    }
+      // LCOV_EXCL_START
+    default: {
+      std::ostringstream os;
+      os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset;
+
+      throw UnexpectedError(os.str());
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+  case ASTNodeDataType::matrix_t: {
+    Assert(data_type.numberOfColumns() == data_type.numberOfRows(), "undefined matrix type");
+    switch (data_type.numberOfColumns()) {
+    case 1: {
+      return this->_integrate<Dimension, TinyMatrix<1>>();
+    }
+    case 2: {
+      return this->_integrate<Dimension, TinyMatrix<2>>();
+    }
+    case 3: {
+      return this->_integrate<Dimension, TinyMatrix<3>>();
+    }
+      // LCOV_EXCL_START
+    default: {
+      std::ostringstream os;
+      os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset;
+
+      throw UnexpectedError(os.str());
+    }
+      // LCOV_EXCL_STOP
+    }
+  }
+    // LCOV_EXCL_START
+  default: {
+    std::ostringstream os;
+    os << "invalid integrate value type: " << rang::fgB::red << dataTypeName(data_type) << rang::style::reset;
+
+    throw UnexpectedError(os.str());
+  }
+    // LCOV_EXCL_STOP
+  }
+}
+
+std::shared_ptr<IDiscreteFunction>
+DiscreteFunctionIntegrator::integrate() const
+{
+  std::shared_ptr<IDiscreteFunction> discrete_function;
+  switch (m_mesh->dimension()) {
+  case 1: {
+    return this->_integrate<1>();
+  }
+  case 2: {
+    return this->_integrate<2>();
+  }
+  case 3: {
+    return this->_integrate<3>();
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("invalid dimension");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/scheme/DiscreteFunctionIntegrator.hpp b/src/scheme/DiscreteFunctionIntegrator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1784f891eaa6fd7471b17006b3c9f277c31e51dd
--- /dev/null
+++ b/src/scheme/DiscreteFunctionIntegrator.hpp
@@ -0,0 +1,39 @@
+#ifndef DISCRETE_FUNCTION_INTEGRATOR_HPP
+#define DISCRETE_FUNCTION_INTEGRATOR_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+#include <language/utils/FunctionSymbolId.hpp>
+#include <mesh/IMesh.hpp>
+#include <scheme/IDiscreteFunction.hpp>
+
+#include <memory>
+
+class DiscreteFunctionIntegrator
+{
+ private:
+  std::shared_ptr<const IMesh> m_mesh;
+  std::shared_ptr<const IQuadratureDescriptor> m_quadrature_descriptor;
+  const FunctionSymbolId m_function_id;
+
+  template <size_t Dimension, typename DataType, typename ValueType = DataType>
+  std::shared_ptr<IDiscreteFunction> _integrate() const;
+
+  template <size_t Dimension>
+  std::shared_ptr<IDiscreteFunction> _integrate() const;
+
+ public:
+  std::shared_ptr<IDiscreteFunction> integrate() const;
+
+  DiscreteFunctionIntegrator(const std::shared_ptr<const IMesh>& mesh,
+                             const std::shared_ptr<const IQuadratureDescriptor>& quadrature_descriptor,
+                             const FunctionSymbolId& function_id)
+    : m_mesh{mesh}, m_quadrature_descriptor{quadrature_descriptor}, m_function_id{function_id}
+  {}
+
+  DiscreteFunctionIntegrator(const DiscreteFunctionIntegrator&) = delete;
+  DiscreteFunctionIntegrator(DiscreteFunctionIntegrator&&)      = delete;
+
+  ~DiscreteFunctionIntegrator() = default;
+};
+
+#endif   // DISCRETE_FUNCTION_INTEGRATOR_HPP
diff --git a/src/scheme/DiscreteFunctionInterpoler.cpp b/src/scheme/DiscreteFunctionInterpoler.cpp
index 11b19835eab3af91c0ea125359364831916cbc18..c39b6ee9d7020b1ffee3582ee36bd461a0c03435 100644
--- a/src/scheme/DiscreteFunctionInterpoler.cpp
+++ b/src/scheme/DiscreteFunctionInterpoler.cpp
@@ -4,7 +4,7 @@
 #include <scheme/DiscreteFunctionP0.hpp>
 #include <utils/Exceptions.hpp>
 
-template <size_t Dimension, typename DataType>
+template <size_t Dimension, typename DataType, typename ValueType>
 std::shared_ptr<IDiscreteFunction>
 DiscreteFunctionInterpoler::_interpolate() const
 {
@@ -12,10 +12,25 @@ DiscreteFunctionInterpoler::_interpolate() const
   using MeshDataType      = MeshData<Dimension>;
   MeshDataType& mesh_data = MeshDataManager::instance().getMeshData(*mesh);
 
-  return std::make_shared<
-    DiscreteFunctionP0<Dimension, DataType>>(mesh,
-                                             InterpolateItemValue<DataType(TinyVector<Dimension>)>::
-                                               template interpolate<ItemType::cell>(m_function_id, mesh_data.xj()));
+  if constexpr (std::is_same_v<DataType, ValueType>) {
+    return std::make_shared<
+      DiscreteFunctionP0<Dimension, ValueType>>(mesh,
+                                                InterpolateItemValue<DataType(TinyVector<Dimension>)>::
+                                                  template interpolate<ItemType::cell>(m_function_id, mesh_data.xj()));
+  } else {
+    static_assert(std::is_convertible_v<DataType, ValueType>);
+
+    CellValue<DataType> cell_data =
+      InterpolateItemValue<DataType(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(m_function_id,
+                                                                                                  mesh_data.xj());
+
+    CellValue<ValueType> cell_value{mesh->connectivity()};
+
+    parallel_for(
+      mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_data[cell_id]; });
+
+    return std::make_shared<DiscreteFunctionP0<Dimension, ValueType>>(mesh, cell_value);
+  }
 }
 
 template <size_t Dimension>
@@ -29,13 +44,13 @@ DiscreteFunctionInterpoler::_interpolate() const
 
   switch (data_type) {
   case ASTNodeDataType::bool_t: {
-    return this->_interpolate<Dimension, bool>();
+    return this->_interpolate<Dimension, bool, double>();
   }
   case ASTNodeDataType::unsigned_int_t: {
-    return this->_interpolate<Dimension, uint64_t>();
+    return this->_interpolate<Dimension, uint64_t, double>();
   }
   case ASTNodeDataType::int_t: {
-    return this->_interpolate<Dimension, int64_t>();
+    return this->_interpolate<Dimension, int64_t, double>();
   }
   case ASTNodeDataType::double_t: {
     return this->_interpolate<Dimension, double>();
@@ -51,12 +66,14 @@ DiscreteFunctionInterpoler::_interpolate() const
     case 3: {
       return this->_interpolate<Dimension, TinyVector<3>>();
     }
+      // LCOV_EXCL_START
     default: {
       std::ostringstream os;
       os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset;
 
       throw UnexpectedError(os.str());
     }
+      // LCOV_EXCL_STOP
     }
   }
   case ASTNodeDataType::matrix_t: {
@@ -71,20 +88,24 @@ DiscreteFunctionInterpoler::_interpolate() const
     case 3: {
       return this->_interpolate<Dimension, TinyMatrix<3>>();
     }
+      // LCOV_EXCL_START
     default: {
       std::ostringstream os;
       os << "invalid vector dimension " << rang::fgB::red << data_type.dimension() << rang::style::reset;
 
       throw UnexpectedError(os.str());
     }
+      // LCOV_EXCL_STOP
     }
   }
+    // LCOV_EXCL_START
   default: {
     std::ostringstream os;
     os << "invalid interpolation value type: " << rang::fgB::red << dataTypeName(data_type) << rang::style::reset;
 
     throw UnexpectedError(os.str());
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -102,9 +123,10 @@ DiscreteFunctionInterpoler::interpolate() const
   case 3: {
     return this->_interpolate<3>();
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid dimension");
   }
+    // LCOV_EXCL_STOP
   }
-  return nullptr;
 }
diff --git a/src/scheme/DiscreteFunctionInterpoler.hpp b/src/scheme/DiscreteFunctionInterpoler.hpp
index 758ff2454ec673ba2cc46c0c561eca1a4a256b51..e5724e3fec3ea75098772827537a1267f68328b7 100644
--- a/src/scheme/DiscreteFunctionInterpoler.hpp
+++ b/src/scheme/DiscreteFunctionInterpoler.hpp
@@ -15,7 +15,7 @@ class DiscreteFunctionInterpoler
   std::shared_ptr<const IDiscreteFunctionDescriptor> m_discrete_function_descriptor;
   const FunctionSymbolId m_function_id;
 
-  template <size_t Dimension, typename DataType>
+  template <size_t Dimension, typename DataType, typename ValueType = DataType>
   std::shared_ptr<IDiscreteFunction> _interpolate() const;
 
   template <size_t Dimension>
diff --git a/src/scheme/DiscreteFunctionUtils.cpp b/src/scheme/DiscreteFunctionUtils.cpp
index 6d2e62da4fcdd9d1c785b01894ca3d4776dfae8c..f7057c4abcf920eea37d678eb410e7edf58006b3 100644
--- a/src/scheme/DiscreteFunctionUtils.cpp
+++ b/src/scheme/DiscreteFunctionUtils.cpp
@@ -10,6 +10,10 @@ std::shared_ptr<const IDiscreteFunction>
 shallowCopy(const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh,
             const std::shared_ptr<const DiscreteFunctionP0<Dimension, DataType>>& discrete_function)
 {
+  Assert(mesh->shared_connectivity() ==
+           dynamic_cast<const Mesh<Connectivity<Dimension>>&>(*discrete_function->mesh()).shared_connectivity(),
+         "connectivities should be the same");
+
   return std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh, discrete_function->cellValues());
 }
 
@@ -22,7 +26,7 @@ shallowCopy(const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh,
     std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(discrete_function->mesh());
 
   if (mesh->shared_connectivity() != function_mesh->shared_connectivity()) {
-    throw NormalError("incompatible connectivities");
+    throw NormalError("cannot shallow copy when connectivity changes");
   }
 
   switch (discrete_function->descriptor().type()) {
@@ -46,17 +50,21 @@ shallowCopy(const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh,
         return shallowCopy(mesh, std::dynamic_pointer_cast<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(
                                    discrete_function));
       }
+        // LCOV_EXCL_START
       default: {
         throw UnexpectedError("invalid data vector dimension: " +
                               std::to_string(discrete_function->dataType().dimension()));
       }
+        // LCOV_EXCL_STOP
       }
     }
     case ASTNodeDataType::matrix_t: {
       if (discrete_function->dataType().numberOfRows() != discrete_function->dataType().numberOfColumns()) {
+        // LCOV_EXCL_START
         throw UnexpectedError(
           "invalid data matrix dimensions: " + std::to_string(discrete_function->dataType().numberOfRows()) + "x" +
           std::to_string(discrete_function->dataType().numberOfColumns()));
+        // LCOV_EXCL_STOP
       }
       switch (discrete_function->dataType().numberOfRows()) {
       case 1: {
@@ -71,21 +79,27 @@ shallowCopy(const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh,
         return shallowCopy(mesh, std::dynamic_pointer_cast<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(
                                    discrete_function));
       }
+        // LCOV_EXCL_START
       default: {
         throw UnexpectedError(
           "invalid data matrix dimensions: " + std::to_string(discrete_function->dataType().numberOfRows()) + "x" +
           std::to_string(discrete_function->dataType().numberOfColumns()));
       }
+        // LCOV_EXCL_STOP
       }
     }
+      // LCOV_EXCL_START
     default: {
       throw UnexpectedError("invalid kind of P0 function: invalid data type");
     }
+      // LCOV_EXCL_STOP
     }
   }
+    // LCOV_EXCL_START
   default: {
-    throw NormalError("invalid discretization type");
+    throw UnexpectedError("invalid discretization type");
   }
+    // LCOV_EXCL_STOP
   }
 }
 
@@ -108,8 +122,10 @@ shallowCopy(const std::shared_ptr<const IMesh>& mesh, const std::shared_ptr<cons
   case 3: {
     return shallowCopy(std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(mesh), discrete_function);
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid mesh dimension");
   }
+    // LCOV_EXCL_STOP
   }
 }
diff --git a/src/scheme/DiscreteFunctionUtils.hpp b/src/scheme/DiscreteFunctionUtils.hpp
index 1c6e28a21236282aab52e696881399a304a9b09a..1eea93a7eae8db4c592b6bbec35341ad33e35bdc 100644
--- a/src/scheme/DiscreteFunctionUtils.hpp
+++ b/src/scheme/DiscreteFunctionUtils.hpp
@@ -4,7 +4,6 @@
 #include <scheme/DiscreteFunctionType.hpp>
 #include <scheme/IDiscreteFunction.hpp>
 #include <scheme/IDiscreteFunctionDescriptor.hpp>
-#include <utils/Exceptions.hpp>
 
 #include <vector>
 
@@ -31,7 +30,7 @@ getCommonMesh(const std::vector<std::shared_ptr<const IDiscreteFunction>>& discr
       i_mesh = discrete_function->mesh();
     } else {
       if (i_mesh != discrete_function->mesh()) {
-        throw NormalError("discrete functions are not defined on the same mesh");
+        return {};
       }
     }
   }
diff --git a/src/scheme/DiscreteFunctionVectorIntegrator.cpp b/src/scheme/DiscreteFunctionVectorIntegrator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d8937f84aeba6ae96ac5fc6e5ba523e5781f239
--- /dev/null
+++ b/src/scheme/DiscreteFunctionVectorIntegrator.cpp
@@ -0,0 +1,70 @@
+#include <scheme/DiscreteFunctionVectorIntegrator.hpp>
+
+#include <language/utils/IntegrateCellArray.hpp>
+
+#include <scheme/DiscreteFunctionP0Vector.hpp>
+#include <utils/Exceptions.hpp>
+
+template <size_t Dimension, typename DataType>
+std::shared_ptr<IDiscreteFunction>
+DiscreteFunctionVectorIntegrator::_integrate() const
+{
+  std::shared_ptr mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<Dimension>>>(m_mesh);
+
+  return std::make_shared<
+    DiscreteFunctionP0Vector<Dimension, DataType>>(mesh, IntegrateCellArray<DataType(TinyVector<Dimension>)>::
+                                                           template integrate(m_function_id_list,
+                                                                              *m_quadrature_descriptor, *mesh));
+}
+
+template <size_t Dimension>
+std::shared_ptr<IDiscreteFunction>
+DiscreteFunctionVectorIntegrator::_integrate() const
+{
+  for (const auto& function_id : m_function_id_list) {
+    const auto& function_descriptor = function_id.descriptor();
+    Assert(function_descriptor.domainMappingNode().children[1]->m_data_type == ASTNodeDataType::typename_t);
+    const ASTNodeDataType& data_type = function_descriptor.domainMappingNode().children[1]->m_data_type.contentType();
+
+    switch (data_type) {
+    case ASTNodeDataType::bool_t:
+    case ASTNodeDataType::unsigned_int_t:
+    case ASTNodeDataType::int_t:
+    case ASTNodeDataType::double_t: {
+      break;
+    }
+    default: {
+      std::ostringstream os;
+      os << "vector functions require scalar value type.\n"
+         << "Invalid interpolation value type: " << rang::fgB::red << dataTypeName(data_type) << rang::style::reset;
+      throw NormalError(os.str());
+    }
+    }
+  }
+  return this->_integrate<Dimension, double>();
+}
+
+std::shared_ptr<IDiscreteFunction>
+DiscreteFunctionVectorIntegrator::integrate() const
+{
+  if (m_discrete_function_descriptor->type() != DiscreteFunctionType::P0Vector) {
+    throw NormalError("invalid discrete function type for vector interpolation");
+  }
+
+  switch (m_mesh->dimension()) {
+  case 1: {
+    return this->_integrate<1>();
+  }
+  case 2: {
+    return this->_integrate<2>();
+  }
+  case 3: {
+    return this->_integrate<3>();
+  }
+    // LCOV_EXCL_START
+  default: {
+    throw UnexpectedError("invalid dimension");
+  }
+    // LCOV_EXCL_STOP
+  }
+}
diff --git a/src/scheme/DiscreteFunctionVectorIntegrator.hpp b/src/scheme/DiscreteFunctionVectorIntegrator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..01e086e7efd9c2334d07dfdb3cf8dce1f33e49f1
--- /dev/null
+++ b/src/scheme/DiscreteFunctionVectorIntegrator.hpp
@@ -0,0 +1,47 @@
+#ifndef DISCRETE_FUNCTION_VECTOR_INTEGRATOR_HPP
+#define DISCRETE_FUNCTION_VECTOR_INTEGRATOR_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+#include <language/utils/FunctionSymbolId.hpp>
+#include <mesh/IMesh.hpp>
+#include <scheme/IDiscreteFunction.hpp>
+#include <scheme/IDiscreteFunctionDescriptor.hpp>
+
+#include <memory>
+#include <vector>
+
+class DiscreteFunctionVectorIntegrator
+{
+ private:
+  std::shared_ptr<const IMesh> m_mesh;
+  std::shared_ptr<const IQuadratureDescriptor> m_quadrature_descriptor;
+  std::shared_ptr<const IDiscreteFunctionDescriptor> m_discrete_function_descriptor;
+  const std::vector<FunctionSymbolId> m_function_id_list;
+
+  template <size_t Dimension, typename DataType>
+  std::shared_ptr<IDiscreteFunction> _integrate() const;
+
+  template <size_t Dimension>
+  std::shared_ptr<IDiscreteFunction> _integrate() const;
+
+ public:
+  std::shared_ptr<IDiscreteFunction> integrate() const;
+
+  DiscreteFunctionVectorIntegrator(
+    const std::shared_ptr<const IMesh>& mesh,
+    const std::shared_ptr<const IQuadratureDescriptor>& quadrature_descriptor,
+    const std::shared_ptr<const IDiscreteFunctionDescriptor>& discrete_function_descriptor,
+    const std::vector<FunctionSymbolId>& function_id_list)
+    : m_mesh{mesh},
+      m_quadrature_descriptor(quadrature_descriptor),
+      m_discrete_function_descriptor{discrete_function_descriptor},
+      m_function_id_list{function_id_list}
+  {}
+
+  DiscreteFunctionVectorIntegrator(const DiscreteFunctionVectorIntegrator&) = delete;
+  DiscreteFunctionVectorIntegrator(DiscreteFunctionVectorIntegrator&&)      = delete;
+
+  ~DiscreteFunctionVectorIntegrator() = default;
+};
+
+#endif   // DISCRETE_FUNCTION_VECTOR_INTEGRATOR_HPP
diff --git a/src/scheme/DiscreteFunctionVectorInterpoler.cpp b/src/scheme/DiscreteFunctionVectorInterpoler.cpp
index 4827345a5864ccae5425f6463e00163d455a4aa8..98d6513c9a896b81d8e6909e057a8300b66d6dc7 100644
--- a/src/scheme/DiscreteFunctionVectorInterpoler.cpp
+++ b/src/scheme/DiscreteFunctionVectorInterpoler.cpp
@@ -38,7 +38,7 @@ DiscreteFunctionVectorInterpoler::_interpolate() const
     default: {
       std::ostringstream os;
       os << "vector functions require scalar value type.\n"
-         << "Invalid interpolation value type:" << rang::fgB::red << dataTypeName(data_type) << rang::style::reset;
+         << "Invalid interpolation value type: " << rang::fgB::red << dataTypeName(data_type) << rang::style::reset;
       throw NormalError(os.str());
     }
     }
@@ -53,7 +53,6 @@ DiscreteFunctionVectorInterpoler::interpolate() const
     throw NormalError("invalid discrete function type for vector interpolation");
   }
 
-  std::shared_ptr<IDiscreteFunction> discrete_function;
   switch (m_mesh->dimension()) {
   case 1: {
     return this->_interpolate<1>();
@@ -64,8 +63,10 @@ DiscreteFunctionVectorInterpoler::interpolate() const
   case 3: {
     return this->_interpolate<3>();
   }
+    // LCOV_EXCL_START
   default: {
     throw UnexpectedError("invalid dimension");
   }
+    // LCOV_EXCL_STOP
   }
 }
diff --git a/src/scheme/EdgeIntegrator.hpp b/src/scheme/EdgeIntegrator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2bf287c3d6f61cb83ef3619124334a2124898f8c
--- /dev/null
+++ b/src/scheme/EdgeIntegrator.hpp
@@ -0,0 +1,166 @@
+#ifndef EDGE_INTEGRATOR_HPP
+#define EDGE_INTEGRATOR_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/LineTransformation.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemId.hpp>
+#include <utils/Array.hpp>
+
+#include <functional>
+
+class EdgeIntegrator
+{
+ private:
+  template <size_t Dimension>
+  class AllEdges
+  {
+   private:
+    const Connectivity<Dimension>& m_connectivity;
+
+   public:
+    using index_type = EdgeId;
+
+    PUGS_INLINE
+    EdgeId
+    edgeId(const EdgeId edge_id) const
+    {
+      return edge_id;
+    }
+
+    PUGS_INLINE
+    size_t
+    size() const
+    {
+      return m_connectivity.numberOfEdges();
+    }
+
+    PUGS_INLINE
+    AllEdges(const Connectivity<Dimension>& connectivity) : m_connectivity{connectivity} {}
+  };
+
+  template <template <typename T> typename ArrayT>
+  class EdgeList
+  {
+   private:
+    const ArrayT<EdgeId>& m_edge_list;
+
+   public:
+    using index_type = typename ArrayT<EdgeId>::index_type;
+
+    PUGS_INLINE
+    EdgeId
+    edgeId(const index_type index) const
+    {
+      return m_edge_list[index];
+    }
+
+    PUGS_INLINE
+    size_t
+    size() const
+    {
+      return m_edge_list.size();
+    }
+
+    PUGS_INLINE
+    EdgeList(const ArrayT<EdgeId>& edge_list) : m_edge_list{edge_list} {}
+  };
+
+  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  static PUGS_INLINE void
+  _integrateTo(std::function<OutputType(InputType)> function,
+               const IQuadratureDescriptor& quadrature_descriptor,
+               const MeshType& mesh,
+               const ListTypeT& edge_list,
+               OutputArrayT& value)
+  {
+    constexpr size_t Dimension = MeshType::Dimension;
+    static_assert(Dimension == 3);
+
+    static_assert(std::is_same_v<TinyVector<Dimension>, std::decay_t<InputType>>, "invalid input data type");
+    static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>,
+                  "invalid output data type");
+
+    using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space;
+    Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens;
+
+    if constexpr (std::is_arithmetic_v<OutputType>) {
+      value.fill(0);
+    } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) {
+      value.fill(zero);
+    } else {
+      static_assert(std::is_same_v<OutputType, double>, "unexpected output type");
+    }
+
+    const auto& connectivity = mesh.connectivity();
+
+    const auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+    const auto xr                  = mesh.xr();
+
+    const auto qf = QuadratureManager::instance().getLineFormula(quadrature_descriptor);
+
+    parallel_for(edge_list.size(), [=, &qf, &edge_list](typename ListTypeT::index_type index) {
+      const EdgeId edge_id = edge_list.edgeId(index);
+
+      const auto edge_to_node = edge_to_node_matrix[edge_id];
+
+      Assert(edge_to_node.size() == 2);
+      const LineTransformation<3> T(xr[edge_to_node[0]], xr[edge_to_node[1]]);
+
+      for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+        const auto xi = qf.point(i_point);
+        value[index] += qf.weight(i_point) * T.velocityNorm() * function(T(xi));
+      }
+    });
+  }
+
+ public:
+  template <typename MeshType, typename OutputArrayT, typename OutputType, typename InputType>
+  static PUGS_INLINE void
+  integrateTo(const std::function<OutputType(InputType)>& function,
+              const IQuadratureDescriptor& quadrature_descriptor,
+              const MeshType& mesh,
+              OutputArrayT& value)
+  {
+    constexpr size_t Dimension = MeshType::Dimension;
+
+    _integrateTo<MeshType, OutputArrayT>(function, quadrature_descriptor, mesh,
+                                         AllEdges<Dimension>{mesh.connectivity()}, value);
+  }
+
+  template <typename MeshType, typename OutputArrayT, typename FunctionType>
+  static PUGS_INLINE void
+  integrateTo(const FunctionType& function,
+              const IQuadratureDescriptor& quadrature_descriptor,
+              const MeshType& mesh,
+              OutputArrayT& value)
+  {
+    integrateTo(std::function(function), quadrature_descriptor, mesh, value);
+  }
+
+  template <typename MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT>
+  static PUGS_INLINE ArrayT<OutputType>
+  integrate(const std::function<OutputType(InputType)>& function,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const ArrayT<EdgeId>& edge_list)
+  {
+    ArrayT<OutputType> value(size(edge_list));
+    _integrateTo<MeshType, ArrayT<OutputType>>(function, quadrature_descriptor, mesh, EdgeList{edge_list}, value);
+
+    return value;
+  }
+
+  template <typename MeshType, typename FunctionType, template <typename DataType> typename ArrayT>
+  static PUGS_INLINE auto
+  integrate(const FunctionType& function,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const ArrayT<EdgeId>& edge_list)
+  {
+    return integrate(std::function(function), quadrature_descriptor, mesh, edge_list);
+  }
+};
+
+#endif   // EDGE_INTEGRATOR_HPP
diff --git a/src/scheme/ExternalBoundaryConditionDescriptor.hpp b/src/scheme/ExternalBoundaryConditionDescriptor.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..056592931b1ce1cf6801f7455d5e8ab3f785dc9f
--- /dev/null
+++ b/src/scheme/ExternalBoundaryConditionDescriptor.hpp
@@ -0,0 +1,62 @@
+#ifndef EXTERNAL_BOUNDARY_CONDITION_DESCRIPTOR_HPP
+#define EXTERNAL_BOUNDARY_CONDITION_DESCRIPTOR_HPP
+
+#include <mesh/IBoundaryDescriptor.hpp>
+#include <scheme/IBoundaryConditionDescriptor.hpp>
+#include <utils/Socket.hpp>
+
+#include <memory>
+
+class ExternalBoundaryConditionDescriptor : public IBoundaryConditionDescriptor
+{
+ private:
+  std::ostream&
+  _write(std::ostream& os) const final
+  {
+    os << "external(" << m_name << ',' << *m_boundary_descriptor << ")";
+    return os;
+  }
+
+  const std::string_view m_name;
+
+  std::shared_ptr<const IBoundaryDescriptor> m_boundary_descriptor;
+  const std::shared_ptr<const Socket> m_socket;
+
+ public:
+  std::string_view
+  name() const
+  {
+    return m_name;
+  }
+
+  const std::shared_ptr<const Socket>&
+  socket() const
+  {
+    return m_socket;
+  }
+
+  const IBoundaryDescriptor&
+  boundaryDescriptor() const final
+  {
+    return *m_boundary_descriptor;
+  }
+
+  Type
+  type() const final
+  {
+    return Type::external;
+  }
+
+  ExternalBoundaryConditionDescriptor(const std::string_view name,
+                                      std::shared_ptr<const IBoundaryDescriptor> boundary_descriptor,
+                                      const std::shared_ptr<const Socket>& socket)
+    : m_name{name}, m_boundary_descriptor(boundary_descriptor), m_socket{socket}
+  {}
+
+  ExternalBoundaryConditionDescriptor(const ExternalBoundaryConditionDescriptor&) = delete;
+  ExternalBoundaryConditionDescriptor(ExternalBoundaryConditionDescriptor&&)      = delete;
+
+  ~ExternalBoundaryConditionDescriptor() = default;
+};
+
+#endif   // EXTERNAL_BOUNDARY_CONDITION_DESCRIPTOR_HPP
diff --git a/src/scheme/FaceIntegrator.hpp b/src/scheme/FaceIntegrator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..657ca7a88a3a959f2856bc34ae8938ab91561785
--- /dev/null
+++ b/src/scheme/FaceIntegrator.hpp
@@ -0,0 +1,326 @@
+#ifndef FACE_INTEGRATOR_HPP
+#define FACE_INTEGRATOR_HPP
+
+#include <analysis/IQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/LineTransformation.hpp>
+#include <geometry/SquareTransformation.hpp>
+#include <geometry/TriangleTransformation.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemId.hpp>
+#include <utils/Array.hpp>
+
+#include <functional>
+
+class FaceIntegrator
+{
+ private:
+  template <size_t Dimension>
+  class AllFaces
+  {
+   private:
+    const Connectivity<Dimension>& m_connectivity;
+
+   public:
+    using index_type = FaceId;
+
+    PUGS_INLINE
+    FaceId
+    faceId(const FaceId face_id) const
+    {
+      return face_id;
+    }
+
+    PUGS_INLINE
+    size_t
+    size() const
+    {
+      return m_connectivity.numberOfFaces();
+    }
+
+    PUGS_INLINE
+    AllFaces(const Connectivity<Dimension>& connectivity) : m_connectivity{connectivity} {}
+  };
+
+  template <template <typename T> typename ArrayT>
+  class FaceList
+  {
+   private:
+    const ArrayT<FaceId>& m_face_list;
+
+   public:
+    using index_type = typename ArrayT<FaceId>::index_type;
+
+    PUGS_INLINE
+    FaceId
+    faceId(const index_type index) const
+    {
+      return m_face_list[index];
+    }
+
+    PUGS_INLINE
+    size_t
+    size() const
+    {
+      return m_face_list.size();
+    }
+
+    PUGS_INLINE
+    FaceList(const ArrayT<FaceId>& face_list) : m_face_list{face_list} {}
+  };
+
+  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  static PUGS_INLINE void
+  _tensorialIntegrateTo(std::function<OutputType(InputType)> function,
+                        const IQuadratureDescriptor& quadrature_descriptor,
+                        const MeshType& mesh,
+                        const ListTypeT& face_list,
+                        OutputArrayT& value)
+  {
+    constexpr size_t Dimension = MeshType::Dimension;
+    static_assert(Dimension > 1);
+
+    static_assert(std::is_same_v<TinyVector<Dimension>, std::decay_t<InputType>>, "invalid input data type");
+    static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>,
+                  "invalid output data type");
+
+    using execution_space = typename Kokkos::DefaultExecutionSpace::execution_space;
+    Kokkos::Experimental::UniqueToken<execution_space, Kokkos::Experimental::UniqueTokenScope::Global> tokens;
+
+    if constexpr (std::is_arithmetic_v<OutputType>) {
+      value.fill(0);
+    } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) {
+      value.fill(zero);
+    } else {
+      static_assert(std::is_same_v<OutputType, double>, "unexpected output type");
+    }
+
+    const auto& connectivity = mesh.connectivity();
+
+    const auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+    const auto xr                  = mesh.xr();
+
+    if constexpr (Dimension == 2) {
+      const auto qf = QuadratureManager::instance().getLineFormula(quadrature_descriptor);
+
+      parallel_for(face_list.size(), [=, &qf, &face_list](typename ListTypeT::index_type index) {
+        const FaceId face_id = face_list.faceId(index);
+
+        const auto face_to_node = face_to_node_matrix[face_id];
+
+        Assert(face_to_node.size() == 2);
+        const LineTransformation<2> T(xr[face_to_node[0]], xr[face_to_node[1]]);
+
+        for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+          const auto xi = qf.point(i_point);
+          value[index] += qf.weight(i_point) * T.velocityNorm() * function(T(xi));
+        }
+      });
+
+    } else if constexpr (Dimension == 3) {
+      const auto qf = QuadratureManager::instance().getSquareFormula(quadrature_descriptor);
+
+      auto invalid_face = std::make_pair(false, FaceId{});
+
+      parallel_for(face_list.size(), [=, &invalid_face, &qf, &face_list](typename ListTypeT::index_type index) {
+        const FaceId face_id = face_list.faceId(index);
+
+        const auto face_to_node = face_to_node_matrix[face_id];
+
+        switch (face_to_node.size()) {
+        case 3: {
+          SquareTransformation<3> T(xr[face_to_node[0]], xr[face_to_node[1]], xr[face_to_node[2]], xr[face_to_node[2]]);
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.areaVariationNorm(xi) * function(T(xi));
+          }
+          break;
+        }
+        case 4: {
+          SquareTransformation<3> T(xr[face_to_node[0]], xr[face_to_node[1]], xr[face_to_node[2]], xr[face_to_node[3]]);
+          for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+            const auto xi = qf.point(i_point);
+            value[index] += qf.weight(i_point) * T.areaVariationNorm(xi) * function(T(xi));
+          }
+          break;
+        }
+          // LCOV_EXCL_START
+        default: {
+          invalid_face = std::make_pair(true, face_id);
+        }
+          // LCOV_EXCL_STOP
+        }
+      });
+
+      // LCOV_EXCL_START
+      if (invalid_face.first) {
+        std::ostringstream os;
+        os << "invalid face type for integration: " << face_to_node_matrix[invalid_face.second].size() << " points";
+        throw UnexpectedError(os.str());
+      }
+      // LCOV_EXCL_STOP
+
+    } else {
+      // LCOV_EXCL_START
+      throw UnexpectedError("invalid dimension for face integration");
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  template <typename MeshType, typename OutputArrayT, typename ListTypeT, typename OutputType, typename InputType>
+  static PUGS_INLINE void
+  _directIntegrateTo(std::function<OutputType(InputType)> function,
+                     const IQuadratureDescriptor& quadrature_descriptor,
+                     const MeshType& mesh,
+                     const ListTypeT& face_list,
+                     OutputArrayT& value)
+  {
+    Assert(not quadrature_descriptor.isTensorial());
+
+    constexpr size_t Dimension = MeshType::Dimension;
+    static_assert(Dimension > 1);
+
+    static_assert(std::is_same_v<TinyVector<Dimension>, std::decay_t<InputType>>, "invalid input data type");
+    static_assert(std::is_same_v<std::remove_const_t<typename OutputArrayT::data_type>, OutputType>,
+                  "invalid output data type");
+
+    if constexpr (std::is_arithmetic_v<OutputType>) {
+      value.fill(0);
+    } else if constexpr (is_tiny_vector_v<OutputType> or is_tiny_matrix_v<OutputType>) {
+      value.fill(zero);
+    } else {
+      static_assert(std::is_same_v<OutputType, double>, "unexpected output type");
+    }
+
+    const auto& connectivity = mesh.connectivity();
+
+    const auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+    const auto xr                  = mesh.xr();
+
+    if constexpr (Dimension == 2) {
+      parallel_for(face_list.size(),
+                   [=, &quadrature_descriptor, &face_list](const typename ListTypeT::index_type& index) {
+                     const FaceId face_id = face_list.faceId(index);
+
+                     const auto face_to_node = face_to_node_matrix[face_id];
+
+                     Assert(face_to_node.size() == 2);
+                     const LineTransformation<2> T(xr[face_to_node[0]], xr[face_to_node[1]]);
+                     const auto qf = QuadratureManager::instance().getLineFormula(quadrature_descriptor);
+
+                     for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                       const auto xi = qf.point(i_point);
+                       value[index] += qf.weight(i_point) * T.velocityNorm() * function(T(xi));
+                     }
+                   });
+
+    } else if constexpr (Dimension == 3) {
+      auto invalid_face = std::make_pair(false, FaceId{});
+
+      parallel_for(face_list.size(),
+                   [=, &invalid_face, &quadrature_descriptor, &face_list](const typename ListTypeT::index_type& index) {
+                     const FaceId face_id = face_list.faceId(index);
+
+                     const auto face_to_node = face_to_node_matrix[face_id];
+
+                     switch (face_to_node.size()) {
+                     case 3: {
+                       const TriangleTransformation<3> T(xr[face_to_node[0]], xr[face_to_node[1]], xr[face_to_node[2]]);
+                       const auto qf = QuadratureManager::instance().getTriangleFormula(quadrature_descriptor);
+
+                       for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                         const auto xi = qf.point(i_point);
+                         value[index] += qf.weight(i_point) * T.areaVariationNorm() * function(T(xi));
+                       }
+                       break;
+                     }
+                     case 4: {
+                       const SquareTransformation<3> T(xr[face_to_node[0]], xr[face_to_node[1]], xr[face_to_node[2]],
+                                                       xr[face_to_node[3]]);
+                       const auto qf = QuadratureManager::instance().getSquareFormula(quadrature_descriptor);
+
+                       for (size_t i_point = 0; i_point < qf.numberOfPoints(); ++i_point) {
+                         const auto xi = qf.point(i_point);
+                         value[index] += qf.weight(i_point) * T.areaVariationNorm(xi) * function(T(xi));
+                       }
+                       break;
+                     }
+                       // LCOV_EXCL_START
+                     default: {
+                       invalid_face = std::make_pair(true, face_id);
+                       break;
+                     }
+                       // LCOV_EXCL_STOP
+                     }
+                   });
+
+      // LCOV_EXCL_START
+      if (invalid_face.first) {
+        std::ostringstream os;
+        os << "invalid face type for integration: " << face_to_node_matrix[invalid_face.second].size() << " points";
+        throw UnexpectedError(os.str());
+      }
+      // LCOV_EXCL_STOP
+    }
+  }
+
+ public:
+  template <typename MeshType, typename OutputArrayT, typename OutputType, typename InputType>
+  static PUGS_INLINE void
+  integrateTo(const std::function<OutputType(InputType)>& function,
+              const IQuadratureDescriptor& quadrature_descriptor,
+              const MeshType& mesh,
+              OutputArrayT& value)
+  {
+    constexpr size_t Dimension = MeshType::Dimension;
+
+    if (quadrature_descriptor.isTensorial()) {
+      _tensorialIntegrateTo<MeshType, OutputArrayT>(function, quadrature_descriptor, mesh,
+                                                    AllFaces<Dimension>{mesh.connectivity()}, value);
+    } else {
+      _directIntegrateTo<MeshType, OutputArrayT>(function, quadrature_descriptor, mesh,
+                                                 AllFaces<Dimension>{mesh.connectivity()}, value);
+    }
+  }
+
+  template <typename MeshType, typename OutputArrayT, typename FunctionType>
+  static PUGS_INLINE void
+  integrateTo(const FunctionType& function,
+              const IQuadratureDescriptor& quadrature_descriptor,
+              const MeshType& mesh,
+              OutputArrayT& value)
+  {
+    integrateTo(std::function(function), quadrature_descriptor, mesh, value);
+  }
+
+  template <typename MeshType, typename OutputType, typename InputType, template <typename DataType> typename ArrayT>
+  static PUGS_INLINE ArrayT<OutputType>
+  integrate(const std::function<OutputType(InputType)>& function,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const ArrayT<FaceId>& face_list)
+  {
+    ArrayT<OutputType> value(size(face_list));
+    if (quadrature_descriptor.isTensorial()) {
+      _tensorialIntegrateTo<MeshType, ArrayT<OutputType>>(function, quadrature_descriptor, mesh, FaceList{face_list},
+                                                          value);
+    } else {
+      _directIntegrateTo<MeshType, ArrayT<OutputType>>(function, quadrature_descriptor, mesh, FaceList{face_list},
+                                                       value);
+    }
+
+    return value;
+  }
+
+  template <typename MeshType, typename FunctionType, template <typename DataType> typename ArrayT>
+  static PUGS_INLINE auto
+  integrate(const FunctionType& function,
+            const IQuadratureDescriptor& quadrature_descriptor,
+            const MeshType& mesh,
+            const ArrayT<FaceId>& face_list)
+  {
+    return integrate(std::function(function), quadrature_descriptor, mesh, face_list);
+  }
+};
+
+#endif   // FACE_INTEGRATOR_HPP
diff --git a/src/scheme/IBoundaryConditionDescriptor.hpp b/src/scheme/IBoundaryConditionDescriptor.hpp
index c0ea161db5d5f869d8ac8517b85ae2385af1d94a..f99eff9355f6fdf6fbd65ad1898f3cba655af284 100644
--- a/src/scheme/IBoundaryConditionDescriptor.hpp
+++ b/src/scheme/IBoundaryConditionDescriptor.hpp
@@ -12,6 +12,7 @@ class IBoundaryConditionDescriptor
   {
     axis,
     dirichlet,
+    external,
     fourier,
     fixed,
     free,
diff --git a/src/scheme/ScalarDiamondScheme.cpp b/src/scheme/ScalarDiamondScheme.cpp
index fee9c1d7390ce01a2f316aa3e472cc1da9f51135..32eafcc7575d8c89b887e326fc2a57f4fff38574 100644
--- a/src/scheme/ScalarDiamondScheme.cpp
+++ b/src/scheme/ScalarDiamondScheme.cpp
@@ -281,11 +281,11 @@ class ScalarDiamondSchemeHandler::ScalarDiamondScheme : public ScalarDiamondSche
                       const std::shared_ptr<const DiscreteFunctionP0<Dimension, double>>& f,
                       const std::vector<std::shared_ptr<const IBoundaryConditionDescriptor>>& bc_descriptor_list)
   {
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mu->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mu->mesh()) {
       throw NormalError("diffusion coefficient is not defined on the dual mesh!");
     }
 
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mub->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mub->mesh()) {
       throw NormalError("boundary diffusion coefficient is not defined on the dual mesh!");
     }
 
@@ -481,13 +481,12 @@ class ScalarDiamondSchemeHandler::ScalarDiamondScheme : public ScalarDiamondSche
       const auto& node_to_face_matrix                  = mesh->connectivity().nodeToFaceMatrix();
 
       {
-        std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+        std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
 
         MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
 
         std::shared_ptr mapper =
-          DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
-            mesh->connectivity());
+          DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity());
 
         CellValue<const double> dual_kappaj  = dual_mu->cellValues();
         CellValue<const double> dual_kappajb = dual_mub->cellValues();
diff --git a/src/scheme/ScalarDiamondScheme.hpp b/src/scheme/ScalarDiamondScheme.hpp
index d10435fc762c4b793f0c6804035b46b97cf11a23..47b66177b5628bb8b13a0ea2d92ede5e41a84950 100644
--- a/src/scheme/ScalarDiamondScheme.hpp
+++ b/src/scheme/ScalarDiamondScheme.hpp
@@ -9,14 +9,14 @@
 #include <algebra/Vector.hpp>
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/MeshFaceBoundary.hpp>
 #include <mesh/MeshNodeBoundary.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <mesh/SubItemValuePerItem.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/FourierBoundaryConditionDescriptor.hpp>
@@ -368,13 +368,12 @@ class LegacyScalarDiamondScheme
       const auto& node_to_face_matrix                  = mesh->connectivity().nodeToFaceMatrix();
 
       {
-        std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+        std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
 
         MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
 
         std::shared_ptr mapper =
-          DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
-            mesh->connectivity());
+          DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity());
 
         CellValue<double> kappaj =
           InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(kappa_id,
diff --git a/src/scheme/VectorDiamondScheme.cpp b/src/scheme/VectorDiamondScheme.cpp
index fbbe3501e5b76b3d4a2664b8705964d6925bbc3b..32cfbdab5904f2f8ceaa025f2539490f6e52a03c 100644
--- a/src/scheme/VectorDiamondScheme.cpp
+++ b/src/scheme/VectorDiamondScheme.cpp
@@ -289,7 +289,7 @@ class VectorDiamondSchemeHandler::VectorDiamondScheme : public VectorDiamondSche
     Assert(dual_lambda->mesh() == dual_mu->mesh());
     Assert(dual_lambdab->mesh() == dual_mu->mesh());
     Assert(dual_mub->mesh() == dual_mu->mesh());
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mu->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mu->mesh()) {
       throw NormalError("diffusion coefficient is not defined on the dual mesh!");
     }
 
@@ -520,13 +520,12 @@ class VectorDiamondSchemeHandler::VectorDiamondScheme : public VectorDiamondSche
       const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr();
 
       {
-        std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+        std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
 
         MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
 
         std::shared_ptr mapper =
-          DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
-            mesh->connectivity());
+          DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity());
 
         CellValue<const double> dual_muj      = dual_mu->cellValues();
         CellValue<const double> dual_lambdaj  = dual_lambda->cellValues();
@@ -1245,7 +1244,7 @@ class EnergyComputerHandler::EnergyComputer : public EnergyComputerHandler::IEne
     Assert(dual_lambdab->mesh() == dual_U->mesh());
     Assert(U->mesh() == source->mesh());
     Assert(dual_lambdab->mesh() == dual_mub->mesh());
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mub->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mub->mesh()) {
       throw NormalError("diffusion coefficient is not defined on the dual mesh!");
     }
 
@@ -1476,13 +1475,12 @@ class EnergyComputerHandler::EnergyComputer : public EnergyComputerHandler::IEne
       const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr();
 
       {
-        std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+        std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
 
         MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
 
         std::shared_ptr mapper =
-          DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
-            mesh->connectivity());
+          DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity());
 
         CellValue<const double> dual_mubj     = dual_mub->cellValues();
         CellValue<const double> dual_lambdabj = dual_lambdab->cellValues();
@@ -1879,7 +1877,7 @@ VectorDiamondSchemeHandler::VectorDiamondSchemeHandler(
 
     std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
 
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mu->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mu->mesh()) {
       throw NormalError("mu_dual is not defined on the dual mesh");
     }
 
@@ -1901,7 +1899,7 @@ VectorDiamondSchemeHandler::VectorDiamondSchemeHandler(
 
     std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
 
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mu->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mu->mesh()) {
       throw NormalError("mu_dual is not defined on the dual mesh");
     }
 
@@ -1923,7 +1921,7 @@ VectorDiamondSchemeHandler::VectorDiamondSchemeHandler(
 
     std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
 
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mu->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mu->mesh()) {
       throw NormalError("mu_dual is not defined on the dual mesh");
     }
 
@@ -1966,7 +1964,7 @@ EnergyComputerHandler::EnergyComputerHandler(
 
     std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
 
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mub->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mub->mesh()) {
       throw NormalError("mu_dual is not defined on the dual mesh");
     }
 
@@ -1987,7 +1985,7 @@ EnergyComputerHandler::EnergyComputerHandler(
 
     std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
 
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mub->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mub->mesh()) {
       throw NormalError("mu_dual is not defined on the dual mesh");
     }
 
@@ -2008,7 +2006,7 @@ EnergyComputerHandler::EnergyComputerHandler(
 
     std::shared_ptr mesh = std::dynamic_pointer_cast<const MeshType>(i_mesh);
 
-    if (DiamondDualMeshManager::instance().getDiamondDualMesh(mesh) != dual_mub->mesh()) {
+    if (DualMeshManager::instance().getDiamondDualMesh(*mesh) != dual_mub->mesh()) {
       throw NormalError("mu_dual is not defined on the dual mesh");
     }
 
diff --git a/src/scheme/VectorDiamondScheme.hpp b/src/scheme/VectorDiamondScheme.hpp
index 261a9b56714bb087995b0bc4b6dd47503bfa2619..20bbadfa3934cabf213d201e47397c97d56047e0 100644
--- a/src/scheme/VectorDiamondScheme.hpp
+++ b/src/scheme/VectorDiamondScheme.hpp
@@ -8,14 +8,14 @@
 #include <algebra/Vector.hpp>
 #include <language/utils/InterpolateItemValue.hpp>
 #include <mesh/Connectivity.hpp>
-#include <mesh/ConnectivityToDiamondDualConnectivityDataMapper.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/Mesh.hpp>
 #include <mesh/MeshData.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/MeshFaceBoundary.hpp>
 #include <mesh/MeshFlatFaceBoundary.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
 #include <output/VTKWriter.hpp>
 #include <scheme/DirichletBoundaryConditionDescriptor.hpp>
 #include <scheme/NeumannBoundaryConditionDescriptor.hpp>
@@ -400,13 +400,12 @@ class LegacyVectorDiamondScheme
       const NodeValuePerFace<const TinyVector<Dimension>> primal_nlr = mesh_data.nlr();
 
       {
-        std::shared_ptr diamond_mesh = DiamondDualMeshManager::instance().getDiamondDualMesh(mesh);
+        std::shared_ptr diamond_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
 
         MeshDataType& diamond_mesh_data = MeshDataManager::instance().getMeshData(*diamond_mesh);
 
         std::shared_ptr mapper =
-          DiamondDualConnectivityManager::instance().getConnectivityToDiamondDualConnectivityDataMapper(
-            mesh->connectivity());
+          DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(mesh->connectivity());
 
         CellValue<double> dual_muj =
           InterpolateItemValue<double(TinyVector<Dimension>)>::template interpolate<ItemType::cell>(mu_id,
diff --git a/src/utils/Array.hpp b/src/utils/Array.hpp
index 54786db88592d7371e7b7fa5e9acd0aaec4fffe5..f78f543080edcfa614e6ad549a73349ffb66da74 100644
--- a/src/utils/Array.hpp
+++ b/src/utils/Array.hpp
@@ -6,6 +6,7 @@
 #include <utils/PugsAssert.hpp>
 #include <utils/PugsMacros.hpp>
 #include <utils/PugsUtils.hpp>
+#include <utils/Types.hpp>
 
 #include <Kokkos_CopyViews.hpp>
 #include <algorithm>
@@ -140,8 +141,15 @@ class [[nodiscard]] Array
   ~Array() = default;
 };
 
+template <typename DataType>
+[[nodiscard]] PUGS_INLINE size_t
+size(const Array<DataType>& array)
+{
+  return array.size();
+}
+
 template <typename DataType, typename... RT>
-PUGS_INLINE Array<DataType>
+[[nodiscard]] PUGS_INLINE Array<DataType>
 encapsulate(const Kokkos::View<DataType*, RT...>& values)
 {
   Array<DataType> array;
@@ -150,7 +158,7 @@ encapsulate(const Kokkos::View<DataType*, RT...>& values)
 }
 
 template <typename DataType>
-PUGS_INLINE Array<DataType>
+[[nodiscard]] PUGS_INLINE Array<DataType>
 subArray(const Array<DataType>& array,
          typename Array<DataType>::index_type begin,
          typename Array<DataType>::index_type size)
@@ -162,7 +170,7 @@ subArray(const Array<DataType>& array,
 
 // map, multimap, unordered_map and stack cannot be copied this way
 template <typename Container>
-PUGS_INLINE Array<std::remove_const_t<typename Container::value_type>>
+[[nodiscard]] PUGS_INLINE Array<std::remove_const_t<typename Container::value_type>>
 convert_to_array(const Container& given_container)
 {
   using DataType = typename Container::value_type;
@@ -173,4 +181,185 @@ convert_to_array(const Container& given_container)
   return array;
 }
 
+template <typename DataType>
+[[nodiscard]] std::remove_const_t<DataType>
+min(const Array<DataType>& array)
+{
+  using ArrayType  = Array<DataType>;
+  using data_type  = std::remove_const_t<typename ArrayType::data_type>;
+  using index_type = typename ArrayType::index_type;
+
+  class ArrayMin
+  {
+   private:
+    const ArrayType m_array;
+
+   public:
+    PUGS_INLINE
+    operator data_type()
+    {
+      data_type reduced_value;
+      parallel_reduce(m_array.size(), *this, reduced_value);
+      return reduced_value;
+    }
+
+    PUGS_INLINE
+    void
+    operator()(const index_type& i, data_type& data) const
+    {
+      if (m_array[i] < data) {
+        data = m_array[i];
+      }
+    }
+
+    PUGS_INLINE
+    void
+    join(volatile data_type& dst, const volatile data_type& src) const
+    {
+      if (src < dst) {
+        dst = src;
+      }
+    }
+
+    PUGS_INLINE
+    void
+    init(data_type& value) const
+    {
+      value = std::numeric_limits<data_type>::max();
+    }
+
+    PUGS_INLINE
+    ArrayMin(const ArrayType& array) : m_array(array)
+    {
+      ;
+    }
+
+    PUGS_INLINE
+    ~ArrayMin() = default;
+  };
+
+  return ArrayMin(array);
+}
+
+template <typename DataType>
+[[nodiscard]] std::remove_const_t<DataType>
+max(const Array<DataType>& array)
+{
+  using ArrayType  = Array<DataType>;
+  using data_type  = std::remove_const_t<typename ArrayType::data_type>;
+  using index_type = typename ArrayType::index_type;
+
+  class ArrayMax
+  {
+   private:
+    const ArrayType m_array;
+
+   public:
+    PUGS_INLINE
+    operator data_type()
+    {
+      data_type reduced_value;
+      parallel_reduce(m_array.size(), *this, reduced_value);
+      return reduced_value;
+    }
+
+    PUGS_INLINE
+    void
+    operator()(const index_type& i, data_type& data) const
+    {
+      if (m_array[i] > data) {
+        data = m_array[i];
+      }
+    }
+
+    PUGS_INLINE
+    void
+    join(volatile data_type& dst, const volatile data_type& src) const
+    {
+      if (src > dst) {
+        dst = src;
+      }
+    }
+
+    PUGS_INLINE
+    void
+    init(data_type& value) const
+    {
+      value = std::numeric_limits<data_type>::min();
+    }
+
+    PUGS_INLINE
+    ArrayMax(const ArrayType& array) : m_array(array)
+    {
+      ;
+    }
+
+    PUGS_INLINE
+    ~ArrayMax() = default;
+  };
+
+  return ArrayMax(array);
+}
+
+template <typename DataType>
+[[nodiscard]] std::remove_const_t<DataType>
+sum(const Array<DataType>& array)
+{
+  using ArrayType  = Array<DataType>;
+  using data_type  = std::remove_const_t<DataType>;
+  using index_type = typename ArrayType::index_type;
+
+  class ArraySum
+  {
+   private:
+    const ArrayType m_array;
+
+   public:
+    PUGS_INLINE
+    operator data_type()
+    {
+      data_type reduced_value;
+      parallel_reduce(m_array.size(), *this, reduced_value);
+      return reduced_value;
+    }
+
+    PUGS_INLINE
+    void
+    operator()(const index_type& i, data_type& data) const
+    {
+      data += m_array[i];
+    }
+
+    PUGS_INLINE
+    void
+    join(volatile data_type& dst, const volatile data_type& src) const
+    {
+      dst += src;
+    }
+
+    PUGS_INLINE
+    void
+    init(data_type& value) const
+    {
+      if constexpr (std::is_arithmetic_v<data_type>) {
+        value = 0;
+      } else {
+        static_assert(is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>, "invalid data type");
+        value = zero;
+      }
+    }
+
+    PUGS_INLINE
+    ArraySum(const ArrayType& array) : m_array(array)
+    {
+      ;
+    }
+
+    PUGS_INLINE
+    ~ArraySum() = default;
+  };
+
+  return ArraySum(array);
+}
+
 #endif   // ARRAY_HPP
diff --git a/src/utils/ArrayUtils.hpp b/src/utils/ArrayUtils.hpp
deleted file mode 100644
index 3b3438294291af1deafe733374e7de9ddf4982b6..0000000000000000000000000000000000000000
--- a/src/utils/ArrayUtils.hpp
+++ /dev/null
@@ -1,211 +0,0 @@
-#ifndef ARRAY_UTILS_HPP
-#define ARRAY_UTILS_HPP
-
-#include <utils/PugsMacros.hpp>
-#include <utils/PugsTraits.hpp>
-#include <utils/PugsUtils.hpp>
-
-#include <utils/Types.hpp>
-
-template <typename DataType, template <typename> typename ArrayT>
-std::remove_const_t<DataType>
-min(const ArrayT<DataType>& array)
-{
-  using ArrayType  = ArrayT<DataType>;
-  using data_type  = std::remove_const_t<typename ArrayType::data_type>;
-  using index_type = typename ArrayType::index_type;
-
-  class ArrayMin
-  {
-   private:
-    const ArrayType m_array;
-
-   public:
-    PUGS_INLINE
-    operator data_type()
-    {
-      data_type reduced_value;
-      parallel_reduce(m_array.size(), *this, reduced_value);
-      return reduced_value;
-    }
-
-    PUGS_INLINE
-    void
-    operator()(const index_type& i, data_type& data) const
-    {
-      if (m_array[i] < data) {
-        data = m_array[i];
-      }
-    }
-
-    PUGS_INLINE
-    void
-    join(volatile data_type& dst, const volatile data_type& src) const
-    {
-      if (src < dst) {
-        dst = src;
-      }
-    }
-
-    PUGS_INLINE
-    void
-    init(data_type& value) const
-    {
-      value = std::numeric_limits<data_type>::max();
-    }
-
-    PUGS_INLINE
-    ArrayMin(const ArrayType& array) : m_array(array)
-    {
-      ;
-    }
-
-    PUGS_INLINE
-    ~ArrayMin() = default;
-  };
-
-  return ArrayMin(array);
-}
-
-template <typename DataType, template <typename> typename ArrayT>
-std::remove_const_t<DataType>
-max(const ArrayT<DataType>& array)
-{
-  using ArrayType  = ArrayT<DataType>;
-  using data_type  = std::remove_const_t<typename ArrayType::data_type>;
-  using index_type = typename ArrayType::index_type;
-
-  class ArrayMax
-  {
-   private:
-    const ArrayType m_array;
-
-   public:
-    PUGS_INLINE
-    operator data_type()
-    {
-      data_type reduced_value;
-      parallel_reduce(m_array.size(), *this, reduced_value);
-      return reduced_value;
-    }
-
-    PUGS_INLINE
-    void
-    operator()(const index_type& i, data_type& data) const
-    {
-      if (m_array[i] > data) {
-        data = m_array[i];
-      }
-    }
-
-    PUGS_INLINE
-    void
-    join(volatile data_type& dst, const volatile data_type& src) const
-    {
-      if (src > dst) {
-        dst = src;
-      }
-    }
-
-    PUGS_INLINE
-    void
-    init(data_type& value) const
-    {
-      value = std::numeric_limits<data_type>::min();
-    }
-
-    PUGS_INLINE
-    ArrayMax(const ArrayType& array) : m_array(array)
-    {
-      ;
-    }
-
-    PUGS_INLINE
-    ~ArrayMax() = default;
-  };
-
-  return ArrayMax(array);
-}
-
-template <typename DataType, template <typename> typename ArrayT>
-std::remove_const_t<DataType>
-sum(const ArrayT<DataType>& array)
-{
-  using ArrayType  = ArrayT<DataType>;
-  using data_type  = std::remove_const_t<DataType>;
-  using index_type = typename ArrayType::index_type;
-
-  class ArraySum
-  {
-   private:
-    const ArrayType m_array;
-
-   public:
-    PUGS_INLINE
-    operator data_type()
-    {
-      data_type reduced_value;
-      parallel_reduce(m_array.size(), *this, reduced_value);
-      return reduced_value;
-    }
-
-    PUGS_INLINE
-    void
-    operator()(const index_type& i, data_type& data) const
-    {
-      data += m_array[i];
-    }
-
-    PUGS_INLINE
-    void
-    join(volatile data_type& dst, const volatile data_type& src) const
-    {
-      dst += src;
-    }
-
-    PUGS_INLINE
-    void
-    init(data_type& value) const
-    {
-      if constexpr (std::is_arithmetic_v<data_type>) {
-        value = 0;
-      } else {
-        static_assert(is_tiny_vector_v<data_type> or is_tiny_matrix_v<data_type>, "invalid data type");
-        value = zero;
-      }
-    }
-
-    PUGS_INLINE
-    ArraySum(const ArrayType& array) : m_array(array)
-    {
-      ;
-    }
-
-    PUGS_INLINE
-    ~ArraySum() = default;
-  };
-
-  return ArraySum(array);
-}
-
-template <template <typename... SourceT> typename SourceArray,
-          template <typename... ImageT>
-          typename ImageArray,
-          typename... SourceT,
-          typename... ImageT>
-void
-value_copy(const SourceArray<SourceT...>& source_array, ImageArray<ImageT...>& image_array)
-{
-  using SourceDataType = typename SourceArray<SourceT...>::data_type;
-  using ImageDataType  = typename ImageArray<ImageT...>::data_type;
-
-  static_assert(std::is_same_v<std::remove_const_t<SourceDataType>, ImageDataType>);
-  static_assert(not std::is_const_v<ImageDataType>);
-
-  Assert(source_array.size() == image_array.size());
-
-  parallel_for(
-    source_array.size(), PUGS_LAMBDA(size_t i) { image_array[i] = source_array[i]; });
-}
-
-#endif   // ARRAY_UTILS_HPP
diff --git a/src/utils/BacktraceManager.cpp b/src/utils/BacktraceManager.cpp
index 26219cbd7b0eb3eb6a451e3ba0587af14445676a..0ba95d6a6edd0e52b059e0b28638384eec13b602 100644
--- a/src/utils/BacktraceManager.cpp
+++ b/src/utils/BacktraceManager.cpp
@@ -8,6 +8,14 @@
 #include <rang.hpp>
 #include <regex>
 
+bool BacktraceManager::s_show = false;
+
+void
+BacktraceManager::setShow(bool show_backtrace)
+{
+  s_show = show_backtrace;
+}
+
 BacktraceManager::BacktraceManager()
 {
   const int size = 100;
@@ -26,28 +34,33 @@ BacktraceManager::BacktraceManager()
 std::ostream&
 operator<<(std::ostream& os, const BacktraceManager& btm)
 {
-  const std::vector<std::string>& lines = btm.m_lines;
-
-  const std::regex mangled_function(R"%(\(.*\+)%");
-  const int width = std::log10(lines.size()) + 1;
-
-  for (size_t i_line = 0; i_line < lines.size(); ++i_line) {
-    const auto& line = lines[i_line];
-    os << rang::fg::green << "[" << std::setw(width) << i_line + 1 << '/' << lines.size() << "] " << rang::fg::reset;
-    std::smatch matchex;
-    if (std::regex_search(line, matchex, mangled_function)) {
-      std::string prefix   = matchex.prefix().str();
-      std::string function = line.substr(matchex.position() + 1, matchex.length() - 2);
-      std::string suffix   = matchex.suffix().str();
-
-      os << prefix << '(';
-      if (function.size() > 0) {
-        os << rang::style::bold << demangle(function) << rang::style::reset;
+  if (BacktraceManager::s_show) {
+    const std::vector<std::string>& lines = btm.m_lines;
+
+    const std::regex mangled_function(R"%(\(.*\+)%");
+    const int width = std::log10(lines.size()) + 1;
+
+    for (size_t i_line = 0; i_line < lines.size(); ++i_line) {
+      const auto& line = lines[i_line];
+      os << rang::fg::green << "[" << std::setw(width) << i_line + 1 << '/' << lines.size() << "] " << rang::fg::reset;
+      std::smatch matchex;
+      if (std::regex_search(line, matchex, mangled_function)) {
+        std::string prefix   = matchex.prefix().str();
+        std::string function = line.substr(matchex.position() + 1, matchex.length() - 2);
+        std::string suffix   = matchex.suffix().str();
+
+        os << prefix << '(';
+        if (function.size() > 0) {
+          os << rang::style::bold << demangle(function) << rang::style::reset;
+        }
+        os << '+' << suffix << '\n';
+      } else {
+        os << line << '\n';
       }
-      os << '+' << suffix << '\n';
-    } else {
-      os << line << '\n';
     }
+  } else {
+    os << rang::fg::yellow << "\n[To display backtrace launch pugs with the --backtrace option]" << rang::style::reset
+       << '\n';
   }
 
   return os;
diff --git a/src/utils/BacktraceManager.hpp b/src/utils/BacktraceManager.hpp
index 0ff2d2d42575ee42498a33fa2fd1c235a3e20b22..e990ffd3b4060f71370a0f8b7386cdfcd6c03ea8 100644
--- a/src/utils/BacktraceManager.hpp
+++ b/src/utils/BacktraceManager.hpp
@@ -7,9 +7,12 @@
 class BacktraceManager
 {
  private:
+  static bool s_show;
   std::vector<std::string> m_lines;
 
  public:
+  static void setShow(bool show_backtrace);
+
   BacktraceManager();
 
   friend std::ostream& operator<<(std::ostream& os, const BacktraceManager& btm);
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
index 19feb273c4a6e2ad09818f467caa9f0f79636b35..741b0d5104cef791cf045fd8cc76cd8b16db30a2 100644
--- a/src/utils/CMakeLists.txt
+++ b/src/utils/CMakeLists.txt
@@ -15,7 +15,8 @@ add_library(
   RandomEngine.cpp
   RevisionInfo.cpp
   SignalManager.cpp
-  SLEPcWrapper.cpp)
+  SLEPcWrapper.cpp
+  Socket.cpp)
 
 target_link_libraries(
   PugsUtils
diff --git a/src/utils/CastArray.hpp b/src/utils/CastArray.hpp
index d329b4ef35f70f3a612ca430c45d0ec23bb7ed9d..07c696428573db04f05186464d0891851c686756 100644
--- a/src/utils/CastArray.hpp
+++ b/src/utils/CastArray.hpp
@@ -32,6 +32,19 @@ class [[nodiscard]] CastArray
     return m_values[i];
   }
 
+  template <typename ImageDataType, typename ImageCastDataType>
+  friend PUGS_INLINE void copy_to(const CastArray& source_array,
+                                  CastArray<ImageDataType, ImageCastDataType>& image_array)
+  {
+    static_assert(std::is_same_v<std::remove_const_t<CastDataType>, ImageCastDataType>);
+    static_assert(not std::is_const_v<ImageCastDataType>);
+
+    Assert(source_array.size() == image_array.size());
+    if (source_array.size() > 0) {
+      std::copy(source_array.m_values, source_array.m_values + source_array.size(), &image_array[0]);
+    }
+  }
+
   PUGS_INLINE
   CastArray& operator=(const CastArray&) = default;
 
diff --git a/src/utils/Messenger.hpp b/src/utils/Messenger.hpp
index 7e8b30c1a8e92c05fc30178e325110617c73c823..44772529e8539814eb30b36dad7b936e4c0269a6 100644
--- a/src/utils/Messenger.hpp
+++ b/src/utils/Messenger.hpp
@@ -5,7 +5,6 @@
 #include <utils/PugsMacros.hpp>
 
 #include <utils/Array.hpp>
-#include <utils/ArrayUtils.hpp>
 #include <utils/CastArray.hpp>
 
 #include <type_traits>
@@ -20,8 +19,6 @@
 #include <utils/Exceptions.hpp>
 #include <utils/PugsTraits.hpp>
 
-#include <iostream>
-
 namespace parallel
 {
 class Messenger
@@ -136,7 +133,7 @@ class Messenger
     MPI_Gather(data_address, data_array.size(), mpi_datatype, gather_address, data_array.size(), mpi_datatype, rank,
                MPI_COMM_WORLD);
 #else    // PUGS_HAS_MPI
-    value_copy(data_array, gather_array);
+    copy_to(data_array, gather_array);
 #endif   // PUGS_HAS_MPI
   }
 
@@ -177,7 +174,7 @@ class Messenger
     MPI_Gatherv(data_address, data_array.size(), mpi_datatype, gather_address, sizes_address, positions_address,
                 mpi_datatype, rank, MPI_COMM_WORLD);
 #else    // PUGS_HAS_MPI
-    value_copy(data_array, gather_array);
+    copy_to(data_array, gather_array);
 #endif   // PUGS_HAS_MPI
   }
 
@@ -222,7 +219,7 @@ class Messenger
     MPI_Allgather(data_address, data_array.size(), mpi_datatype, gather_address, data_array.size(), mpi_datatype,
                   MPI_COMM_WORLD);
 #else    // PUGS_HAS_MPI
-    value_copy(data_array, gather_array);
+    copy_to(data_array, gather_array);
 #endif   // PUGS_HAS_MPI
   }
 
@@ -262,7 +259,7 @@ class Messenger
     MPI_Allgatherv(data_address, data_array.size(), mpi_datatype, gather_address, sizes_address, positions_address,
                    mpi_datatype, MPI_COMM_WORLD);
 #else    // PUGS_HAS_MPI
-    value_copy(data_array, gather_array);
+    copy_to(data_array, gather_array);
 #endif   // PUGS_HAS_MPI
   }
 
@@ -323,7 +320,7 @@ class Messenger
 
     MPI_Alltoall(sent_address, count, mpi_datatype, recv_address, count, mpi_datatype, MPI_COMM_WORLD);
 #else    // PUGS_HAS_MPI
-    value_copy(sent_array, recv_array);
+    copy_to(sent_array, recv_array);
 #endif   // PUGS_HAS_MPI
   }
 
@@ -378,7 +375,7 @@ class Messenger
     Assert(sent_array_list.size() == 1);
     Assert(recv_array_list.size() == 1);
 
-    value_copy(sent_array_list[0], recv_array_list[0]);
+    copy_to(sent_array_list[0], recv_array_list[0]);
 #endif   // PUGS_HAS_MPI
   }
 
diff --git a/src/utils/PugsUtils.cpp b/src/utils/PugsUtils.cpp
index 696dd8f86a2409bb5c456beeecb3063263d4f699..3214ba653989b13805181b7c54e3502182cf9e63 100644
--- a/src/utils/PugsUtils.cpp
+++ b/src/utils/PugsUtils.cpp
@@ -1,5 +1,6 @@
 #include <utils/PugsUtils.hpp>
 
+#include <utils/BacktraceManager.hpp>
 #include <utils/BuildInfo.hpp>
 #include <utils/ConsoleManager.hpp>
 #include <utils/FPEManager.hpp>
@@ -65,8 +66,8 @@ pugsBuildInfo()
 void
 setDefaultOMPEnvironment()
 {
-  if constexpr (std::string_view{PUGS_BUILD_KOKKOS_DEVICES} == std::string_view{"OpenMP"}) {
-    setenv("OMP_PROC_BIND", "spread", 0);
+  if constexpr (std::string_view{PUGS_BUILD_KOKKOS_DEVICES}.find("OpenMP") != std::string_view::npos) {
+    setenv("OMP_PROC_BIND", "spread,close", 0);
     setenv("OMP_PLACES", "threads", 0);
   }
 }
@@ -104,6 +105,9 @@ initialize(int& argc, char* argv[])
 
     app.add_flag("--fpe,!--no-fpe", enable_fpe, "Trap floating point exceptions [default: true]");
 
+    bool show_backtrace = false;
+    app.add_flag("-b,--backtrace,!--no-backtrace", show_backtrace, "Show backtrace on failure [default: false]");
+
     app.add_flag("--signal,!--no-signal", enable_signals, "Catches signals [default: true]");
 
     bool pause_on_error = false;
@@ -118,6 +122,7 @@ initialize(int& argc, char* argv[])
       std::exit(app.exit(e, std::cout, std::cerr));
     }
 
+    BacktraceManager::setShow(show_backtrace);
     ConsoleManager::init(enable_color);
     SignalManager::setPauseForDebug(pause_on_error);
   }
diff --git a/src/utils/SmallArray.hpp b/src/utils/SmallArray.hpp
index b5186a7f6341606a92084fd7ee5521b5d252b4ee..9e0782f1c0adb0b9bfa4bcf25bcaa8977a3800d4 100644
--- a/src/utils/SmallArray.hpp
+++ b/src/utils/SmallArray.hpp
@@ -6,6 +6,7 @@
 #include <utils/PugsAssert.hpp>
 #include <utils/PugsMacros.hpp>
 #include <utils/PugsUtils.hpp>
+#include <utils/Types.hpp>
 
 #include <algorithm>
 
@@ -128,6 +129,13 @@ class [[nodiscard]] SmallArray
   ~SmallArray() = default;
 };
 
+template <typename DataType>
+PUGS_INLINE size_t
+size(const SmallArray<DataType>& array)
+{
+  return array.size();
+}
+
 // map, multimap, unordered_map and stack cannot be copied this way
 template <typename Container>
 PUGS_INLINE SmallArray<std::remove_const_t<typename Container::value_type>>
@@ -141,4 +149,60 @@ convert_to_small_array(const Container& given_container)
   return array;
 }
 
+template <typename DataType>
+[[nodiscard]] std::remove_const_t<DataType>
+min(const SmallArray<DataType>& array)
+{
+  using data_type  = std::remove_const_t<DataType>;
+  using index_type = typename SmallArray<DataType>::index_type;
+
+  data_type min_value = std::numeric_limits<data_type>::max();
+  for (index_type i = 0; i < array.size(); ++i) {
+    if (array[i] < min_value) {
+      min_value = array[i];
+    }
+  }
+  return min_value;
+}
+
+template <typename DataType>
+[[nodiscard]] std::remove_const_t<DataType>
+max(const SmallArray<DataType>& array)
+{
+  using data_type  = std::remove_const_t<DataType>;
+  using index_type = typename SmallArray<DataType>::index_type;
+
+  data_type max_value = -std::numeric_limits<data_type>::max();
+  for (index_type i = 0; i < array.size(); ++i) {
+    if (array[i] > max_value) {
+      max_value = array[i];
+    }
+  }
+  return max_value;
+}
+
+template <typename DataType>
+[[nodiscard]] std::remove_const_t<DataType>
+sum(const SmallArray<DataType>& array)
+{
+  using data_type  = std::remove_const_t<DataType>;
+  using index_type = typename SmallArray<DataType>::index_type;
+
+  data_type sum_value = [] {
+    if constexpr (std::is_arithmetic_v<DataType>) {
+      return 0;
+    } else if constexpr (is_tiny_vector_v<DataType> or is_tiny_matrix_v<DataType>) {
+      return zero;
+    } else {
+      static_assert(std::is_arithmetic_v<DataType>, "invalid data type");
+    }
+  }();
+
+  for (index_type i = 0; i < array.size(); ++i) {
+    sum_value += array[i];
+  }
+
+  return sum_value;
+}
+
 #endif   // SMALL_ARRAY_HPP
diff --git a/src/utils/Socket.cpp b/src/utils/Socket.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3582e359be22a257b2b52109f7a3ed57b2944a1b
--- /dev/null
+++ b/src/utils/Socket.cpp
@@ -0,0 +1,220 @@
+#include <utils/Exceptions.hpp>
+#include <utils/Socket.hpp>
+
+#include <utils/Messenger.hpp>
+
+#include <arpa/inet.h>
+#include <cstring>
+#include <iostream>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <stdexcept>
+#include <sys/socket.h>
+#include <unistd.h>
+
+class Socket::Internals
+{
+ private:
+  int m_socket_fd;
+  sockaddr_in m_address;
+
+  const bool m_is_server_socket;
+
+ public:
+  friend Socket createServerSocket(int port_number);
+  friend Socket acceptClientSocket(const Socket& server);
+  friend Socket connectServerSocket(const std::string& server_name, int port_number);
+
+  friend std::ostream&
+  operator<<(std::ostream& os, const Socket::Internals& internals)
+  {
+    // This function's coverage is not performed since it's quite
+    // complex to create its various conditions
+    //
+    // LCOV_EXCL_START
+    char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
+    if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf,
+                      sizeof(hbuf), sbuf, sizeof(sbuf), NI_NAMEREQD) == 0) {
+      os << hbuf << ':' << sbuf;
+    } else if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf,
+                             sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0) {
+      if (std ::string{hbuf} == "0.0.0.0") {
+        if (::gethostname(hbuf, NI_MAXHOST) == 0) {
+          os << hbuf << ':' << sbuf;
+        } else {
+          os << "localhost:" << sbuf;
+        }
+      } else {
+        os << hbuf << ':' << sbuf;
+      }
+    } else {
+      os << "<unknown host>";
+    }
+    // LCOV_EXCL_STOP
+    return os;
+  }
+
+  bool
+  isServerSocket() const
+  {
+    return m_is_server_socket;
+  }
+
+  int
+  portNumber() const
+  {
+    return ::ntohs(m_address.sin_port);
+  }
+
+  int
+  fileDescriptor() const
+  {
+    return m_socket_fd;
+  }
+
+  Internals(const Internals&) = delete;
+  Internals(Internals&&)      = delete;
+
+  Internals(bool is_server_socket = false) : m_is_server_socket{is_server_socket}
+  {
+    if (parallel::size() > 1) {
+      // LCOV_EXCL_START
+      throw NotImplementedError("Sockets are not managed in parallel");
+      // LCOV_EXCL_STOP
+    }
+  }
+
+  ~Internals()
+  {
+    close(m_socket_fd);
+  }
+};
+
+std::ostream&
+operator<<(std::ostream& os, const Socket& socket)
+{
+  return os << *socket.m_internals;
+}
+
+int
+Socket::portNumber() const
+{
+  return m_internals->portNumber();
+}
+
+Socket
+createServerSocket(int port_number)
+{
+  auto p_socket_internals             = std::make_shared<Socket::Internals>(true);
+  Socket::Internals& socket_internals = *p_socket_internals;
+
+  socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
+  if (socket_internals.m_socket_fd < 0) {
+    // This should never happen
+    throw UnexpectedError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+
+  socket_internals.m_address.sin_family      = AF_INET;
+  socket_internals.m_address.sin_addr.s_addr = INADDR_ANY;
+  socket_internals.m_address.sin_port        = htons(port_number);
+
+  int on = 1;
+  ::setsockopt(socket_internals.m_socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+  socklen_t length = sizeof(socket_internals.m_address);
+
+  if (::bind(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&socket_internals.m_address), length) < 0) {
+    throw NormalError(strerror(errno));
+  }
+
+  if (::getsockname(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&socket_internals.m_address), &length) ==
+      -1) {
+    // This should never happen
+    throw UnexpectedError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+
+  ::listen(socket_internals.m_socket_fd, 1);
+
+  return Socket{p_socket_internals};
+}
+
+Socket
+acceptClientSocket(const Socket& server)
+{
+  auto p_socket_internals             = std::make_shared<Socket::Internals>();
+  Socket::Internals& socket_internals = *p_socket_internals;
+
+  socklen_t address_lenght     = sizeof(socket_internals.m_address);
+  socket_internals.m_socket_fd = ::accept(server.m_internals->m_socket_fd,
+                                          reinterpret_cast<sockaddr*>(&socket_internals.m_address), &address_lenght);
+
+  if (socket_internals.m_socket_fd < 0) {
+    // This should never happen
+    throw UnexpectedError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+
+  return Socket{p_socket_internals};
+}
+
+Socket
+connectServerSocket(const std::string& server_name, int port_number)
+{
+  auto p_socket_internals             = std::make_shared<Socket::Internals>();
+  Socket::Internals& socket_internals = *p_socket_internals;
+
+  socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
+  if (socket_internals.m_socket_fd < 0) {
+    // This should never happen
+    throw UnexpectedError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+
+  hostent* server = ::gethostbyname(server_name.c_str());
+  if (server == NULL) {
+    throw NormalError(strerror(errno));
+  }
+
+  sockaddr_in& serv_addr = socket_internals.m_address;
+  ::memset(reinterpret_cast<char*>(&serv_addr), 0, sizeof(serv_addr));
+  serv_addr.sin_family = AF_INET;
+
+  ::memcpy(reinterpret_cast<char*>(&serv_addr.sin_addr.s_addr), reinterpret_cast<char*>(server->h_addr),
+           server->h_length);
+
+  serv_addr.sin_port = ::htons(port_number);
+
+  if (::connect(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&serv_addr), sizeof(serv_addr))) {
+    throw NormalError(strerror(errno));
+  }
+
+  return Socket{p_socket_internals};
+}
+
+void
+Socket::_write(const char* data, const size_t lenght) const
+{
+  if (this->m_internals->isServerSocket()) {
+    throw NormalError("Server cannot write to server socket");
+  }
+
+  if (::write(this->m_internals->fileDescriptor(), data, lenght) < 0) {
+    // Quite complex to test
+    throw NormalError(strerror(errno));   // LCOV_EXCL_LINE
+  }
+}
+
+void
+Socket::_read(char* data, const size_t length) const
+{
+  if (this->m_internals->isServerSocket()) {
+    throw NormalError("Server cannot read from server socket");
+  }
+
+  size_t received = 0;
+  while (received < length) {
+    int n = ::read(this->m_internals->fileDescriptor(), reinterpret_cast<char*>(data) + received, length - received);
+    if (n <= 0) {
+      throw NormalError("Could not read data");
+    }
+    received += n;
+  }
+}
diff --git a/src/utils/Socket.hpp b/src/utils/Socket.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e6d25ddd1328d559e4786e677aff8cde9af06549
--- /dev/null
+++ b/src/utils/Socket.hpp
@@ -0,0 +1,86 @@
+#ifndef SOCKET_HPP
+#define SOCKET_HPP
+
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <utils/PugsTraits.hpp>
+
+class Socket
+{
+ private:
+  class Internals;
+
+  std::shared_ptr<Internals> m_internals;
+
+  Socket(std::shared_ptr<Internals> internals) : m_internals{internals} {}
+
+  void _write(const char* data, const size_t lenght) const;
+  void _read(char* data, const size_t lenght) const;
+
+ public:
+  int portNumber() const;
+
+  friend std::ostream& operator<<(std::ostream& os, const Socket& s);
+
+  friend Socket createServerSocket(int port_number);
+  friend Socket acceptClientSocket(const Socket& server);
+  friend Socket connectServerSocket(const std::string& server_name, int port_number);
+
+  template <typename T>
+  friend void write(const Socket& socket, const T& value);
+
+  template <typename T>
+  friend void read(const Socket& socket, T& value);
+
+  template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
+  friend void write(const Socket& socket, const ArrayT<T, R...>& array);
+
+  template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
+  friend void read(const Socket& socket, ArrayT<T, R...>& array);
+
+  Socket(Socket&&)      = default;
+  Socket(const Socket&) = default;
+
+  ~Socket() = default;
+};
+
+Socket createServerSocket(int port_number);
+Socket acceptClientSocket(const Socket& server);
+Socket connectServerSocket(const std::string& server_name, int port_number);
+
+template <typename T>
+inline void
+write(const Socket& socket, const T& value)
+{
+  static_assert(std::is_arithmetic_v<T> or is_tiny_vector_v<T> or is_tiny_matrix_v<T>, "unexpected value type");
+  socket._write(reinterpret_cast<const char*>(&value), sizeof(T) / sizeof(char));
+}
+
+template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
+void
+write(const Socket& socket, const ArrayT<T, R...>& array)
+{
+  socket._write(reinterpret_cast<const char*>(&array[0]), array.size() * sizeof(T) / sizeof(char));
+}
+
+template <typename T>
+inline void
+read(const Socket& socket, T& value)
+{
+  static_assert(not std::is_const_v<T>, "cannot read values into const data");
+  socket._read(reinterpret_cast<char*>(&value), sizeof(T) / sizeof(char));
+}
+
+template <template <typename T, typename... R> typename ArrayT, typename T, typename... R>
+void
+read(const Socket& socket, ArrayT<T, R...>& array)
+{
+  static_assert(not std::is_const_v<T>, "cannot read values into const data");
+  static_assert(std::is_arithmetic_v<T> or is_tiny_vector_v<T> or is_tiny_matrix_v<T>, "unexpected value type");
+  if (array.size() > 0) {
+    socket._read(reinterpret_cast<char*>(&array[0]), array.size() * sizeof(T) / sizeof(char));
+  }
+}
+
+#endif   // SOCKET_HPP
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 4bac187608a6a5bfd19f058b6f8a00683f852f05..ddfa977a6ef8ca76df9f27bbdc2bda86395cc67f 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -11,7 +11,6 @@ add_executable (unit_tests
   test_AffectationToTupleProcessor.cpp
   test_Array.cpp
   test_ArraySubscriptProcessor.cpp
-  test_ArrayUtils.cpp
   test_ASTBuilder.cpp
   test_ASTDotPrinter.cpp
   test_ASTModulesImporter.cpp
@@ -44,6 +43,9 @@ add_executable (unit_tests
   test_BinaryExpressionProcessor_comparison.cpp
   test_BinaryExpressionProcessor_equality.cpp
   test_BinaryExpressionProcessor_logic.cpp
+  test_BinaryExpressionProcessor_raw.cpp
+  test_BinaryExpressionProcessor_shift.cpp
+  test_BinaryOperatorMangler.cpp
   test_BiCGStab.cpp
   test_BuildInfo.cpp
   test_BuiltinFunctionEmbedder.cpp
@@ -51,63 +53,115 @@ add_executable (unit_tests
   test_BuiltinFunctionEmbedderTable.cpp
   test_BuiltinFunctionProcessor.cpp
   test_CastArray.cpp
+  test_CellIntegrator.cpp
+  test_CellType.cpp
   test_ConsoleManager.cpp
   test_CG.cpp
-  test_ContinueProcessor.cpp
   test_ConcatExpressionProcessor.cpp
+  test_ConsoleManager.cpp
+  test_ContinueProcessor.cpp
   test_CRSGraph.cpp
   test_CRSMatrix.cpp
   test_CRSMatrixDescriptor.cpp
+  test_CubeGaussQuadrature.cpp
+  test_CubeTransformation.cpp
   test_DataVariant.cpp
   test_Demangle.cpp
+  test_DiamondDualConnectivityBuilder.cpp
+  test_DiamondDualMeshBuilder.cpp
   test_DiscreteFunctionDescriptorP0.cpp
   test_DiscreteFunctionDescriptorP0Vector.cpp
   test_DiscreteFunctionType.cpp
+  test_DiscreteFunctionUtils.cpp
   test_DoWhileProcessor.cpp
+  test_Dual1DConnectivityBuilder.cpp
+  test_Dual1DMeshBuilder.cpp
+  test_DualConnectivityManager.cpp
+  test_DualMeshManager.cpp
+  test_DualMeshType.cpp
+  test_EdgeIntegrator.cpp
   test_EigenvalueSolver.cpp
   test_EmbeddedData.cpp
+  test_EmbeddedIDiscreteFunctionUtils.cpp
   test_EscapedString.cpp
   test_Exceptions.cpp
   test_ExecutionPolicy.cpp
+  test_FaceIntegrator.cpp
   test_FakeProcessor.cpp
   test_ForProcessor.cpp
   test_FunctionArgumentConverter.cpp
   test_FunctionProcessor.cpp
   test_FunctionSymbolId.cpp
   test_FunctionTable.cpp
+  test_GaussLegendreQuadratureDescriptor.cpp
+  test_GaussLobattoQuadratureDescriptor.cpp
+  test_GaussQuadratureDescriptor.cpp
   test_IfProcessor.cpp
   test_IncDecExpressionProcessor.cpp
+  test_IntegrateCellArray.cpp
+  test_IntegrateCellValue.cpp
+  test_IntegrateOnCells.cpp
   test_INodeProcessor.cpp
+  test_ItemId.cpp
   test_ItemType.cpp
   test_LinearSolver.cpp
   test_LinearSolverOptions.cpp
+  test_LineTransformation.cpp
   test_ListAffectationProcessor.cpp
   test_MathModule.cpp
+  test_MedianDualConnectivityBuilder.cpp
+  test_MedianDualMeshBuilder.cpp
   test_NameProcessor.cpp
   test_NaNHelper.cpp
   test_OStream.cpp
   test_ParseError.cpp
   test_PETScUtils.cpp
+  test_PrimalToDiamondDualConnectivityDataMapper.cpp
+  test_PrimalToDual1DConnectivityDataMapper.cpp
+  test_PrimalToMedianDualConnectivityDataMapper.cpp
+  test_PrismGaussQuadrature.cpp
+  test_PrismTransformation.cpp
   test_PugsAssert.cpp
   test_PugsFunctionAdapter.cpp
   test_PugsUtils.cpp
+  test_PyramidGaussQuadrature.cpp
+  test_PyramidTransformation.cpp
+  test_QuadratureType.cpp
+  test_RefId.cpp
+  test_RefItemList.cpp
   test_RevisionInfo.cpp
   test_SmallArray.cpp
+  test_SmallVector.cpp
+  test_Socket.cpp
+  test_SocketModule.cpp
+  test_SquareGaussQuadrature.cpp
+  test_SquareTransformation.cpp
   test_SymbolTable.cpp
   test_Table.cpp
+  test_TetrahedronGaussQuadrature.cpp
+  test_TetrahedronTransformation.cpp
+  test_TensorialGaussLegendreQuadrature.cpp
+  test_TensorialGaussLobattoQuadrature.cpp
   test_Timer.cpp
   test_TinyMatrix.cpp
   test_TinyVector.cpp
+  test_TriangleGaussQuadrature.cpp
+  test_TriangleTransformation.cpp
   test_TupleToVectorProcessor.cpp
   test_UnaryExpressionProcessor.cpp
+  test_UnaryOperatorMangler.cpp
   test_Vector.cpp
   test_WhileProcessor.cpp
   )
 
 add_executable (mpi_unit_tests
   mpi_test_main.cpp
+  test_DiscreteFunctionInterpoler.cpp
   test_DiscreteFunctionP0.cpp
   test_DiscreteFunctionP0Vector.cpp
+  test_DiscreteFunctionVectorInterpoler.cpp
+  test_EmbeddedIDiscreteFunctionMathFunctions.cpp
+  test_EmbeddedIDiscreteFunctionOperators.cpp
   test_InterpolateItemArray.cpp
   test_InterpolateItemValue.cpp
   test_ItemArray.cpp
@@ -120,6 +174,7 @@ add_executable (mpi_unit_tests
   test_RandomEngine.cpp
   test_SubItemValuePerItem.cpp
   test_SubItemArrayPerItem.cpp
+  test_Synchronizer.cpp
   )
 
 add_library(test_Pugs_MeshDataBase
@@ -134,6 +189,7 @@ target_link_libraries (unit_tests
   PugsLanguage
   PugsMesh
   PugsAlgebra
+  PugsAnalysis
   PugsScheme
   PugsOutput
   PugsUtils
@@ -149,6 +205,7 @@ target_link_libraries (unit_tests
 target_link_libraries (mpi_unit_tests
   test_Pugs_MeshDataBase
   PugsAlgebra
+  PugsAnalysis
   PugsUtils
   PugsLanguage
   PugsLanguageAST
diff --git a/tests/FixturesForBuiltinT.hpp b/tests/FixturesForBuiltinT.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2ff0fcb3b0db1e37a9a5f00487eb8288f1aec3d
--- /dev/null
+++ b/tests/FixturesForBuiltinT.hpp
@@ -0,0 +1,68 @@
+#ifndef FIXTURES_FOR_BUILTIN_T_HPP
+#define FIXTURES_FOR_BUILTIN_T_HPP
+
+#include <language/utils/ASTNodeDataTypeTraits.hpp>
+
+#include <memory>
+#include <stdexcept>
+
+template <>
+inline ASTNodeDataType ast_node_data_type_from<std::shared_ptr<const double>> =
+  ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t");
+const auto builtin_data_type = ast_node_data_type_from<std::shared_ptr<const double>>;
+
+inline std::shared_ptr<const double>
+operator-(const std::shared_ptr<const double>& p_a)
+{
+  return std::make_shared<double>(-*p_a);
+}
+
+inline std::shared_ptr<const double>
+operator+(const std::shared_ptr<const double>& p_a, const std::shared_ptr<const double>& p_b)
+{
+  return std::make_shared<double>(*p_a + *p_b);
+}
+
+inline std::shared_ptr<const double>
+operator+(std::shared_ptr<const double> p_a, double b)
+{
+  return std::make_shared<double>(*p_a + b);
+}
+
+inline std::shared_ptr<const double>
+operator+(double a, const std::shared_ptr<const double>& p_b)
+{
+  return std::make_shared<double>(a + *p_b);
+}
+
+inline std::shared_ptr<const double>
+operator/(const std::shared_ptr<const double>&, const std::shared_ptr<const double>&)
+{
+  throw std::runtime_error("runtime error both");
+}
+
+inline std::shared_ptr<const double>
+operator/(const std::shared_ptr<const double>&, double)
+{
+  throw std::runtime_error("runtime error lhs");
+}
+
+inline std::shared_ptr<const double>
+operator/(double, const std::shared_ptr<const double>&)
+{
+  throw std::runtime_error("runtime error rhs");
+}
+
+inline std::shared_ptr<const double>
+operator<<(const std::shared_ptr<const double>& p_a, const std::shared_ptr<const double>& p_b)
+{
+  return std::make_shared<double>(static_cast<int>(*p_a) << static_cast<int>(*p_b));
+}
+
+inline std::shared_ptr<const double>
+operator>>(const std::shared_ptr<const double>& p_a, const std::shared_ptr<const double>& p_b)
+{
+  return std::make_shared<double>(static_cast<int>(*p_a) >> static_cast<int>(*p_b));
+}
+
+#endif   // FIXTURES_FOR_BUILTIN_T_HPP
diff --git a/tests/MeshDataBaseForTests.cpp b/tests/MeshDataBaseForTests.cpp
index 95399d4564f38313f443f144cf214298577f315a..ab9d957cfe8dc0f1d178643884369c150ad7e239 100644
--- a/tests/MeshDataBaseForTests.cpp
+++ b/tests/MeshDataBaseForTests.cpp
@@ -1,8 +1,13 @@
 #include <MeshDataBaseForTests.hpp>
 #include <mesh/CartesianMeshBuilder.hpp>
 #include <mesh/Connectivity.hpp>
+#include <mesh/GmshReader.hpp>
+#include <utils/Messenger.hpp>
 #include <utils/PugsAssert.hpp>
 
+#include <filesystem>
+#include <fstream>
+
 const MeshDataBaseForTests* MeshDataBaseForTests::m_instance = nullptr;
 
 MeshDataBaseForTests::MeshDataBaseForTests()
@@ -15,6 +20,10 @@ MeshDataBaseForTests::MeshDataBaseForTests()
 
   m_cartesian_3d_mesh = std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(
     CartesianMeshBuilder{TinyVector<3>{0, 1, 0}, TinyVector<3>{2, -1, 3}, TinyVector<3, size_t>{6, 7, 4}}.mesh());
+
+  m_unordered_1d_mesh = _buildUnordered1dMesh();
+  m_hybrid_2d_mesh    = _buildHybrid2dMesh();
+  m_hybrid_3d_mesh    = _buildHybrid3dMesh();
 }
 
 const MeshDataBaseForTests&
@@ -39,19 +48,943 @@ MeshDataBaseForTests::destroy()
 }
 
 std::shared_ptr<const Mesh<Connectivity<1>>>
-MeshDataBaseForTests::cartesianMesh1D() const
+MeshDataBaseForTests::cartesian1DMesh() const
 {
   return m_cartesian_1d_mesh;
 }
 
 std::shared_ptr<const Mesh<Connectivity<2>>>
-MeshDataBaseForTests::cartesianMesh2D() const
+MeshDataBaseForTests::cartesian2DMesh() const
 {
   return m_cartesian_2d_mesh;
 }
 
 std::shared_ptr<const Mesh<Connectivity<3>>>
-MeshDataBaseForTests::cartesianMesh3D() const
+MeshDataBaseForTests::cartesian3DMesh() const
 {
   return m_cartesian_3d_mesh;
 }
+
+std::shared_ptr<const Mesh<Connectivity<1>>>
+MeshDataBaseForTests::unordered1DMesh() const
+{
+  return m_unordered_1d_mesh;
+}
+
+std::shared_ptr<const Mesh<Connectivity<2>>>
+MeshDataBaseForTests::hybrid2DMesh() const
+{
+  return m_hybrid_2d_mesh;
+}
+
+std::shared_ptr<const Mesh<Connectivity<3>>>
+MeshDataBaseForTests::hybrid3DMesh() const
+{
+  return m_hybrid_3d_mesh;
+}
+
+std::shared_ptr<const Mesh<Connectivity<1>>>
+MeshDataBaseForTests::_buildUnordered1dMesh()
+{
+  const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("unordered-1d.msh");
+  if (parallel::rank() == 0) {
+    std::ofstream fout(filename);
+    fout << R"($MeshFormat
+2.2 0 8
+$EndMeshFormat
+$PhysicalNames
+5
+0 1 "XMIN"
+0 2 "XMAX"
+0 3 "INTERFACE"
+1 4 "LEFT"
+1 5 "RIGHT"
+$EndPhysicalNames
+$Nodes
+35
+1 0 0 0
+2 -1 0 0
+3 1 0 0
+4 0.03246387076421061 0 0
+5 0.07692961499792127 0 0
+6 0.1378343163027127 0 0
+7 0.2212554508344625 0 0
+8 0.3355173320316702 0 0
+9 0.4920217492706127 0 0
+10 0.7063857794730655 0 0
+11 -0.9513615311521172 0 0
+12 -0.9036693300347614 0 0
+13 -0.8569049934960202 0 0
+14 -0.8110504814343311 0 0
+15 -0.7660880891035384 0 0
+16 -0.7220004662902741 0 0
+17 -0.6787705699229041 0 0
+18 -0.6363817403249178 0 0
+19 -0.5948175927947426 0 0
+20 -0.5540620983944274 0 0
+21 -0.5140995098187926 0 0
+22 -0.4749144219806904 0 0
+23 -0.4364916774353312 0 0
+24 -0.3988164798351664 0 0
+25 -0.3618742567441507 0 0
+26 -0.3256507606537927 0 0
+27 -0.2901320091863738 0 0
+28 -0.2553042883413312 0 0
+29 -0.2211541538737678 0 0
+30 -0.1876684263237806 0 0
+31 -0.1548341743437643 0 0
+32 -0.1226387276497952 0 0
+33 -0.09106965120674937 0 0
+34 -0.0601147701140996 0 0
+35 -0.02976212372405818 0 0
+$EndNodes
+$Elements
+37
+1 15 2 3 1 1
+2 15 2 1 2 2
+3 15 2 2 3 3
+4 1 2 5 1 1 4
+5 1 2 5 1 4 5
+6 1 2 5 1 5 6
+7 1 2 5 1 6 7
+8 1 2 5 1 7 8
+9 1 2 5 1 8 9
+10 1 2 5 1 9 10
+11 1 2 5 1 10 3
+12 1 2 4 2 2 11
+13 1 2 4 2 11 12
+14 1 2 4 2 12 13
+15 1 2 4 2 13 14
+16 1 2 4 2 14 15
+17 1 2 4 2 15 16
+18 1 2 4 2 16 17
+19 1 2 4 2 17 18
+20 1 2 4 2 18 19
+21 1 2 4 2 19 20
+22 1 2 4 2 20 21
+23 1 2 4 2 21 22
+24 1 2 4 2 22 23
+25 1 2 4 2 23 24
+26 1 2 4 2 24 25
+27 1 2 4 2 25 26
+28 1 2 4 2 26 27
+29 1 2 4 2 27 28
+30 1 2 4 2 28 29
+31 1 2 4 2 29 30
+32 1 2 4 2 30 31
+33 1 2 4 2 31 32
+34 1 2 4 2 32 33
+35 1 2 4 2 33 34
+36 1 2 4 2 34 35
+37 1 2 4 2 35 1
+$EndElements
+)";
+  }
+
+  return std::dynamic_pointer_cast<const Mesh<Connectivity<1>>>(GmshReader{filename}.mesh());
+}
+
+std::shared_ptr<const Mesh<Connectivity<2>>>
+MeshDataBaseForTests::_buildHybrid2dMesh()
+{
+  const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("hybrid-2d.msh");
+  if (parallel::rank() == 0) {
+    std::ofstream fout(filename);
+    fout << R"($MeshFormat
+2.2 0 8
+$EndMeshFormat
+$PhysicalNames
+11
+0 8 "XMINYMIN"
+0 9 "XMINYMAX"
+0 10 "XMAXYMIN"
+0 11 "XMAXYMAX"
+1 1 "XMIN"
+1 2 "XMAX"
+1 3 "YMAX"
+1 4 "YMIN"
+1 5 "INTERFACE"
+2 6 "LEFT"
+2 7 "RIGHT"
+$EndPhysicalNames
+$Nodes
+53
+1 0 0 0
+2 1 0 0
+3 1 1 0
+4 0 1 0
+5 2 0 0
+6 2 1 0
+7 0 0.7500000000003477 0
+8 0 0.5000000000020616 0
+9 0 0.2500000000010419 0
+10 1 0.2499999999994109 0
+11 1 0.4999999999986918 0
+12 1 0.7499999999993401 0
+13 2 0.2499999999994109 0
+14 2 0.4999999999986918 0
+15 2 0.7499999999993401 0
+16 0.7500000000003477 1 0
+17 0.5000000000020616 1 0
+18 0.2500000000010419 1 0
+19 1.749999999999999 1 0
+20 1.499999999999998 1 0
+21 1.249999999999999 1 0
+22 0.2499999999994109 0 0
+23 0.4999999999986918 0 0
+24 0.7499999999993401 0 0
+25 1.250000000000001 0 0
+26 1.500000000000002 0 0
+27 1.750000000000001 0 0
+28 0.5645539210988633 0.7822119881665017 0
+29 0.8034002623653069 0.616177001724073 0
+30 0.6429040982169911 0.5281266166868597 0
+31 0.8070137458488089 0.4050273873671746 0
+32 0.193468206450602 0.7967828567280011 0
+33 0.192644796000149 0.6419508693748935 0
+34 0.3924123452244094 0.5739639975328588 0
+35 0.3555609898784376 0.7915857937795373 0
+36 0.1932938945983771 0.4291816994842411 0
+37 0.3837729988924632 0.3582605556259788 0
+38 0.5797710532625071 0.2819566416178859 0
+39 0.7822395465040892 0.7780567773069014 0
+40 0.355038784333282 0.2031847484281184 0
+41 0.2059222030287144 0.2167595959294507 0
+42 0.7859869002303115 0.2399319972346836 0
+43 1.212848849190124 0.2732369527435796 0
+44 1.307250451008527 0.5758879308566747 0
+45 1.493594137086004 0.410605759034483 0
+46 1.439097356236337 0.1761649793157971 0
+47 1.574782862067863 0.7190230111887566 0
+48 1.740502300977129 0.4934083027495074 0
+49 1.853320217111459 0.2440179674328906 0
+50 1.629419001691624 0.2206995018497341 0
+51 1.844214193840832 0.743738552322716 0
+52 1.151907331010239 0.7762099486172758 0
+53 1.354193535052909 0.8313717608489495 0
+$EndNodes
+$Elements
+90
+1 15 2 8 1 1
+2 15 2 9 4 4
+3 15 2 10 5 5
+4 15 2 11 6 6
+5 1 2 1 1 4 7
+6 1 2 1 1 7 8
+7 1 2 1 1 8 9
+8 1 2 1 1 9 1
+9 1 2 5 2 2 10
+10 1 2 5 2 10 11
+11 1 2 5 2 11 12
+12 1 2 5 2 12 3
+13 1 2 2 3 5 13
+14 1 2 2 3 13 14
+15 1 2 2 3 14 15
+16 1 2 2 3 15 6
+17 1 2 3 4 3 16
+18 1 2 3 4 16 17
+19 1 2 3 4 17 18
+20 1 2 3 4 18 4
+21 1 2 3 5 6 19
+22 1 2 3 5 19 20
+23 1 2 3 5 20 21
+24 1 2 3 5 21 3
+25 1 2 4 6 1 22
+26 1 2 4 6 22 23
+27 1 2 4 6 23 24
+28 1 2 4 6 24 2
+29 1 2 4 7 2 25
+30 1 2 4 7 25 26
+31 1 2 4 7 26 27
+32 1 2 4 7 27 5
+33 2 2 7 2 43 45 44
+34 2 2 7 2 46 45 43
+35 2 2 7 2 11 43 44
+36 2 2 7 2 45 48 47
+37 2 2 7 2 46 43 25
+38 2 2 7 2 44 45 47
+39 2 2 7 2 43 11 10
+40 2 2 7 2 48 50 49
+41 2 2 7 2 14 51 48
+42 2 2 7 2 45 50 48
+43 2 2 7 2 27 49 50
+44 2 2 7 2 52 11 44
+45 2 2 7 2 49 27 5
+46 2 2 7 2 47 19 20
+47 2 2 7 2 12 52 3
+48 2 2 7 2 52 21 3
+49 2 2 7 2 47 53 44
+50 2 2 7 2 27 50 26
+51 2 2 7 2 43 10 2
+52 2 2 7 2 25 43 2
+53 2 2 7 2 20 53 47
+54 2 2 7 2 21 53 20
+55 2 2 7 2 46 50 45
+56 2 2 7 2 26 50 46
+57 2 2 7 2 44 53 52
+58 2 2 7 2 46 25 26
+59 2 2 7 2 11 52 12
+60 2 2 7 2 47 48 51
+61 2 2 7 2 51 14 15
+62 2 2 7 2 13 49 5
+63 2 2 7 2 51 19 47
+64 2 2 7 2 21 52 53
+65 2 2 7 2 19 51 6
+66 2 2 7 2 51 15 6
+67 2 2 7 2 48 49 14
+68 2 2 7 2 49 13 14
+69 3 2 6 1 37 34 33 36
+70 3 2 6 1 30 31 11 29
+71 3 2 6 1 35 34 30 28
+72 3 2 6 1 30 29 39 28
+73 3 2 6 1 34 37 38 30
+74 3 2 6 1 37 40 23 38
+75 3 2 6 1 41 40 37 36
+76 3 2 6 1 28 17 18 35
+77 3 2 6 1 30 38 42 31
+78 3 2 6 1 11 31 42 10
+79 3 2 6 1 39 16 17 28
+80 3 2 6 1 33 32 4 7
+81 3 2 6 1 11 12 39 29
+82 3 2 6 1 35 18 4 32
+83 3 2 6 1 23 40 41 22
+84 3 2 6 1 16 39 12 3
+85 3 2 6 1 41 36 8 9
+86 3 2 6 1 9 1 22 41
+87 3 2 6 1 10 42 24 2
+88 3 2 6 1 23 24 42 38
+89 3 2 6 1 7 8 36 33
+90 3 2 6 1 35 32 33 34
+$EndElements
+)";
+  }
+  return std::dynamic_pointer_cast<const Mesh<Connectivity<2>>>(GmshReader{filename}.mesh());
+}
+
+std::shared_ptr<const Mesh<Connectivity<3>>>
+MeshDataBaseForTests::_buildHybrid3dMesh()
+{
+  const std::string filename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("hybrid-3d.msh");
+  if (parallel::rank() == 0) {
+    std::ofstream fout(filename);
+    fout << R"($MeshFormat
+2.2 0 8
+$EndMeshFormat
+$PhysicalNames
+31
+0 40 "XMINYMINZMIN"
+0 41 "XMAXYMINZMIN"
+0 42 "XMINYMAXZMIN"
+0 43 "XMINYMAXZMAX"
+0 44 "XMINYMINZMAX"
+0 45 "XMAXYMINZMAX"
+0 47 "XMAXYMAXZMAX"
+0 51 "XMAXYMAXZMIN"
+1 28 "XMINZMIN"
+1 29 "XMINZMAX"
+1 30 "XMINYMAX"
+1 31 "XMINYMIN"
+1 32 "XMAXZMIN"
+1 33 "XMAXZMAX"
+1 34 "XMAXYMAX"
+1 35 "XMAXYMIN"
+1 36 "YMINZMIN"
+1 37 "YMINZMAX"
+1 38 "YMAXZMIN"
+1 39 "YMAXZMAX"
+2 22 "XMIN"
+2 23 "XMAX"
+2 24 "ZMAX"
+2 25 "ZMIN"
+2 26 "YMAX"
+2 27 "YMIN"
+2 55 "INTERFACE1"
+2 56 "INTERFACE2"
+3 52 "LEFT"
+3 53 "MIDDLE"
+3 54 "RIGHT"
+$EndPhysicalNames
+$Nodes
+132
+1 0 0 0
+2 0.7 0 0
+3 0.8 1 0
+4 0 1 0
+5 1.3 0 0
+6 1.2 1 0
+7 0 1 1
+8 0 0 1
+9 0.7 0 1
+10 0.8 1 1
+11 1.3 0 1
+12 1.2 1 1
+13 2 0 0
+14 2 1 0
+15 2 0 1
+16 2 1 1
+17 0 0.7500000000003466 0
+18 0 0.5000000000020587 0
+19 0 0.2500000000010403 0
+20 0.7249999999999414 0.2499999999994139 0
+21 0.7499999999998691 0.4999999999986909 0
+22 0.7749999999999342 0.7499999999993421 0
+23 1.266666666666749 0.3333333333325114 0
+24 1.23333333333342 0.6666666666657952 0
+25 0.4000000000010956 1 0
+26 0.3499999999991014 0 0
+27 0.9999999999987851 0 0
+28 0 0.7500000000003466 1
+29 0 0.5000000000020587 1
+30 0 0.2500000000010403 1
+31 0.3499999999991014 0 1
+32 0.7249999999999414 0.2499999999994139 1
+33 0.7499999999998691 0.4999999999986909 1
+34 0.7749999999999342 0.7499999999993421 1
+35 0.4000000000010956 1 1
+36 0 1 0.3333333333333333
+37 0 1 0.6666666666666666
+38 0 0 0.3333333333333333
+39 0 0 0.6666666666666666
+40 0.7 0 0.3333333333333333
+41 0.7 0 0.6666666666666666
+42 0.8 1 0.3333333333333333
+43 0.8 1 0.6666666666666666
+44 0.9999999999987851 0 1
+45 1.266666666666749 0.3333333333325114 1
+46 1.23333333333342 0.6666666666657952 1
+47 1.3 0 0.3333333333333333
+48 1.3 0 0.6666666666666666
+49 1.2 1 0.3333333333333333
+50 1.2 1 0.6666666666666666
+51 1.630495225600928 0 0
+52 1.630495225600928 0 1
+53 2 0 0.499999999998694
+54 2 0.499999999998694 1
+55 2 0.499999999998694 0
+56 1.577708829260476 1 0
+57 1.577708829258421 1 1
+58 2 1 0.499999999998694
+59 0.3962888297748171 0.7916234456658734 0
+60 0.5588133104363274 0.5235006176096062 0
+61 0.48540966431768 0.3326974099157888 0
+62 0.2545886146233393 0.4410037988896774 0
+63 0.1888904744336107 0.2469120833355397 0
+64 0.9952483415423343 0.5591370310039079 0
+65 1.009047204638942 0.8056163713004886 0
+66 0.9496163434716166 0.3148023652713143 0
+67 0 0.7500000000003466 0.3333333333333333
+68 0 0.7500000000003466 0.6666666666666666
+69 0 0.5000000000020587 0.3333333333333333
+70 0 0.5000000000020587 0.6666666666666666
+71 0 0.2500000000010403 0.3333333333333333
+72 0 0.2500000000010403 0.6666666666666666
+73 0.3499999999991014 0 0.3333333333333333
+74 0.3499999999991014 0 0.6666666666666666
+75 0.7249999999999414 0.2499999999994139 0.3333333333333333
+76 0.7249999999999414 0.2499999999994139 0.6666666666666666
+77 0.7499999999998691 0.4999999999986909 0.3333333333333333
+78 0.7499999999998691 0.4999999999986909 0.6666666666666666
+79 0.7749999999999342 0.7499999999993421 0.3333333333333333
+80 0.7749999999999342 0.7499999999993421 0.6666666666666666
+81 0.4000000000010956 1 0.3333333333333333
+82 0.4000000000010956 1 0.6666666666666666
+83 0.3962888297748171 0.7916234456658734 1
+84 0.5588133104363274 0.5235006176096062 1
+85 0.48540966431768 0.3326974099157888 1
+86 0.2545886146233393 0.4410037988896774 1
+87 0.1888904744336107 0.2469120833355397 1
+88 0.9999999999987851 0 0.3333333333333333
+89 0.9999999999987851 0 0.6666666666666666
+90 1.266666666666749 0.3333333333325114 0.3333333333333333
+91 1.266666666666749 0.3333333333325114 0.6666666666666666
+92 1.23333333333342 0.6666666666657952 0.3333333333333333
+93 1.23333333333342 0.6666666666657952 0.6666666666666666
+94 0.9952483415423343 0.5591370310039079 1
+95 1.009047204638942 0.8056163713004886 1
+96 0.9496163434716166 0.3148023652713143 1
+97 1.694794622046484 0 0.4999999999999999
+98 2 0.667671095007814 0.3323289049917093
+99 2 0.3400507564846686 0.5028063863717376
+100 2 0.7126842003874787 0.6873157996117608
+101 1.638498486684473 0.2509397074193687 1
+102 1.619828855633658 0.6299359336032246 1
+103 1.638498486684556 0.2509397074193682 0
+104 1.619828855634071 0.629935933603235 0
+105 1.657210007479593 1 0.5000000082593427
+106 0.3962888297748171 0.7916234456658734 0.3333333333333333
+107 0.3962888297748171 0.7916234456658734 0.6666666666666666
+108 0.5588133104363274 0.5235006176096062 0.3333333333333333
+109 0.5588133104363274 0.5235006176096062 0.6666666666666666
+110 0.48540966431768 0.3326974099157888 0.3333333333333333
+111 0.48540966431768 0.3326974099157888 0.6666666666666666
+112 0.2545886146233393 0.4410037988896774 0.3333333333333333
+113 0.2545886146233393 0.4410037988896774 0.6666666666666666
+114 0.1888904744336107 0.2469120833355397 0.3333333333333333
+115 0.1888904744336107 0.2469120833355397 0.6666666666666666
+116 0.9952483415423343 0.5591370310039079 0.3333333333333333
+117 0.9952483415423343 0.5591370310039079 0.6666666666666666
+118 1.009047204638942 0.8056163713004886 0.3333333333333333
+119 1.009047204638942 0.8056163713004886 0.6666666666666666
+120 0.9496163434716166 0.3148023652713143 0.3333333333333333
+121 0.9496163434716166 0.3148023652713143 0.6666666666666666
+122 1.605048220195734 0.3680048220187183 0.5
+123 1.521560099439846 0.6946560099431293 0.4999999999999999
+124 1.449585918125941 0.1832919251455124 0.1666666666666667
+125 1.449585918125941 0.1832919251455124 0.4999999999999999
+126 1.449585918125941 0.1832919251455124 0.8333333333333333
+127 1.416252584792843 0.5166252584784292 0.1666666666666667
+128 1.416252584792843 0.5166252584784292 0.4999999999999999
+129 1.416252584792843 0.5166252584784292 0.8333333333333333
+130 1.382919251459699 0.8499585918121965 0.1666666666666667
+131 1.382919251459699 0.8499585918121965 0.4999999999999999
+132 1.382919251459699 0.8499585918121965 0.8333333333333333
+$EndNodes
+$Elements
+405
+1 15 2 40 1 1
+2 15 2 42 4 4
+3 15 2 43 7 7
+4 15 2 44 8 8
+5 15 2 41 27 13
+6 15 2 51 28 14
+7 15 2 45 29 15
+8 15 2 47 30 16
+9 1 2 28 1 4 17
+10 1 2 28 1 17 18
+11 1 2 28 1 18 19
+12 1 2 28 1 19 1
+13 1 2 38 4 3 25
+14 1 2 38 4 25 4
+15 1 2 38 5 6 3
+16 1 2 36 6 1 26
+17 1 2 36 6 26 2
+18 1 2 36 7 2 27
+19 1 2 36 7 27 5
+20 1 2 29 12 7 28
+21 1 2 29 12 28 29
+22 1 2 29 12 29 30
+23 1 2 29 12 30 8
+24 1 2 37 13 8 31
+25 1 2 37 13 31 9
+26 1 2 39 15 10 35
+27 1 2 39 15 35 7
+28 1 2 30 17 4 36
+29 1 2 30 17 36 37
+30 1 2 30 17 37 7
+31 1 2 31 18 1 38
+32 1 2 31 18 38 39
+33 1 2 31 18 39 8
+34 1 2 37 35 9 44
+35 1 2 37 35 44 11
+36 1 2 39 37 12 10
+37 1 2 36 49 5 51
+38 1 2 36 49 51 13
+39 1 2 37 50 11 52
+40 1 2 37 50 52 15
+41 1 2 35 51 13 53
+42 1 2 35 51 53 15
+43 1 2 33 52 15 54
+44 1 2 33 52 54 16
+45 1 2 32 53 13 55
+46 1 2 32 53 55 14
+47 1 2 38 54 14 56
+48 1 2 38 54 56 6
+49 1 2 39 55 12 57
+50 1 2 39 55 57 16
+51 1 2 34 56 14 58
+52 1 2 34 56 58 16
+53 2 2 25 2 20 2 27
+54 2 2 25 2 23 27 5
+55 2 2 25 2 64 23 24
+56 2 2 25 2 22 65 3
+57 2 2 25 2 23 64 66
+58 2 2 25 2 65 6 3
+59 2 2 25 2 64 21 66
+60 2 2 25 2 65 24 6
+61 2 2 25 2 24 65 64
+62 2 2 25 2 65 22 64
+63 2 2 25 2 66 20 27
+64 2 2 25 2 21 64 22
+65 2 2 25 2 20 66 21
+66 2 2 25 2 23 66 27
+67 2 2 24 54 32 9 44
+68 2 2 24 54 45 44 11
+69 2 2 24 54 94 45 46
+70 2 2 24 54 34 95 10
+71 2 2 24 54 45 94 96
+72 2 2 24 54 95 12 10
+73 2 2 24 54 94 33 96
+74 2 2 24 54 95 46 12
+75 2 2 24 54 46 95 94
+76 2 2 24 54 95 34 94
+77 2 2 24 54 96 32 44
+78 2 2 24 54 33 94 34
+79 2 2 24 54 32 96 33
+80 2 2 24 54 45 96 44
+81 2 2 27 55 51 5 47
+82 2 2 27 55 48 11 52
+83 2 2 27 55 51 97 13
+84 2 2 27 55 97 53 13
+85 2 2 27 55 97 52 15
+86 2 2 27 55 15 53 97
+87 2 2 27 55 97 47 48
+88 2 2 27 55 47 97 51
+89 2 2 27 55 48 52 97
+90 2 2 23 56 53 99 13
+91 2 2 23 56 99 55 13
+92 2 2 23 56 55 98 14
+93 2 2 23 56 98 58 14
+94 2 2 23 56 99 53 15
+95 2 2 23 56 54 99 15
+96 2 2 23 56 100 54 16
+97 2 2 23 56 58 100 16
+98 2 2 23 56 100 99 54
+99 2 2 23 56 55 99 98
+100 2 2 23 56 100 58 98
+101 2 2 23 56 98 99 100
+102 2 2 24 57 45 101 11
+103 2 2 24 57 101 52 11
+104 2 2 24 57 46 12 57
+105 2 2 24 57 52 101 15
+106 2 2 24 57 101 54 15
+107 2 2 24 57 54 102 16
+108 2 2 24 57 102 57 16
+109 2 2 24 57 46 102 45
+110 2 2 24 57 101 45 102
+111 2 2 24 57 102 46 57
+112 2 2 24 57 102 54 101
+113 2 2 25 58 23 103 5
+114 2 2 25 58 103 51 5
+115 2 2 25 58 24 6 56
+116 2 2 25 58 51 103 13
+117 2 2 25 58 103 55 13
+118 2 2 25 58 55 104 14
+119 2 2 25 58 104 56 14
+120 2 2 25 58 24 104 23
+121 2 2 25 58 103 23 104
+122 2 2 25 58 104 24 56
+123 2 2 25 58 104 55 103
+124 2 2 26 59 6 49 56
+125 2 2 26 59 50 12 57
+126 2 2 26 59 56 105 14
+127 2 2 26 59 105 58 14
+128 2 2 26 59 105 57 16
+129 2 2 26 59 16 58 105
+130 2 2 26 59 105 49 50
+131 2 2 26 59 49 105 56
+132 2 2 26 59 50 57 105
+133 3 2 25 1 59 60 21 22
+134 3 2 25 1 22 3 25 59
+135 3 2 25 1 18 62 59 17
+136 3 2 25 1 25 4 17 59
+137 3 2 25 1 62 63 26 61
+138 3 2 25 1 60 61 20 21
+139 3 2 25 1 62 18 19 63
+140 3 2 25 1 26 2 20 61
+141 3 2 25 1 61 60 59 62
+142 3 2 25 1 19 1 26 63
+143 3 2 22 19 4 17 67 36
+144 3 2 22 19 36 67 68 37
+145 3 2 22 19 37 68 28 7
+146 3 2 22 19 17 18 69 67
+147 3 2 22 19 67 69 70 68
+148 3 2 22 19 68 70 29 28
+149 3 2 22 19 18 19 71 69
+150 3 2 22 19 69 71 72 70
+151 3 2 22 19 70 72 30 29
+152 3 2 22 19 19 1 38 71
+153 3 2 22 19 71 38 39 72
+154 3 2 22 19 72 39 8 30
+155 3 2 27 23 1 26 73 38
+156 3 2 27 23 38 73 74 39
+157 3 2 27 23 39 74 31 8
+158 3 2 27 23 26 2 40 73
+159 3 2 27 23 73 40 41 74
+160 3 2 27 23 74 41 9 31
+161 3 2 55 27 2 20 75 40
+162 3 2 55 27 40 75 76 41
+163 3 2 55 27 41 76 32 9
+164 3 2 55 27 20 21 77 75
+165 3 2 55 27 75 77 78 76
+166 3 2 55 27 76 78 33 32
+167 3 2 55 27 21 22 79 77
+168 3 2 55 27 77 79 80 78
+169 3 2 55 27 78 80 34 33
+170 3 2 55 27 22 3 42 79
+171 3 2 55 27 79 42 43 80
+172 3 2 55 27 80 43 10 34
+173 3 2 26 31 3 25 81 42
+174 3 2 26 31 42 81 82 43
+175 3 2 26 31 43 82 35 10
+176 3 2 26 31 25 4 36 81
+177 3 2 26 31 81 36 37 82
+178 3 2 26 31 82 37 7 35
+179 3 2 24 32 83 84 33 34
+180 3 2 24 32 34 10 35 83
+181 3 2 24 32 29 86 83 28
+182 3 2 24 32 35 7 28 83
+183 3 2 24 32 86 87 31 85
+184 3 2 24 32 84 85 32 33
+185 3 2 24 32 86 29 30 87
+186 3 2 24 32 31 9 32 85
+187 3 2 24 32 85 84 83 86
+188 3 2 24 32 30 8 31 87
+189 3 2 27 45 2 27 88 40
+190 3 2 27 45 40 88 89 41
+191 3 2 27 45 41 89 44 9
+192 3 2 27 45 27 5 47 88
+193 3 2 27 45 88 47 48 89
+194 3 2 27 45 89 48 11 44
+195 3 2 56 49 5 23 90 47
+196 3 2 56 49 47 90 91 48
+197 3 2 56 49 48 91 45 11
+198 3 2 56 49 23 24 92 90
+199 3 2 56 49 90 92 93 91
+200 3 2 56 49 91 93 46 45
+201 3 2 56 49 24 6 49 92
+202 3 2 56 49 92 49 50 93
+203 3 2 56 49 93 50 12 46
+204 3 2 26 53 6 3 42 49
+205 3 2 26 53 49 42 43 50
+206 3 2 26 53 50 43 10 12
+207 4 2 54 3 90 103 127 122
+208 4 2 54 3 101 91 129 122
+209 4 2 54 3 103 90 124 122
+210 4 2 54 3 126 91 101 122
+211 4 2 54 3 92 104 130 123
+212 4 2 54 3 102 93 132 123
+213 4 2 54 3 129 45 91 101
+214 4 2 54 3 90 23 127 103
+215 4 2 54 3 45 126 91 101
+216 4 2 54 3 23 90 124 103
+217 4 2 54 3 92 127 104 123
+218 4 2 54 3 93 102 129 123
+219 4 2 54 3 125 90 91 122
+220 4 2 54 3 91 90 128 122
+221 4 2 54 3 104 127 103 122
+222 4 2 54 3 101 129 102 122
+223 4 2 54 3 127 122 104 123
+224 4 2 54 3 102 122 129 123
+225 4 2 54 3 103 124 97 122
+226 4 2 54 3 126 101 97 122
+227 4 2 54 3 132 57 102 123
+228 4 2 54 3 130 104 56 123
+229 4 2 54 3 46 93 132 102
+230 4 2 54 3 92 24 130 104
+231 4 2 54 3 129 93 46 102
+232 4 2 54 3 92 127 24 104
+233 4 2 54 3 102 100 54 122
+234 4 2 54 3 100 122 102 123
+235 4 2 54 3 54 101 102 122
+236 4 2 54 3 104 103 55 122
+237 4 2 54 3 51 103 124 97
+238 4 2 54 3 126 101 52 97
+239 4 2 54 3 105 130 56 123
+240 4 2 54 3 132 105 57 123
+241 4 2 54 3 98 104 55 122
+242 4 2 54 3 93 92 131 123
+243 4 2 54 3 93 128 92 123
+244 4 2 54 3 51 124 47 97
+245 4 2 54 3 52 48 126 97
+246 4 2 54 3 54 100 99 122
+247 4 2 54 3 104 122 98 123
+248 4 2 54 3 98 122 100 123
+249 4 2 54 3 54 99 101 122
+250 4 2 54 3 55 103 99 122
+251 4 2 54 3 49 130 56 105
+252 4 2 54 3 57 132 50 105
+253 4 2 54 3 55 99 98 122
+254 4 2 54 3 103 99 97 53
+255 4 2 54 3 101 15 97 53
+256 4 2 54 3 97 99 101 53
+257 4 2 54 3 105 100 102 123
+258 4 2 54 3 53 97 103 13
+259 4 2 54 3 103 97 99 122
+260 4 2 54 3 53 103 99 13
+261 4 2 54 3 15 101 99 53
+262 4 2 54 3 99 97 101 122
+263 4 2 54 3 105 104 98 123
+264 4 2 54 3 105 57 102 100
+265 4 2 54 3 98 105 56 104
+266 4 2 54 3 12 132 50 57
+267 4 2 54 3 56 6 130 49
+268 4 2 54 3 104 127 23 103
+269 4 2 54 3 129 45 101 102
+270 4 2 54 3 103 55 99 13
+271 4 2 54 3 15 99 101 54
+272 4 2 54 3 14 98 56 104
+273 4 2 54 3 52 101 15 97
+274 4 2 54 3 97 51 103 13
+275 4 2 54 3 105 56 104 123
+276 4 2 54 3 57 105 102 123
+277 4 2 54 3 56 105 98 14
+278 4 2 54 3 100 98 99 122
+279 4 2 54 3 57 12 132 46
+280 4 2 54 3 24 56 6 130
+281 4 2 54 3 105 16 57 100
+282 4 2 54 3 16 102 57 100
+283 4 2 54 3 126 11 48 52
+284 4 2 54 3 5 124 47 51
+285 4 2 54 3 58 98 105 14
+286 4 2 54 3 132 57 46 102
+287 4 2 54 3 24 56 130 104
+288 4 2 54 3 105 98 100 123
+289 4 2 54 3 100 105 16 58
+290 4 2 54 3 11 126 45 101
+291 4 2 54 3 23 124 5 103
+292 4 2 54 3 50 131 49 105
+293 4 2 54 3 45 129 46 102
+294 4 2 54 3 24 127 23 104
+295 4 2 54 3 54 102 16 100
+296 4 2 54 3 104 14 98 55
+297 4 2 54 3 124 5 103 51
+298 4 2 54 3 11 126 101 52
+299 4 2 54 3 105 100 98 58
+300 4 2 54 3 47 125 48 97
+301 4 2 54 3 93 128 129 91
+302 4 2 54 3 93 129 128 123
+303 4 2 54 3 122 129 128 91
+304 4 2 54 3 122 128 129 123
+305 4 2 54 3 122 128 127 90
+306 4 2 54 3 122 127 128 123
+307 4 2 54 3 92 127 128 90
+308 4 2 54 3 92 128 127 123
+309 4 2 54 3 91 125 126 48
+310 4 2 54 3 91 126 125 122
+311 4 2 54 3 97 126 125 48
+312 4 2 54 3 97 125 126 122
+313 4 2 54 3 97 125 124 47
+314 4 2 54 3 97 124 125 122
+315 4 2 54 3 90 124 125 47
+316 4 2 54 3 90 125 124 122
+317 4 2 54 3 105 131 132 50
+318 4 2 54 3 105 132 131 123
+319 4 2 54 3 93 132 131 50
+320 4 2 54 3 93 131 132 123
+321 4 2 54 3 92 131 130 49
+322 4 2 54 3 92 130 131 123
+323 4 2 54 3 105 130 131 49
+324 4 2 54 3 105 131 130 123
+325 5 2 52 1 21 22 59 60 77 79 106 108
+326 5 2 52 1 77 79 106 108 78 80 107 109
+327 5 2 52 1 78 80 107 109 33 34 83 84
+328 5 2 52 1 25 59 22 3 81 106 79 42
+329 5 2 52 1 81 106 79 42 82 107 80 43
+330 5 2 52 1 82 107 80 43 35 83 34 10
+331 5 2 52 1 59 17 18 62 106 67 69 112
+332 5 2 52 1 106 67 69 112 107 68 70 113
+333 5 2 52 1 107 68 70 113 83 28 29 86
+334 5 2 52 1 17 59 25 4 67 106 81 36
+335 5 2 52 1 67 106 81 36 68 107 82 37
+336 5 2 52 1 68 107 82 37 28 83 35 7
+337 5 2 52 1 26 61 62 63 73 110 112 114
+338 5 2 52 1 73 110 112 114 74 111 113 115
+339 5 2 52 1 74 111 113 115 31 85 86 87
+340 5 2 52 1 20 21 60 61 75 77 108 110
+341 5 2 52 1 75 77 108 110 76 78 109 111
+342 5 2 52 1 76 78 109 111 32 33 84 85
+343 5 2 52 1 19 63 62 18 71 114 112 69
+344 5 2 52 1 71 114 112 69 72 115 113 70
+345 5 2 52 1 72 115 113 70 30 87 86 29
+346 5 2 52 1 20 61 26 2 75 110 73 40
+347 5 2 52 1 75 110 73 40 76 111 74 41
+348 5 2 52 1 76 111 74 41 32 85 31 9
+349 5 2 52 1 59 62 61 60 106 112 110 108
+350 5 2 52 1 106 112 110 108 107 113 111 109
+351 5 2 52 1 107 113 111 109 83 86 85 84
+352 5 2 52 1 26 63 19 1 73 114 71 38
+353 5 2 52 1 73 114 71 38 74 115 72 39
+354 5 2 52 1 74 115 72 39 31 87 30 8
+355 6 2 53 2 27 20 2 88 75 40
+356 6 2 53 2 88 75 40 89 76 41
+357 6 2 53 2 89 76 41 44 32 9
+358 6 2 53 2 5 23 27 47 90 88
+359 6 2 53 2 47 90 88 48 91 89
+360 6 2 53 2 48 91 89 11 45 44
+361 6 2 53 2 24 64 23 92 116 90
+362 6 2 53 2 92 116 90 93 117 91
+363 6 2 53 2 93 117 91 46 94 45
+364 6 2 53 2 3 22 65 42 79 118
+365 6 2 53 2 42 79 118 43 80 119
+366 6 2 53 2 43 80 119 10 34 95
+367 6 2 53 2 66 23 64 120 90 116
+368 6 2 53 2 120 90 116 121 91 117
+369 6 2 53 2 121 91 117 96 45 94
+370 6 2 53 2 3 65 6 42 118 49
+371 6 2 53 2 42 118 49 43 119 50
+372 6 2 53 2 43 119 50 10 95 12
+373 6 2 53 2 66 64 21 120 116 77
+374 6 2 53 2 120 116 77 121 117 78
+375 6 2 53 2 121 117 78 96 94 33
+376 6 2 53 2 6 65 24 49 118 92
+377 6 2 53 2 49 118 92 50 119 93
+378 6 2 53 2 50 119 93 12 95 46
+379 6 2 53 2 64 24 65 116 92 118
+380 6 2 53 2 116 92 118 117 93 119
+381 6 2 53 2 117 93 119 94 46 95
+382 6 2 53 2 64 65 22 116 118 79
+383 6 2 53 2 116 118 79 117 119 80
+384 6 2 53 2 117 119 80 94 95 34
+385 6 2 53 2 27 66 20 88 120 75
+386 6 2 53 2 88 120 75 89 121 76
+387 6 2 53 2 89 121 76 44 96 32
+388 6 2 53 2 22 21 64 79 77 116
+389 6 2 53 2 79 77 116 80 78 117
+390 6 2 53 2 80 78 117 34 33 94
+391 6 2 53 2 21 20 66 77 75 120
+392 6 2 53 2 77 75 120 78 76 121
+393 6 2 53 2 78 76 121 33 32 96
+394 6 2 53 2 27 23 66 88 90 120
+395 6 2 53 2 88 90 120 89 91 121
+396 6 2 53 2 89 91 121 44 45 96
+397 7 2 54 3 5 23 90 47 124
+398 7 2 54 3 47 90 91 48 125
+399 7 2 54 3 48 91 45 11 126
+400 7 2 54 3 23 24 92 90 127
+401 7 2 54 3 90 92 93 91 128
+402 7 2 54 3 91 93 46 45 129
+403 7 2 54 3 24 6 49 92 130
+404 7 2 54 3 92 49 50 93 131
+405 7 2 54 3 93 50 12 46 132
+$EndElements
+$Periodic
+9
+0 7 4
+1
+7 4
+0 8 1
+1
+8 1
+1 12 1
+3
+28 17
+29 18
+30 19
+1 13 6
+1
+31 26
+1 15 4
+1
+35 25
+1 35 7
+1
+44 27
+1 37 5
+0
+2 32 1
+5
+86 62
+87 63
+83 59
+84 60
+85 61
+2 54 2
+3
+94 64
+95 65
+96 66
+$EndPeriodic
+)";
+  }
+  return std::dynamic_pointer_cast<const Mesh<Connectivity<3>>>(GmshReader{filename}.mesh());
+}
diff --git a/tests/MeshDataBaseForTests.hpp b/tests/MeshDataBaseForTests.hpp
index 589ec9b82f9be6451afd496aab3399c13bb3d718..79f3379f0cbfd51416594aa6015d66aa0b9f861c 100644
--- a/tests/MeshDataBaseForTests.hpp
+++ b/tests/MeshDataBaseForTests.hpp
@@ -9,10 +9,38 @@ class Connectivity;
 template <typename ConnectivityT>
 class Mesh;
 
+#include <array>
 #include <memory>
+#include <string>
 
 class MeshDataBaseForTests
 {
+ public:
+  template <size_t Dimension>
+  class NamedMesh
+  {
+   private:
+    const std::string m_name;
+    const std::shared_ptr<const Mesh<Connectivity<Dimension>>> m_mesh;
+
+   public:
+    NamedMesh(const std::string& name, const std::shared_ptr<const Mesh<Connectivity<Dimension>>>& mesh)
+      : m_name(name), m_mesh(mesh)
+    {}
+
+    const std::string&
+    name() const
+    {
+      return m_name;
+    }
+
+    auto
+    mesh() const
+    {
+      return m_mesh;
+    }
+  };
+
  private:
   explicit MeshDataBaseForTests();
 
@@ -22,12 +50,47 @@ class MeshDataBaseForTests
   std::shared_ptr<const Mesh<Connectivity<2>>> m_cartesian_2d_mesh;
   std::shared_ptr<const Mesh<Connectivity<3>>> m_cartesian_3d_mesh;
 
+  std::shared_ptr<const Mesh<Connectivity<1>>> m_unordered_1d_mesh;
+  std::shared_ptr<const Mesh<Connectivity<2>>> m_hybrid_2d_mesh;
+  std::shared_ptr<const Mesh<Connectivity<3>>> m_hybrid_3d_mesh;
+
+  std::shared_ptr<const Mesh<Connectivity<1>>> _buildUnordered1dMesh();
+  std::shared_ptr<const Mesh<Connectivity<2>>> _buildHybrid2dMesh();
+  std::shared_ptr<const Mesh<Connectivity<3>>> _buildHybrid3dMesh();
+
  public:
-  std::shared_ptr<const Mesh<Connectivity<1>>> cartesianMesh1D() const;
-  std::shared_ptr<const Mesh<Connectivity<2>>> cartesianMesh2D() const;
-  std::shared_ptr<const Mesh<Connectivity<3>>> cartesianMesh3D() const;
+  std::shared_ptr<const Mesh<Connectivity<1>>> cartesian1DMesh() const;
+  std::shared_ptr<const Mesh<Connectivity<1>>> unordered1DMesh() const;
+
+  std::shared_ptr<const Mesh<Connectivity<2>>> cartesian2DMesh() const;
+  std::shared_ptr<const Mesh<Connectivity<2>>> hybrid2DMesh() const;
+
+  std::shared_ptr<const Mesh<Connectivity<3>>> cartesian3DMesh() const;
+  std::shared_ptr<const Mesh<Connectivity<3>>> hybrid3DMesh() const;
 
   static const MeshDataBaseForTests& get();
+
+  auto
+  all1DMeshes() const
+  {
+    return std::array{NamedMesh{"cartesian 1d mesh", cartesian1DMesh()},   //
+                      NamedMesh{"unordered 1d mesh", unordered1DMesh()}};
+  }
+
+  auto
+  all2DMeshes() const
+  {
+    return std::array{NamedMesh{"cartesian 2d mesh", cartesian2DMesh()},   //
+                      NamedMesh{"hybrid 2d mesh", hybrid2DMesh()}};
+  }
+
+  auto
+  all3DMeshes() const
+  {
+    return std::array{NamedMesh{"cartesian 3d mesh", cartesian3DMesh()},   //
+                      NamedMesh{std::string("hybrid 3d mesh"), hybrid3DMesh()}};
+  }
+
   static void create();
   static void destroy();
 
diff --git a/tests/mpi_test_main.cpp b/tests/mpi_test_main.cpp
index 4c94634173799a17519b5d417bfc914a99e2e740..93f3c7d908c7e4d3840231ba90278123e387a945 100644
--- a/tests/mpi_test_main.cpp
+++ b/tests/mpi_test_main.cpp
@@ -2,9 +2,10 @@
 
 #include <Kokkos_Core.hpp>
 
+#include <analysis/QuadratureManager.hpp>
 #include <language/utils/OperatorRepository.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/SynchronizerManager.hpp>
 #include <utils/Messenger.hpp>
@@ -27,7 +28,7 @@ main(int argc, char* argv[])
 
   const std::string output_base_name{"mpi_test_rank_"};
 
-  std::filesystem::path parallel_output(std::string{PUGS_BINARY_DIR});
+  std::filesystem::path parallel_output = std::filesystem::path{PUGS_BINARY_DIR}.append("tests");
 
   std::filesystem::path gcov_prefix = [&]() -> std::filesystem::path {
     std::string template_temp_dir = std::filesystem::temp_directory_path() / "pugs_gcov_XXXXXX";
@@ -59,9 +60,10 @@ main(int argc, char* argv[])
 
       SynchronizerManager::create();
       RandomEngine::create();
+      QuadratureManager::create();
       MeshDataManager::create();
-      DiamondDualConnectivityManager::create();
-      DiamondDualMeshManager::create();
+      DualConnectivityManager::create();
+      DualMeshManager::create();
 
       MeshDataBaseForTests::create();
 
@@ -71,7 +73,7 @@ main(int argc, char* argv[])
                                     << rang::style::reset << '\n';
 
           for (size_t i_rank = 1; i_rank < parallel::size(); ++i_rank) {
-            std::filesystem::path parallel_output(std::string{PUGS_BINARY_DIR});
+            std::filesystem::path parallel_output = std::filesystem::path{PUGS_BINARY_DIR}.append("tests");
             parallel_output /= output_base_name + std::to_string(i_rank);
             session.config().stream() << " - " << rang::fg::green << parallel_output.parent_path().string()
                                       << parallel_output.preferred_separator << rang::style::reset << rang::fgB::green
@@ -88,9 +90,10 @@ main(int argc, char* argv[])
 
       MeshDataBaseForTests::destroy();
 
-      DiamondDualMeshManager::destroy();
-      DiamondDualConnectivityManager::destroy();
+      DualMeshManager::destroy();
+      DualConnectivityManager::destroy();
       MeshDataManager::destroy();
+      QuadratureManager::destroy();
       RandomEngine::destroy();
       SynchronizerManager::destroy();
     }
diff --git a/tests/test_ASTBuilder.cpp b/tests/test_ASTBuilder.cpp
index 3ce2855eff1b059c0ac0e4215b8b1410e45af165..fdcf13315b9eb606f71f06d2cb1c2da399566a43 100644
--- a/tests/test_ASTBuilder.cpp
+++ b/tests/test_ASTBuilder.cpp
@@ -159,15 +159,30 @@ let s:string; s = "foo";
       SECTION("parented expression")
       {
         std::string_view data = R"(
-(2+3)*6;
+(-2)*6;
+(-2+3)*6;
+(-2+3+5)*6;
 )";
 
         std::string_view result = R"(
 (root)
+ +-(language::multiply_op)
+ |   +-(language::unary_minus)
+ |   |   `-(language::integer:2)
+ |   `-(language::integer:6)
+ +-(language::multiply_op)
+ |   +-(language::plus_op)
+ |   |   +-(language::unary_minus)
+ |   |   |   `-(language::integer:2)
+ |   |   `-(language::integer:3)
+ |   `-(language::integer:6)
  `-(language::multiply_op)
      +-(language::plus_op)
-     |   +-(language::integer:2)
-     |   `-(language::integer:3)
+     |   +-(language::plus_op)
+     |   |   +-(language::unary_minus)
+     |   |   |   `-(language::integer:2)
+     |   |   `-(language::integer:3)
+     |   `-(language::integer:5)
      `-(language::integer:6)
 )";
         CHECK_AST(data, result);
diff --git a/tests/test_ASTNodeBinaryOperatorExpressionBuilder.cpp b/tests/test_ASTNodeBinaryOperatorExpressionBuilder.cpp
index b99aa045958dc356cb03f2b548e8c632d58b124d..175093d61f214c4bf0d793a5ccf4fea26126f1d4 100644
--- a/tests/test_ASTNodeBinaryOperatorExpressionBuilder.cpp
+++ b/tests/test_ASTNodeBinaryOperatorExpressionBuilder.cpp
@@ -1,6 +1,8 @@
 #include <catch2/catch_test_macros.hpp>
 #include <catch2/matchers/catch_matchers_all.hpp>
 
+#include <FixturesForBuiltinT.hpp>
+
 #include <language/ast/ASTBuilder.hpp>
 #include <language/ast/ASTModulesImporter.hpp>
 #include <language/ast/ASTNodeBinaryOperatorExpressionBuilder.hpp>
@@ -10,6 +12,11 @@
 #include <language/ast/ASTNodeTypeCleaner.hpp>
 #include <language/ast/ASTSymbolTableBuilder.hpp>
 #include <language/utils/ASTPrinter.hpp>
+#include <language/utils/BasicAffectationRegistrerFor.hpp>
+#include <language/utils/BinaryOperatorProcessorBuilder.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OperatorRepository.hpp>
+#include <language/utils/TypeDescriptor.hpp>
 #include <utils/Demangle.hpp>
 
 #include <pegtl/string_input.hpp>
@@ -1417,6 +1424,118 @@ x!=y;
     }
   }
 
+  SECTION("shift")
+  {
+#define CHECK_AST_BUILTIN_SHIFT_EXPRESSION(data, expected_output)                                               \
+  {                                                                                                             \
+    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};                                                  \
+    auto ast = ASTBuilder::build(input);                                                                        \
+                                                                                                                \
+    ASTModulesImporter{*ast};                                                                                   \
+                                                                                                                \
+    BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; \
+                                                                                                                \
+    OperatorRepository& repository = OperatorRepository::instance();                                            \
+                                                                                                                \
+    repository.addBinaryOperator<language::shift_left_op>(                                                      \
+      std::make_shared<                                                                                         \
+        BinaryOperatorProcessorBuilder<language::shift_left_op, std::shared_ptr<const double>,                  \
+                                       std::shared_ptr<const double>, std::shared_ptr<const double>>>());       \
+                                                                                                                \
+    repository.addBinaryOperator<language::shift_right_op>(                                                     \
+      std::make_shared<                                                                                         \
+        BinaryOperatorProcessorBuilder<language::shift_right_op, std::shared_ptr<const double>,                 \
+                                       std::shared_ptr<const double>, std::shared_ptr<const double>>>());       \
+                                                                                                                \
+    SymbolTable& symbol_table = *ast->m_symbol_table;                                                           \
+    auto [i_symbol, success]  = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin());               \
+    if (not success) {                                                                                          \
+      throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing");          \
+    }                                                                                                           \
+                                                                                                                \
+    i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>());              \
+    i_symbol->attributes().setIsInitialized();                                                                  \
+    i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size();                                   \
+    symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId()));   \
+                                                                                                                \
+    auto [i_symbol_bt_a, success_bt_a] = symbol_table.add("bt_a", ast->begin());                                \
+    if (not success_bt_a) {                                                                                     \
+      throw UnexpectedError("cannot add 'bt_a' of type builtin_t for testing");                                 \
+    }                                                                                                           \
+    i_symbol_bt_a->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>);            \
+    i_symbol_bt_a->attributes().setIsInitialized();                                                             \
+    i_symbol_bt_a->attributes().value() =                                                                       \
+      EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2)));                 \
+                                                                                                                \
+    auto [i_symbol_bt_b, success_bt_b] = symbol_table.add("bt_b", ast->begin());                                \
+    if (not success_bt_b) {                                                                                     \
+      throw UnexpectedError("cannot add 'bt_b' of type builtin_t for testing");                                 \
+    }                                                                                                           \
+    i_symbol_bt_b->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>);            \
+    i_symbol_bt_b->attributes().setIsInitialized();                                                             \
+    i_symbol_bt_b->attributes().value() =                                                                       \
+      EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(5.3)));                 \
+                                                                                                                \
+    ASTNodeTypeCleaner<language::import_instruction>{*ast};                                                     \
+                                                                                                                \
+    ASTSymbolTableBuilder{*ast};                                                                                \
+    ASTNodeDataTypeBuilder{*ast};                                                                               \
+                                                                                                                \
+    ASTNodeDeclarationToAffectationConverter{*ast};                                                             \
+    ASTNodeTypeCleaner<language::var_declaration>{*ast};                                                        \
+                                                                                                                \
+    ASTNodeExpressionBuilder{*ast};                                                                             \
+                                                                                                                \
+    std::stringstream ast_output;                                                                               \
+    ast_output << '\n' << ASTPrinter{*ast, ASTPrinter::Format::raw, {ASTPrinter::Info::exec_type}};             \
+                                                                                                                \
+    REQUIRE(ast_output.str() == expected_output);                                                               \
+  }
+
+    SECTION("shift left (builtin)")
+    {
+      std::string_view data          = R"(
+let m : builtin_t;
+let n : builtin_t;
+n << m;
+)";
+      const std::string sptr_mangled = demangle<std::shared_ptr<double const>>();
+
+      std::string result =
+        R"(
+(root:ASTNodeListProcessor)
+ `-(language::shift_left_op:BinaryExpressionProcessor<language::shift_left_op, )" +
+        sptr_mangled + ", " + sptr_mangled + ", " + sptr_mangled + R"( >)
+     +-(language::name:n:NameProcessor)
+     `-(language::name:m:NameProcessor)
+)";
+
+      CHECK_AST_BUILTIN_SHIFT_EXPRESSION(data, result);
+    }
+
+    SECTION("shift right (builtin)")
+    {
+      std::string_view data = R"(
+let m : builtin_t;
+let n : builtin_t;
+n >> m;
+)";
+
+      const std::string sptr_mangled = demangle<std::shared_ptr<double const>>();
+
+      std::string result =
+        R"(
+(root:ASTNodeListProcessor)
+ `-(language::shift_right_op:BinaryExpressionProcessor<language::shift_right_op, )" +
+        sptr_mangled + ", " + sptr_mangled + ", " + sptr_mangled + R"( >)
+     +-(language::name:n:NameProcessor)
+     `-(language::name:m:NameProcessor)
+)";
+
+      CHECK_AST_BUILTIN_SHIFT_EXPRESSION(data, result);
+    }
+  }
+
   SECTION("Errors")
   {
     SECTION("Invalid binary operator type")
diff --git a/tests/test_Array.cpp b/tests/test_Array.cpp
index 5b9889f396f6413b4c704e48ffa1497bf6edb8fe..fc5a76735fcb78c81ca7635751f39e85f8d24040 100644
--- a/tests/test_Array.cpp
+++ b/tests/test_Array.cpp
@@ -1,6 +1,8 @@
 #include <catch2/catch_test_macros.hpp>
 #include <catch2/matchers/catch_matchers_all.hpp>
 
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
 #include <utils/Array.hpp>
 #include <utils/PugsAssert.hpp>
 #include <utils/Types.hpp>
@@ -251,6 +253,72 @@ TEST_CASE("Array", "[utils]")
     REQUIRE(array_ost.str() == ref_ost.str());
   }
 
+  SECTION("checking for Array reductions")
+  {
+    Array<int> a(10);
+    a[0] = 13;
+    a[1] = 1;
+    a[2] = 8;
+    a[3] = -3;
+    a[4] = 23;
+    a[5] = -1;
+    a[6] = 13;
+    a[7] = 0;
+    a[8] = 12;
+    a[9] = 9;
+
+    SECTION("Min")
+    {
+      REQUIRE(min(a) == -3);
+    }
+
+    SECTION("Max")
+    {
+      REQUIRE(max(a) == 23);
+    }
+
+    SECTION("Sum")
+    {
+      REQUIRE((sum(a) == 75));
+    }
+
+    SECTION("TinyVector Sum")
+    {
+      using N2 = TinyVector<2, int>;
+      Array<N2> b(10);
+      b[0] = N2{13, 2};
+      b[1] = N2{1, 3};
+      b[2] = N2{8, -2};
+      b[3] = N2{-3, 2};
+      b[4] = N2{23, 4};
+      b[5] = N2{-1, -3};
+      b[6] = N2{13, 17};
+      b[7] = N2{0, 9};
+      b[8] = N2{12, 13};
+      b[9] = N2{9, -17};
+
+      REQUIRE((sum(b) == N2{75, 28}));
+    }
+
+    SECTION("TinyMatrix Sum")
+    {
+      using N22 = TinyMatrix<2, 2, int>;
+      Array<N22> b(10);
+      b[0] = N22{13, 2, 0, 1};
+      b[1] = N22{1, 3, 6, 3};
+      b[2] = N22{8, -2, -1, 21};
+      b[3] = N22{-3, 2, 5, 12};
+      b[4] = N22{23, 4, 7, 1};
+      b[5] = N22{-1, -3, 33, 11};
+      b[6] = N22{13, 17, 12, 13};
+      b[7] = N22{0, 9, 1, 14};
+      b[8] = N22{12, 13, -3, -71};
+      b[9] = N22{9, -17, 0, 16};
+
+      REQUIRE((sum(b) == N22{75, 28, 60, 21}));
+    }
+  }
+
 #ifndef NDEBUG
 
   SECTION("output with signaling NaN")
diff --git a/tests/test_ArrayUtils.cpp b/tests/test_ArrayUtils.cpp
deleted file mode 100644
index eb976b40df67c640bbe1a376671706835ca4c2e0..0000000000000000000000000000000000000000
--- a/tests/test_ArrayUtils.cpp
+++ /dev/null
@@ -1,83 +0,0 @@
-#include <catch2/catch_test_macros.hpp>
-#include <catch2/matchers/catch_matchers_all.hpp>
-
-#include <utils/Array.hpp>
-#include <utils/ArrayUtils.hpp>
-#include <utils/PugsAssert.hpp>
-
-#include <algebra/TinyMatrix.hpp>
-#include <algebra/TinyVector.hpp>
-
-// clazy:excludeall=non-pod-global-static
-
-// Instantiate to ensure full coverage is performed
-template class Array<int>;
-
-TEST_CASE("ArrayUtils", "[utils]")
-{
-  SECTION("checking for Array reductions")
-  {
-    Array<int> a(10);
-    a[0] = 13;
-    a[1] = 1;
-    a[2] = 8;
-    a[3] = -3;
-    a[4] = 23;
-    a[5] = -1;
-    a[6] = 13;
-    a[7] = 0;
-    a[8] = 12;
-    a[9] = 9;
-
-    SECTION("Min")
-    {
-      REQUIRE((min(a) == -3));
-    }
-
-    SECTION("Max")
-    {
-      REQUIRE((max(a) == 23));
-    }
-
-    SECTION("Sum")
-    {
-      REQUIRE((sum(a) == 75));
-    }
-
-    SECTION("TinyVector Sum")
-    {
-      using N2 = TinyVector<2, int>;
-      Array<N2> b(10);
-      b[0] = {13, 2};
-      b[1] = {1, 3};
-      b[2] = {8, -2};
-      b[3] = {-3, 2};
-      b[4] = {23, 4};
-      b[5] = {-1, -3};
-      b[6] = {13, 17};
-      b[7] = {0, 9};
-      b[8] = {12, 13};
-      b[9] = {9, -17};
-
-      REQUIRE((sum(b) == N2{75, 28}));
-    }
-
-    SECTION("TinyMatrix Sum")
-    {
-      using N22 = TinyMatrix<2, 2, int>;
-      Array<N22> b(10);
-      b[0] = {13, 2, 0, 1};
-      b[1] = {1, 3, 6, 3};
-      b[2] = {8, -2, -1, 21};
-      b[3] = {-3, 2, 5, 12};
-      b[4] = {23, 4, 7, 1};
-      b[5] = {-1, -3, 33, 11};
-      b[6] = {13, 17, 12, 13};
-      b[7] = {0, 9, 1, 14};
-      b[8] = {12, 13, -3, -71};
-      b[9] = {9, -17, 0, 16};
-
-      REQUIRE((sum(b) == N22{75, 28, 60, 21}));
-    }
-  }
-}
diff --git a/tests/test_BinaryExpressionProcessor_arithmetic.cpp b/tests/test_BinaryExpressionProcessor_arithmetic.cpp
index 2c91674b2fd300bb59ef4faae100c4ea0423df33..10699f3c06a24ba5b298ef2ac10984473c1e9e02 100644
--- a/tests/test_BinaryExpressionProcessor_arithmetic.cpp
+++ b/tests/test_BinaryExpressionProcessor_arithmetic.cpp
@@ -1,10 +1,161 @@
 #include <catch2/catch_test_macros.hpp>
 #include <catch2/matchers/catch_matchers_all.hpp>
 
+#include <FixturesForBuiltinT.hpp>
+
+#include <language/utils/BasicAffectationRegistrerFor.hpp>
+#include <language/utils/BinaryOperatorProcessorBuilder.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OperatorRepository.hpp>
+#include <language/utils/TypeDescriptor.hpp>
+
 #include <test_BinaryExpressionProcessor_utils.hpp>
 
 // clazy:excludeall=non-pod-global-static
 
+#define CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, result)                                                    \
+  {                                                                                                             \
+    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};                                                  \
+    auto ast = ASTBuilder::build(input);                                                                        \
+                                                                                                                \
+    ASTModulesImporter{*ast};                                                                                   \
+                                                                                                                \
+    BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")}; \
+                                                                                                                \
+    OperatorRepository& repository = OperatorRepository::instance();                                            \
+                                                                                                                \
+    repository.addBinaryOperator<language::plus_op>(                                                            \
+      std::make_shared<                                                                                         \
+        BinaryOperatorProcessorBuilder<language::plus_op, std::shared_ptr<const double>,                        \
+                                       std::shared_ptr<const double>, std::shared_ptr<const double>>>());       \
+                                                                                                                \
+    repository.addBinaryOperator<language::plus_op>(                                                            \
+      std::make_shared<BinaryOperatorProcessorBuilder<language::plus_op, std::shared_ptr<const double>,         \
+                                                      std::shared_ptr<const double>, double>>());               \
+                                                                                                                \
+    repository.addBinaryOperator<language::plus_op>(                                                            \
+      std::make_shared<BinaryOperatorProcessorBuilder<language::plus_op, std::shared_ptr<const double>, double, \
+                                                      std::shared_ptr<const double>>>());                       \
+                                                                                                                \
+    SymbolTable& symbol_table = *ast->m_symbol_table;                                                           \
+    auto [i_symbol, success]  = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin());               \
+    if (not success) {                                                                                          \
+      throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing");          \
+    }                                                                                                           \
+                                                                                                                \
+    i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>());              \
+    i_symbol->attributes().setIsInitialized();                                                                  \
+    i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size();                                   \
+    symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId()));   \
+                                                                                                                \
+    auto [i_symbol_bt_a, success_bt_a] = symbol_table.add("bt_a", ast->begin());                                \
+    if (not success_bt_a) {                                                                                     \
+      throw UnexpectedError("cannot add 'bt_a' of type builtin_t for testing");                                 \
+    }                                                                                                           \
+    i_symbol_bt_a->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>);            \
+    i_symbol_bt_a->attributes().setIsInitialized();                                                             \
+    i_symbol_bt_a->attributes().value() =                                                                       \
+      EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2)));                 \
+                                                                                                                \
+    auto [i_symbol_bt_b, success_bt_b] = symbol_table.add("bt_b", ast->begin());                                \
+    if (not success_bt_b) {                                                                                     \
+      throw UnexpectedError("cannot add 'bt_b' of type builtin_t for testing");                                 \
+    }                                                                                                           \
+    i_symbol_bt_b->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>);            \
+    i_symbol_bt_b->attributes().setIsInitialized();                                                             \
+    i_symbol_bt_b->attributes().value() =                                                                       \
+      EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(5.3)));                 \
+                                                                                                                \
+    ASTNodeTypeCleaner<language::import_instruction>{*ast};                                                     \
+                                                                                                                \
+    ASTSymbolTableBuilder{*ast};                                                                                \
+    ASTNodeDataTypeBuilder{*ast};                                                                               \
+                                                                                                                \
+    ASTNodeDeclarationToAffectationConverter{*ast};                                                             \
+    ASTNodeTypeCleaner<language::var_declaration>{*ast};                                                        \
+                                                                                                                \
+    ASTNodeExpressionBuilder{*ast};                                                                             \
+    ExecutionPolicy exec_policy;                                                                                \
+    ast->execute(exec_policy);                                                                                  \
+                                                                                                                \
+    using namespace TAO_PEGTL_NAMESPACE;                                                                        \
+    position use_position{internal::iterator{"fixture"}, "fixture"};                                            \
+    use_position.byte    = 10000;                                                                               \
+    auto [symbol, found] = symbol_table.find("r", use_position);                                                \
+                                                                                                                \
+    auto attributes     = symbol->attributes();                                                                 \
+    auto embedded_value = std::get<EmbeddedData>(attributes.value());                                           \
+                                                                                                                \
+    double value = *dynamic_cast<const DataHandler<const double>&>(embedded_value.get()).data_ptr();            \
+    REQUIRE(value == expected);                                                                                 \
+  }
+
+#define CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data, error_msg)                                                    \
+  {                                                                                                               \
+    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};                                                    \
+    auto ast = ASTBuilder::build(input);                                                                          \
+                                                                                                                  \
+    ASTModulesImporter{*ast};                                                                                     \
+                                                                                                                  \
+    BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")};   \
+                                                                                                                  \
+    OperatorRepository& repository = OperatorRepository::instance();                                              \
+                                                                                                                  \
+    repository.addBinaryOperator<language::divide_op>(                                                            \
+      std::make_shared<                                                                                           \
+        BinaryOperatorProcessorBuilder<language::divide_op, std::shared_ptr<const double>,                        \
+                                       std::shared_ptr<const double>, std::shared_ptr<const double>>>());         \
+                                                                                                                  \
+    repository.addBinaryOperator<language::divide_op>(                                                            \
+      std::make_shared<BinaryOperatorProcessorBuilder<language::divide_op, std::shared_ptr<const double>,         \
+                                                      std::shared_ptr<const double>, double>>());                 \
+                                                                                                                  \
+    repository.addBinaryOperator<language::divide_op>(                                                            \
+      std::make_shared<BinaryOperatorProcessorBuilder<language::divide_op, std::shared_ptr<const double>, double, \
+                                                      std::shared_ptr<const double>>>());                         \
+                                                                                                                  \
+    SymbolTable& symbol_table = *ast->m_symbol_table;                                                             \
+    auto [i_symbol, success]  = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin());                 \
+    if (not success) {                                                                                            \
+      throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing");            \
+    }                                                                                                             \
+                                                                                                                  \
+    i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>());                \
+    i_symbol->attributes().setIsInitialized();                                                                    \
+    i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size();                                     \
+    symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId()));     \
+                                                                                                                  \
+    auto [i_symbol_bt_a, success_bt_a] = symbol_table.add("bt_a", ast->begin());                                  \
+    if (not success_bt_a) {                                                                                       \
+      throw UnexpectedError("cannot add 'bt_a' of type builtin_t for testing");                                   \
+    }                                                                                                             \
+    i_symbol_bt_a->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>);              \
+    i_symbol_bt_a->attributes().setIsInitialized();                                                               \
+    i_symbol_bt_a->attributes().value() =                                                                         \
+      EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2)));                   \
+                                                                                                                  \
+    auto [i_symbol_bt_b, success_bt_b] = symbol_table.add("bt_b", ast->begin());                                  \
+    if (not success_bt_b) {                                                                                       \
+      throw UnexpectedError("cannot add 'bt_b' of type builtin_t for testing");                                   \
+    }                                                                                                             \
+    i_symbol_bt_b->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>);              \
+    i_symbol_bt_b->attributes().setIsInitialized();                                                               \
+    i_symbol_bt_b->attributes().value() =                                                                         \
+      EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(5.3)));                   \
+                                                                                                                  \
+    ASTNodeTypeCleaner<language::import_instruction>{*ast};                                                       \
+                                                                                                                  \
+    ASTSymbolTableBuilder{*ast};                                                                                  \
+    ASTNodeDataTypeBuilder{*ast};                                                                                 \
+                                                                                                                  \
+    ASTNodeDeclarationToAffectationConverter{*ast};                                                               \
+    ASTNodeTypeCleaner<language::var_declaration>{*ast};                                                          \
+                                                                                                                  \
+    ASTNodeExpressionBuilder{*ast};                                                                               \
+    ExecutionPolicy exec_policy;                                                                                  \
+    REQUIRE_THROWS_WITH(ast->execute(exec_policy), error_msg);                                                    \
+  }
+
 TEST_CASE("BinaryExpressionProcessor arithmetic", "[language]")
 {
   SECTION("+")
@@ -33,7 +184,7 @@ TEST_CASE("BinaryExpressionProcessor arithmetic", "[language]")
     {
       CHECK_BINARY_EXPRESSION_RESULT(R"(let z:Z, z = -1 + true;)", "z", 0l);
       CHECK_BINARY_EXPRESSION_RESULT(R"(let n:N, n = 4; let z:Z, z = -3 + n;)", "z", 1l);
-      CHECK_BINARY_EXPRESSION_RESULT(R"(let n:N, n = 2; let z:Z, z = -3 + n;)", "z", -1l);
+      CHECK_BINARY_EXPRESSION_RESULT(R"(let n:N, n = 2; let z:Z, z = (-3 + n);)", "z", -1l);
       CHECK_BINARY_EXPRESSION_RESULT(R"(let z:Z, z = 1 + 2;)", "z", 3l);
       CHECK_BINARY_EXPRESSION_RESULT(R"(let x:R, x = 3 + 2.5;)", "x", 5.5);
     }
@@ -41,7 +192,7 @@ TEST_CASE("BinaryExpressionProcessor arithmetic", "[language]")
     SECTION("lhs is R")
     {
       CHECK_BINARY_EXPRESSION_RESULT(R"(let r:R, r = -1.2 + true;)", "r", (-1.2 + true));
-      CHECK_BINARY_EXPRESSION_RESULT(R"(let n:N, n = 2; let r:R, r = -1.2 + n;)", "r", (-1.2 + uint64_t{2}));
+      CHECK_BINARY_EXPRESSION_RESULT(R"(let n:N, n = 2; let r:R, r = (-1.2 + n);)", "r", (-1.2 + uint64_t{2}));
       CHECK_BINARY_EXPRESSION_RESULT(R"(let r:R, r = -1.2 + 1;)", "r", (-1.2 + 1));
       CHECK_BINARY_EXPRESSION_RESULT(R"(let r:R, r = -1.2 + 2.3;)", "r", (-1.2 + 2.3));
     }
@@ -68,7 +219,7 @@ TEST_CASE("BinaryExpressionProcessor arithmetic", "[language]")
     SECTION("lhs is Z")
     {
       CHECK_BINARY_EXPRESSION_RESULT(R"(let z:Z, z = -1 - true;)", "z", -2l);
-      CHECK_BINARY_EXPRESSION_RESULT(R"(let n:N, n = 4; let z:Z, z = 3 - n;)", "z", -1l);
+      CHECK_BINARY_EXPRESSION_RESULT(R"(let n:N, n = 4; let z:Z, z = (-3 - n + 6);)", "z", -1l);
       CHECK_BINARY_EXPRESSION_RESULT(R"(let z:Z, z = 7 - 2;)", "z", 5l);
       CHECK_BINARY_EXPRESSION_RESULT(R"(let x:R, x = 4 - 2.5;)", "x", 1.5);
     }
@@ -151,4 +302,43 @@ TEST_CASE("BinaryExpressionProcessor arithmetic", "[language]")
       CHECK_BINARY_EXPRESSION_RESULT(R"(let r:R, r = -1.2 / 2.3;)", "r", (-1.2 / 2.3));
     }
   }
+
+  SECTION("binary operator [builtin]")
+  {
+    SECTION("builtin both side")
+    {
+      std::string_view data = R"(let r:builtin_t, r = bt_a + bt_b;)";
+      const double expected = double{3.2} + double{5.3};
+
+      CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, expected);
+    }
+
+    SECTION("builtin lhs")
+    {
+      std::string_view data = R"(let r:builtin_t, r = bt_a + 5.;)";
+      const double expected = double{3.2} + double{5};
+
+      CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, expected);
+    }
+
+    SECTION("builtin rhs")
+    {
+      std::string_view data = R"(let r:builtin_t, r = 5. + bt_a;)";
+      const double expected = double{3.2} + double{5};
+
+      CHECK_BUILTIN_BINARY_EXPRESSION_RESULT(data, expected);
+    }
+
+    SECTION("runtime error")
+    {
+      std::string_view data_both = R"(let r:builtin_t, r = bt_a / bt_b;)";
+      CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data_both, "runtime error both");
+
+      std::string_view data_lhs = R"(let r:builtin_t, r = bt_a / 2.3;)";
+      CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data_lhs, "runtime error lhs");
+
+      std::string_view data_rhs = R"(let r:builtin_t, r = 2.3/ bt_a;)";
+      CHECK_BUILTIN_BINARY_EXPRESSION_ERROR(data_rhs, "runtime error rhs");
+    }
+  }
 }
diff --git a/tests/test_BinaryExpressionProcessor_raw.cpp b/tests/test_BinaryExpressionProcessor_raw.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..22e32de89c715a5fa8e24a38007e0ab792e9dd19
--- /dev/null
+++ b/tests/test_BinaryExpressionProcessor_raw.cpp
@@ -0,0 +1,87 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/node_processor/BinaryExpressionProcessor.hpp>
+#include <language/utils/OFStream.hpp>
+#include <utils/pugs_config.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("BinaryExpressionProcessor raw operators", "[language]")
+{
+  REQUIRE(BinOp<language::and_op>{}.eval(true, true) == true);
+  REQUIRE(BinOp<language::and_op>{}.eval(true, false) == false);
+  REQUIRE(BinOp<language::and_op>{}.eval(false, true) == false);
+  REQUIRE(BinOp<language::and_op>{}.eval(false, false) == false);
+
+  REQUIRE(BinOp<language::or_op>{}.eval(true, true) == true);
+  REQUIRE(BinOp<language::or_op>{}.eval(true, false) == true);
+  REQUIRE(BinOp<language::or_op>{}.eval(false, true) == true);
+  REQUIRE(BinOp<language::or_op>{}.eval(false, false) == false);
+
+  REQUIRE(BinOp<language::xor_op>{}.eval(true, true) == false);
+  REQUIRE(BinOp<language::xor_op>{}.eval(true, false) == true);
+  REQUIRE(BinOp<language::xor_op>{}.eval(false, true) == true);
+  REQUIRE(BinOp<language::xor_op>{}.eval(false, false) == false);
+
+  REQUIRE(BinOp<language::eqeq_op>{}.eval(3, 3) == true);
+  REQUIRE(BinOp<language::eqeq_op>{}.eval(2, 3) == false);
+
+  REQUIRE(BinOp<language::not_eq_op>{}.eval(3, 3) == false);
+  REQUIRE(BinOp<language::not_eq_op>{}.eval(2, 3) == true);
+
+  REQUIRE(BinOp<language::lesser_op>{}.eval(2.9, 3) == true);
+  REQUIRE(BinOp<language::lesser_op>{}.eval(3, 3) == false);
+  REQUIRE(BinOp<language::lesser_op>{}.eval(3.1, 3) == false);
+
+  REQUIRE(BinOp<language::lesser_or_eq_op>{}.eval(2.9, 3) == true);
+  REQUIRE(BinOp<language::lesser_or_eq_op>{}.eval(3, 3) == true);
+  REQUIRE(BinOp<language::lesser_or_eq_op>{}.eval(3.1, 3) == false);
+
+  REQUIRE(BinOp<language::greater_op>{}.eval(2.9, 3) == false);
+  REQUIRE(BinOp<language::greater_op>{}.eval(3, 3) == false);
+  REQUIRE(BinOp<language::greater_op>{}.eval(3.1, 3) == true);
+
+  REQUIRE(BinOp<language::greater_or_eq_op>{}.eval(2.9, 3) == false);
+  REQUIRE(BinOp<language::greater_or_eq_op>{}.eval(3, 3) == true);
+  REQUIRE(BinOp<language::greater_or_eq_op>{}.eval(3.1, 3) == true);
+
+  REQUIRE(BinOp<language::plus_op>{}.eval(2.9, 3) == (2.9 + 3));
+  REQUIRE(BinOp<language::minus_op>{}.eval(2.9, 3) == (2.9 - 3));
+  REQUIRE(BinOp<language::multiply_op>{}.eval(2.9, 3) == (2.9 * 3));
+  REQUIRE(BinOp<language::divide_op>{}.eval(2.9, 3) == (2.9 / 3));
+
+  {
+    std::filesystem::path path{PUGS_BINARY_DIR};
+    path.append("tests").append(std::string{"binary_expression_processor_shift_left_"} + std::to_string(getpid()));
+
+    std::string filename = path.string();
+
+    std::shared_ptr<const OStream> p_fout = std::make_shared<OFStream>(filename);
+    BinOp<language::shift_left_op>{}.eval(p_fout, true);
+    BinOp<language::shift_left_op>{}.eval(p_fout, std::string{" bar\n"});
+    p_fout.reset();
+
+    REQUIRE(std::filesystem::exists(filename));
+
+    {
+      std::string file_content;
+      std::ifstream fin(filename.c_str());
+
+      do {
+        char c = fin.get();
+        if (c != EOF) {
+          file_content += c;
+        }
+      } while (fin);
+
+      REQUIRE(file_content == "true bar\n");
+    }
+
+    std::filesystem::remove(filename);
+    REQUIRE(not std::filesystem::exists(filename));
+  }
+
+  REQUIRE(BinOp<language::shift_left_op>{}.eval(3, 2) == (3 << 2));
+  REQUIRE(BinOp<language::shift_right_op>{}.eval(17, 2) == (17 >> 2));
+}
diff --git a/tests/test_BinaryExpressionProcessor_shift.cpp b/tests/test_BinaryExpressionProcessor_shift.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c1892a2ff3a793a1cdc9a48e6f1a454b090fe77d
--- /dev/null
+++ b/tests/test_BinaryExpressionProcessor_shift.cpp
@@ -0,0 +1,85 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <test_BinaryExpressionProcessor_utils.hpp>
+#include <utils/pugs_config.hpp>
+
+#include <fstream>
+#include <netdb.h>
+#include <unistd.h>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("BinaryExpressionProcessor shift", "[language]")
+{
+  SECTION("<<")
+  {
+    std::filesystem::path path{PUGS_BINARY_DIR};
+    path.append("tests").append(std::string{"binary_expression_processor_"} + std::to_string(getpid()));
+
+    std::string filename = path.string();
+
+    {
+      std::ostringstream data;
+      data << R"(import socket;)";
+      data << "let fout:ostream, fout = ofstream(\"" << path.string() << "\");\n";
+      data << R"(fout << 2 << " " << true << " " << 2 + 3 << "\n";
+fout << createSocketServer(0);)";
+
+      TAO_PEGTL_NAMESPACE::string_input input{data.str(), "test.pgs"};
+      auto ast = ASTBuilder::build(input);
+
+      ASTModulesImporter{*ast};
+      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+      ASTSymbolTableBuilder{*ast};
+      ASTNodeDataTypeBuilder{*ast};
+
+      ASTNodeDeclarationToAffectationConverter{*ast};
+      ASTNodeTypeCleaner<language::var_declaration>{*ast};
+
+      ASTNodeExpressionBuilder{*ast};
+      ExecutionPolicy exec_policy;
+      ast->execute(exec_policy);
+    }
+
+    REQUIRE(std::filesystem::exists(filename));
+
+    {
+      std::string file_content;
+      std::ifstream fin(filename.c_str());
+
+      do {
+        char c = fin.get();
+        if (c != EOF) {
+          file_content += c;
+        }
+      } while (fin);
+
+      std::string expected = "2 true 5\n";
+      char hbuf[NI_MAXHOST];
+      ::gethostname(hbuf, NI_MAXHOST);
+      expected += hbuf;
+      expected += ':';
+
+      REQUIRE(file_content.size() > expected.size());
+      REQUIRE(file_content.substr(0, expected.size()) == expected);
+
+      std::string suffix = file_content.substr(expected.size(), file_content.size() - expected.size());
+
+      auto is_int = [](const std::string& s) {
+        for (const char& c : s) {
+          if (not std::isdigit(c)) {
+            return false;
+          }
+        }
+        return true;
+      };
+
+      REQUIRE(is_int(suffix));
+    }
+
+    std::filesystem::remove(filename);
+    REQUIRE(not std::filesystem::exists(filename));
+  }
+}
diff --git a/tests/test_BinaryOperatorMangler.cpp b/tests/test_BinaryOperatorMangler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..329c59dcb9fd5fadfae97f39dcbb9e7a6ee3e582
--- /dev/null
+++ b/tests/test_BinaryOperatorMangler.cpp
@@ -0,0 +1,31 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/utils/BinaryOperatorMangler.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("BinaryOperatorMangler", "[language]")
+{
+  SECTION("binary operators")
+  {
+    const ASTNodeDataType R = ASTNodeDataType::build<ASTNodeDataType::double_t>();
+    const ASTNodeDataType Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+
+    REQUIRE(binaryOperatorMangler<language::multiply_op>(R, Z) == "R * Z");
+    REQUIRE(binaryOperatorMangler<language::divide_op>(R, Z) == "R / Z");
+    REQUIRE(binaryOperatorMangler<language::plus_op>(R, Z) == "R + Z");
+    REQUIRE(binaryOperatorMangler<language::minus_op>(R, Z) == "R - Z");
+    REQUIRE(binaryOperatorMangler<language::or_op>(R, Z) == "R or Z");
+    REQUIRE(binaryOperatorMangler<language::and_op>(R, Z) == "R and Z");
+    REQUIRE(binaryOperatorMangler<language::xor_op>(R, Z) == "R xor Z");
+    REQUIRE(binaryOperatorMangler<language::greater_op>(R, Z) == "R > Z");
+    REQUIRE(binaryOperatorMangler<language::greater_or_eq_op>(R, Z) == "R >= Z");
+    REQUIRE(binaryOperatorMangler<language::lesser_op>(R, Z) == "R < Z");
+    REQUIRE(binaryOperatorMangler<language::lesser_or_eq_op>(R, Z) == "R <= Z");
+    REQUIRE(binaryOperatorMangler<language::eqeq_op>(R, Z) == "R == Z");
+    REQUIRE(binaryOperatorMangler<language::not_eq_op>(R, Z) == "R != Z");
+    REQUIRE(binaryOperatorMangler<language::shift_left_op>(R, Z) == "R << Z");
+    REQUIRE(binaryOperatorMangler<language::shift_right_op>(R, Z) == "R >> Z");
+  }
+}
diff --git a/tests/test_BuiltinFunctionEmbedder.cpp b/tests/test_BuiltinFunctionEmbedder.cpp
index ba88ac0b0a0e3d6e7c3a16977e03437a1e14a05c..51b2462b44a696e9b5214678b17e96d888c6dd27 100644
--- a/tests/test_BuiltinFunctionEmbedder.cpp
+++ b/tests/test_BuiltinFunctionEmbedder.cpp
@@ -89,7 +89,7 @@ TEST_CASE("BuiltinFunctionEmbedder", "[language]")
 
     BuiltinFunctionEmbedder<TinyVector<2>(TinyMatrix<2>, TinyVector<2>)> embedded_c{c};
 
-    TinyMatrix<2> a_arg = {2.3, 1, -2, 3};
+    TinyMatrix<2> a_arg{2.3, 1, -2, 3};
     TinyVector<2> x_arg{3, 2};
 
     std::vector<DataVariant> args;
diff --git a/tests/test_BuiltinFunctionEmbedderUtils.cpp b/tests/test_BuiltinFunctionEmbedderUtils.cpp
index 965d709b97187fbab363b4cacad7cde4f8ad7b7c..da2fbdd1dc8466377621b077af3ef790ec15e24e 100644
--- a/tests/test_BuiltinFunctionEmbedderUtils.cpp
+++ b/tests/test_BuiltinFunctionEmbedderUtils.cpp
@@ -226,7 +226,7 @@ foo(3,1);
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyVector<2>(double, const TinyVector<1>&)>>(
                                             [](double a, const TinyVector<1>& x) -> TinyVector<2> {
-                                              return {a, x[0]};
+                                              return TinyVector<2>{a, x[0]};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -253,7 +253,7 @@ foo(3.1,f(1));
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyVector<2>(double, const TinyVector<1>&)>>(
                                             [](double a, const TinyVector<1>& x) -> TinyVector<2> {
-                                              return {a, x[0]};
+                                              return TinyVector<2>{a, x[0]};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -280,7 +280,7 @@ foo(f(2));
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyVector<2>(double, const TinyVector<1>&)>>(
                                             [](double a, const TinyVector<1>& x) -> TinyVector<2> {
-                                              return {a, x[0]};
+                                              return TinyVector<2>{a, x[0]};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -334,7 +334,7 @@ foo(3,0);
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyVector<3>(double, const TinyVector<2>&)>>(
                                             [](double a, const TinyVector<2>& x) -> TinyVector<3> {
-                                              return {a, x[0], x[1]};
+                                              return TinyVector<3>{a, x[0], x[1]};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -360,7 +360,7 @@ foo(3.1,(1,2.3));
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyVector<2>(double, const TinyVector<2>&)>>(
                                             [](double a, const TinyVector<2>& x) -> TinyVector<2> {
-                                              return {a * x[1], x[0]};
+                                              return TinyVector<2>{a * x[1], x[0]};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -390,7 +390,7 @@ foo(3,x);
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyVector<2>(double, const TinyVector<3>&)>>(
                                             [](double a, const TinyVector<3>& x) -> TinyVector<2> {
-                                              return {a * x[0], x[1] + x[2]};
+                                              return TinyVector<2>{a * x[0], x[1] + x[2]};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -442,7 +442,7 @@ foo(3.1,(1,2.3,4));
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyVector<2>(double, const TinyVector<3>&)>>(
                                             [](double a, const TinyVector<3>& x) -> TinyVector<2> {
-                                              return {a * x[1], x[0] * x[2]};
+                                              return TinyVector<2>{a * x[1], x[0] * x[2]};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -496,7 +496,7 @@ foo(3,1);
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyMatrix<2>(double, const TinyMatrix<1>&)>>(
                                             [](double a, const TinyMatrix<1>& x) -> TinyMatrix<2> {
-                                              return {a, x(0, 0), a * x(0, 0), x(0, 0)};
+                                              return TinyMatrix<2>{a, x(0, 0), a * x(0, 0), x(0, 0)};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -523,7 +523,7 @@ foo(3.1,f(1));
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyMatrix<2>(double, const TinyMatrix<1>&)>>(
                                             [](double a, const TinyMatrix<1>& x) -> TinyMatrix<2> {
-                                              return {a, x(0, 0), 2 + x(0, 0), 1};
+                                              return TinyMatrix<2>{a, x(0, 0), 2 + x(0, 0), 1};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -550,7 +550,7 @@ foo(f(2));
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyMatrix<2>(double, const TinyMatrix<1>&)>>(
                                             [](double a, const TinyMatrix<1>& x) -> TinyMatrix<2> {
-                                              return {a, x(0, 0), a + x(0, 0), 1};
+                                              return TinyMatrix<2>{a, x(0, 0), a + x(0, 0), 1};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -604,9 +604,9 @@ foo(3,0);
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyMatrix<3>(double, const TinyMatrix<2>&)>>(
                                             [](double a, const TinyMatrix<2>& x) -> TinyMatrix<3> {
-                                              return {a, x(0, 0), x(1, 0),   //
-                                                      a, x(1, 0), x(1, 1),   //
-                                                      a, x(0, 0), x(1, 1)};
+                                              return TinyMatrix<3>{a, x(0, 0), x(1, 0),   //
+                                                                   a, x(1, 0), x(1, 1),   //
+                                                                   a, x(0, 0), x(1, 1)};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -632,7 +632,7 @@ foo(3.1,(1,2.3,0,3));
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyMatrix<2>(double, const TinyMatrix<2>&)>>(
                                             [](double a, const TinyMatrix<2>& x) -> TinyMatrix<2> {
-                                              return {a * x(0, 0), x(1, 1), a, x(1, 0)};
+                                              return TinyMatrix<2>{a * x(0, 0), x(1, 1), a, x(1, 0)};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -662,8 +662,8 @@ foo(3,x);
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyMatrix<2>(double, const TinyMatrix<3>&)>>(
                                             [](double a, const TinyMatrix<3>& x) -> TinyMatrix<2> {
-                                              return {a * x(0, 0), x(1, 0) + x(0, 1),   //
-                                                      a, x(1, 1)};
+                                              return TinyMatrix<2>{a * x(0, 0), x(1, 0) + x(0, 1),   //
+                                                                   a, x(1, 1)};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
@@ -715,8 +715,8 @@ foo(3.1,(1, 2.3, 4, 0.3, 2.5, 4.6, 2.7, 8.1, -9));
                                           std::make_shared<
                                             BuiltinFunctionEmbedder<TinyMatrix<2>(double, const TinyMatrix<3>&)>>(
                                             [](double a, const TinyMatrix<3>& x) -> TinyMatrix<2> {
-                                              return {a * x(1, 1), x(1, 0),   //
-                                                      x(0, 1), a * x(0, 0)};
+                                              return TinyMatrix<2>{a * x(1, 1), x(1, 0),   //
+                                                                   x(0, 1), a * x(0, 0)};
                                             }))});
 
       auto function_embedder = getBuiltinFunctionEmbedder(*root_node->children[0]);
diff --git a/tests/test_BuiltinFunctionProcessor.cpp b/tests/test_BuiltinFunctionProcessor.cpp
index a1fb3fe82b982bc50d2b5247c1158941da24ce0d..bbc5ac52e1a6e8034b68b591adff83c1d8701ff5 100644
--- a/tests/test_BuiltinFunctionProcessor.cpp
+++ b/tests/test_BuiltinFunctionProcessor.cpp
@@ -278,6 +278,42 @@ let z:Z, z = round(-1.2);
       CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "z", int64_t{-1});
     }
 
+    {   // min
+      tested_function_set.insert("min:R*R");
+      std::string_view data = R"(
+import math;
+let x:R, x = min(-2,2.3);
+)";
+      CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "x", double{-2});
+    }
+
+    {   // min
+      tested_function_set.insert("min:Z*Z");
+      std::string_view data = R"(
+import math;
+let z:Z, z = min(-1,2);
+)";
+      CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "z", int64_t{-1});
+    }
+
+    {   // max
+      tested_function_set.insert("max:R*R");
+      std::string_view data = R"(
+import math;
+let x:R, x = max(-1,2.3);
+)";
+      CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "x", double{2.3});
+    }
+
+    {   // max
+      tested_function_set.insert("max:Z*Z");
+      std::string_view data = R"(
+import math;
+let z:Z, z = max(-1,2);
+)";
+      CHECK_BUILTIN_FUNCTION_EVALUATION_RESULT(data, "z", int64_t{2});
+    }
+
     {   // dot
       tested_function_set.insert("dot:R^1*R^1");
       std::string_view data = R"(
diff --git a/tests/test_BuiltinFunctionRegister.hpp b/tests/test_BuiltinFunctionRegister.hpp
index 0ac5226fcac218e5ad195e5f2ecbdef6aa058cb3..49bceabb565ec13b04350f083009c85702886fc2 100644
--- a/tests/test_BuiltinFunctionRegister.hpp
+++ b/tests/test_BuiltinFunctionRegister.hpp
@@ -53,7 +53,7 @@ class test_BuiltinFunctionRegister
 
     m_name_builtin_function_map.insert(
       std::make_pair("RtoR1:R", std::make_shared<BuiltinFunctionEmbedder<TinyVector<1>(double)>>(
-                                  [](double r) -> TinyVector<1> { return {r}; })));
+                                  [](double r) -> TinyVector<1> { return TinyVector<1>{r}; })));
 
     m_name_builtin_function_map.insert(
       std::make_pair("R1toR:R^1", std::make_shared<BuiltinFunctionEmbedder<double(TinyVector<1>)>>(
@@ -74,7 +74,7 @@ class test_BuiltinFunctionRegister
 
     m_name_builtin_function_map.insert(
       std::make_pair("RtoR11:R", std::make_shared<BuiltinFunctionEmbedder<TinyMatrix<1>(double)>>(
-                                   [](double r) -> TinyMatrix<1> { return {r}; })));
+                                   [](double r) -> TinyMatrix<1> { return TinyMatrix<1>{r}; })));
 
     m_name_builtin_function_map.insert(
       std::make_pair("R11toR:R^1x1", std::make_shared<BuiltinFunctionEmbedder<double(TinyMatrix<1>)>>(
diff --git a/tests/test_CRSMatrixDescriptor.cpp b/tests/test_CRSMatrixDescriptor.cpp
index 28276aaadb747f01933fda8ce302cb853af5cf6d..4d31a7397aee4fa031179a912f143b3cf3f32578 100644
--- a/tests/test_CRSMatrixDescriptor.cpp
+++ b/tests/test_CRSMatrixDescriptor.cpp
@@ -12,6 +12,34 @@ template class CRSMatrixDescriptor<int>;
 
 TEST_CASE("CRSMatrixDescriptor", "[algebra]")
 {
+  SECTION("sizes")
+  {
+    SECTION("rectangle")
+    {
+      const size_t nb_lines   = 2;
+      const size_t nb_columns = 5;
+
+      Array<int> non_zeros{nb_lines};
+      non_zeros.fill(2);
+      CRSMatrixDescriptor<int> S(nb_lines, nb_columns, non_zeros);
+
+      REQUIRE(S.numberOfRows() == 2);
+      REQUIRE(S.numberOfColumns() == 5);
+    }
+
+    SECTION("square")
+    {
+      const size_t nb_lines = 3;
+
+      Array<int> non_zeros{nb_lines};
+      non_zeros.fill(2);
+      CRSMatrixDescriptor<int> S(nb_lines, non_zeros);
+
+      REQUIRE(S.numberOfRows() == 3);
+      REQUIRE(S.numberOfColumns() == 3);
+    }
+  }
+
   SECTION("has overflow / not filled")
   {
     const size_t nb_lines   = 2;
diff --git a/tests/test_CastArray.cpp b/tests/test_CastArray.cpp
index d1c9cad2c1f7d2d39dad4e8fba8764d9b0bd6c2c..0f4a841f1e6bfc270fe789947efeafdccff40ff7 100644
--- a/tests/test_CastArray.cpp
+++ b/tests/test_CastArray.cpp
@@ -1,7 +1,6 @@
 #include <catch2/catch_test_macros.hpp>
 #include <catch2/matchers/catch_matchers_all.hpp>
 
-#include <utils/ArrayUtils.hpp>
 #include <utils/CastArray.hpp>
 
 // clazy:excludeall=non-pod-global-static
diff --git a/tests/test_CellIntegrator.cpp b/tests/test_CellIntegrator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ebd322d14c8ea5df4053163dcae9b60d02318a53
--- /dev/null
+++ b/tests/test_CellIntegrator.cpp
@@ -0,0 +1,2784 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/ItemValue.hpp>
+#include <mesh/Mesh.hpp>
+#include <scheme/CellIntegrator.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("CellIntegrator", "[scheme]")
+{
+  SECTION("scalar")
+  {
+    SECTION("1D")
+    {
+      using R1 = TinyVector<1>;
+
+      const auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      auto f          = [](const R1& x) -> double { return x[0] * x[0] + 1; };
+
+      Array<const double> int_f_per_cell = [=] {
+        Array<double> int_f(mesh->numberOfCells());
+        auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+        parallel_for(
+          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+            auto cell_node_list  = cell_to_node_matrix[cell_id];
+            auto xr              = mesh->xr();
+            const double x_left  = xr[cell_node_list[0]][0];
+            const double x_right = xr[cell_node_list[1]][0];
+            int_f[cell_id]       = 1. / 3 * (x_right * x_right * x_right - x_left * x_left * x_left) + x_right - x_left;
+          });
+
+        return int_f;
+      }();
+
+      SECTION("direct formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<double> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R1 x) { return f(x); }, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<double> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<double> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<double> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<double> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+
+      SECTION("tensorial formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<double> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R1 x) { return f(x); }, GaussLobattoQuadratureDescriptor(2), *mesh,
+                                        values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<double> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<double> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<double> values = CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<double> values =
+              CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      using R2 = TinyVector<2>;
+
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+
+      auto f = [](const R2& X) -> double {
+        const double x = X[0];
+        const double y = X[1];
+        return x * x + 2 * x * y + 3 * y * y + 2;
+      };
+
+      Array<const double> int_f_per_cell = [=] {
+        Array<double> int_f(mesh->numberOfCells());
+        auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+        auto cell_type           = mesh->connectivity().cellType();
+
+        parallel_for(
+          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+            auto cell_node_list = cell_to_node_matrix[cell_id];
+            auto xr             = mesh->xr();
+            double integral     = 0;
+
+            switch (cell_type[cell_id]) {
+            case CellType::Triangle: {
+              TriangleTransformation<2> T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]]);
+              auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(4));
+
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.jacobianDeterminant() * f(T(xi));
+              }
+              break;
+            }
+            case CellType::Quadrangle: {
+              SquareTransformation<2> T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                        xr[cell_node_list[3]]);
+              auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(4));
+
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+              }
+              break;
+            }
+            default: {
+              throw UnexpectedError("invalid cell type in 2d");
+            }
+            }
+            int_f[cell_id] = integral;
+          });
+
+        return int_f;
+      }();
+
+      SECTION("direct formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<double> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<double> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<double> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<double> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<double> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+
+      SECTION("tensorial formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<double> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussLobattoQuadratureDescriptor(2), *mesh,
+                                        values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<double> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<double> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<double> values = CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<double> values =
+              CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> double {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1;
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            Array<const double> int_f_per_cell = [=] {
+              Array<double> int_f(mesh->numberOfCells());
+              auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+              auto cell_type           = mesh->connectivity().cellType();
+
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                  auto cell_node_list = cell_to_node_matrix[cell_id];
+                  auto xr             = mesh->xr();
+                  double integral     = 0;
+
+                  switch (cell_type[cell_id]) {
+                  case CellType::Tetrahedron: {
+                    TetrahedronTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                                xr[cell_node_list[3]]);
+                    auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant() * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Pyramid: {
+                    PyramidTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                            xr[cell_node_list[3]], xr[cell_node_list[4]]);
+                    auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Prism: {
+                    PrismTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                          xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]]);
+                    auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Hexahedron: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]],
+                                         xr[cell_node_list[6]], xr[cell_node_list[7]]);
+                    auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Diamond: {
+                    if (cell_node_list.size() == 5) {
+                      auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(4));
+                      {   // top tetrahedron
+                        TetrahedronTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                                     xr[cell_node_list[3]], xr[cell_node_list[4]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant() * f(T0(xi));
+                        }
+                      }
+                      {   // bottom tetrahedron
+                        TetrahedronTransformation T1(xr[cell_node_list[3]], xr[cell_node_list[2]],
+                                                     xr[cell_node_list[1]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant() * f(T1(xi));
+                        }
+                      }
+                    } else if (cell_node_list.size() == 6) {
+                      auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(4));
+                      {   // top pyramid
+                        PyramidTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]], xr[cell_node_list[3]],
+                                                 xr[cell_node_list[4]], xr[cell_node_list[5]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant(xi) * f(T0(xi));
+                        }
+                      }
+                      {   // bottom pyramid
+                        PyramidTransformation T1(xr[cell_node_list[4]], xr[cell_node_list[3]], xr[cell_node_list[2]],
+                                                 xr[cell_node_list[1]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant(xi) * f(T1(xi));
+                        }
+                      }
+                    } else {
+                      INFO("Diamond cells with more than 6 vertices are not tested");
+                      REQUIRE(false);
+                    }
+                    break;
+                  }
+                  default: {
+                    INFO("Diamond cells not tested yet");
+                    REQUIRE(cell_type[cell_id] != CellType::Diamond);
+                  }
+                  }
+                  int_f[cell_id] = integral;
+                });
+
+              return int_f;
+            }();
+
+            SECTION("all cells")
+            {
+              SECTION("CellValue")
+              {
+                CellValue<double> values(mesh->connectivity());
+                CellIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == 0);
+              }
+
+              SECTION("Array")
+              {
+                Array<double> values(mesh->numberOfCells());
+
+                CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == 0);
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<double> values(mesh->numberOfCells());
+                CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == 0);
+              }
+            }
+
+            SECTION("cell list")
+            {
+              SECTION("Array")
+              {
+                Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                Array<double> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == 0);
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                SmallArray<double> values =
+                  CellIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == 0);
+              }
+            }
+          }
+
+          SECTION("tensorial formula")
+          {
+            Array<const double> int_f_per_cell = [=] {
+              Array<double> int_f(mesh->numberOfCells());
+              auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+              auto cell_type           = mesh->connectivity().cellType();
+
+              auto qf = QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(4));
+
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                  auto cell_node_list = cell_to_node_matrix[cell_id];
+                  auto xr             = mesh->xr();
+                  double integral     = 0;
+
+                  switch (cell_type[cell_id]) {
+                  case CellType::Tetrahedron: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[2]], xr[cell_node_list[3]], xr[cell_node_list[3]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[3]]);
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Pyramid: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[4]],
+                                         xr[cell_node_list[4]], xr[cell_node_list[4]]);
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Prism: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[2]], xr[cell_node_list[3]], xr[cell_node_list[4]],
+                                         xr[cell_node_list[5]], xr[cell_node_list[5]]);
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Hexahedron: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]],
+                                         xr[cell_node_list[6]], xr[cell_node_list[7]]);
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Diamond: {
+                    if (cell_node_list.size() == 5) {
+                      {   // top tetrahedron
+                        CubeTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]], xr[cell_node_list[3]],
+                                              xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[4]],
+                                              xr[cell_node_list[4]], xr[cell_node_list[4]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant(xi) * f(T0(xi));
+                        }
+                      }
+                      {   // bottom tetrahedron
+                        CubeTransformation T1(xr[cell_node_list[3]], xr[cell_node_list[2]], xr[cell_node_list[1]],
+                                              xr[cell_node_list[1]], xr[cell_node_list[0]], xr[cell_node_list[0]],
+                                              xr[cell_node_list[0]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant(xi) * f(T1(xi));
+                        }
+                      }
+                    } else if (cell_node_list.size() == 6) {
+                      {   // top pyramid
+                        CubeTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]], xr[cell_node_list[3]],
+                                              xr[cell_node_list[4]], xr[cell_node_list[5]], xr[cell_node_list[5]],
+                                              xr[cell_node_list[5]], xr[cell_node_list[5]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant(xi) * f(T0(xi));
+                        }
+                      }
+                      {   // bottom pyramid
+                        CubeTransformation T1(xr[cell_node_list[4]], xr[cell_node_list[3]], xr[cell_node_list[2]],
+                                              xr[cell_node_list[1]], xr[cell_node_list[0]], xr[cell_node_list[0]],
+                                              xr[cell_node_list[0]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant(xi) * f(T1(xi));
+                        }
+                      }
+                    } else {
+                      INFO("Diamond cells with more than 6 vertices are not tested");
+                      REQUIRE(false);
+                    }
+                    break;
+                  }
+                  default: {
+                    INFO("Diamond cells not tested yet");
+                    REQUIRE(cell_type[cell_id] != CellType::Diamond);
+                  }
+                  }
+                  int_f[cell_id] = integral;
+                });
+
+              return int_f;
+            }();
+
+            SECTION("all cells")
+            {
+              SECTION("CellValue")
+              {
+                CellValue<double> values(mesh->connectivity());
+                CellIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                auto cell_type = mesh->connectivity().cellType();
+                double error   = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<double> values(mesh->numberOfCells());
+
+                CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<double> values(mesh->numberOfCells());
+                CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += std::abs(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("cell list")
+            {
+              SECTION("Array")
+              {
+                Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                Array<double> values =
+                  CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                SmallArray<double> values =
+                  CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += std::abs(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("vector")
+  {
+    using R2 = TinyVector<2>;
+
+    SECTION("1D")
+    {
+      using R1 = TinyVector<1>;
+
+      const auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      auto f          = [](const R1& x) -> R2 { return R2{x[0] * x[0] + 1, 2 * x[0]}; };
+
+      Array<const R2> int_f_per_cell = [=] {
+        Array<R2> int_f(mesh->numberOfCells());
+        auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+        parallel_for(
+          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+            auto cell_node_list  = cell_to_node_matrix[cell_id];
+            auto xr              = mesh->xr();
+            const double x_left  = xr[cell_node_list[0]][0];
+            const double x_right = xr[cell_node_list[1]][0];
+            int_f[cell_id] = R2{1. / 3 * (x_right * x_right * x_right - x_left * x_left * x_left) + x_right - x_left,
+                                x_right * x_right - x_left * x_left};
+          });
+
+        return int_f;
+      }();
+
+      SECTION("direct formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<R2> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R1 x) { return f(x); }, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<R2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<R2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+
+      SECTION("tensorial formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<R2> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R1 x) { return f(x); }, GaussLobattoQuadratureDescriptor(2), *mesh,
+                                        values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<R2> values = CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<R2> values = CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      using R2 = TinyVector<2>;
+
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+
+      auto f = [](const R2& X) -> R2 {
+        const double x = X[0];
+        const double y = X[1];
+        return R2{x * x + 2 * x * y + 3 * y * y + 2, 2 * x + 3 * y * y};
+      };
+
+      Array<const R2> int_f_per_cell = [=] {
+        Array<R2> int_f(mesh->numberOfCells());
+        auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+        auto cell_type           = mesh->connectivity().cellType();
+
+        parallel_for(
+          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+            auto cell_node_list = cell_to_node_matrix[cell_id];
+            auto xr             = mesh->xr();
+            R2 integral         = zero;
+
+            switch (cell_type[cell_id]) {
+            case CellType::Triangle: {
+              TriangleTransformation<2> T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]]);
+              auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(4));
+
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.jacobianDeterminant() * f(T(xi));
+              }
+              break;
+            }
+            case CellType::Quadrangle: {
+              SquareTransformation<2> T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                        xr[cell_node_list[3]]);
+              auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(4));
+
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+              }
+              break;
+            }
+            default: {
+              throw UnexpectedError("invalid cell type in 2d");
+            }
+            }
+            int_f[cell_id] = integral;
+          });
+
+        return int_f;
+      }();
+
+      SECTION("direct formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<R2> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<R2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<R2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+
+      SECTION("tensorial formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<R2> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussLobattoQuadratureDescriptor(2), *mesh,
+                                        values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<R2> values = CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<R2> values = CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> R2 {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return R2{x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1, 2 * x * y - 3 * y * z + 2 * x};
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            Array<const R2> int_f_per_cell = [=] {
+              Array<R2> int_f(mesh->numberOfCells());
+              auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+              auto cell_type           = mesh->connectivity().cellType();
+
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                  auto cell_node_list = cell_to_node_matrix[cell_id];
+                  auto xr             = mesh->xr();
+                  R2 integral         = zero;
+
+                  switch (cell_type[cell_id]) {
+                  case CellType::Tetrahedron: {
+                    TetrahedronTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                                xr[cell_node_list[3]]);
+                    auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant() * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Pyramid: {
+                    PyramidTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                            xr[cell_node_list[3]], xr[cell_node_list[4]]);
+                    auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Prism: {
+                    PrismTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                          xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]]);
+                    auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Hexahedron: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]],
+                                         xr[cell_node_list[6]], xr[cell_node_list[7]]);
+                    auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Diamond: {
+                    if (cell_node_list.size() == 5) {
+                      auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(4));
+                      {   // top tetrahedron
+                        TetrahedronTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                                     xr[cell_node_list[3]], xr[cell_node_list[4]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant() * f(T0(xi));
+                        }
+                      }
+                      {   // bottom tetrahedron
+                        TetrahedronTransformation T1(xr[cell_node_list[3]], xr[cell_node_list[2]],
+                                                     xr[cell_node_list[1]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant() * f(T1(xi));
+                        }
+                      }
+                    } else if (cell_node_list.size() == 6) {
+                      auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(4));
+                      {   // top pyramid
+                        PyramidTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]], xr[cell_node_list[3]],
+                                                 xr[cell_node_list[4]], xr[cell_node_list[5]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant(xi) * f(T0(xi));
+                        }
+                      }
+                      {   // bottom pyramid
+                        PyramidTransformation T1(xr[cell_node_list[4]], xr[cell_node_list[3]], xr[cell_node_list[2]],
+                                                 xr[cell_node_list[1]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant(xi) * f(T1(xi));
+                        }
+                      }
+                    } else {
+                      INFO("Diamond cells with more than 6 vertices are not tested");
+                      REQUIRE(false);
+                    }
+                    break;
+                  }
+                  default: {
+                    INFO("Diamond cells not tested yet");
+                    REQUIRE(cell_type[cell_id] != CellType::Diamond);
+                  }
+                  }
+                  int_f[cell_id] = integral;
+                });
+
+              return int_f;
+            }();
+
+            SECTION("all cells")
+            {
+              SECTION("CellValue")
+              {
+                CellValue<R2> values(mesh->connectivity());
+                CellIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == 0);
+              }
+
+              SECTION("Array")
+              {
+                Array<R2> values(mesh->numberOfCells());
+
+                CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == 0);
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2> values(mesh->numberOfCells());
+                CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == 0);
+              }
+            }
+
+            SECTION("cell list")
+            {
+              SECTION("Array")
+              {
+                Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                Array<R2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == 0);
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                SmallArray<R2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == 0);
+              }
+            }
+          }
+
+          SECTION("tensorial formula")
+          {
+            Array<const R2> int_f_per_cell = [=] {
+              Array<R2> int_f(mesh->numberOfCells());
+              auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+              auto cell_type           = mesh->connectivity().cellType();
+
+              auto qf = QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(4));
+
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                  auto cell_node_list = cell_to_node_matrix[cell_id];
+                  auto xr             = mesh->xr();
+                  R2 integral         = zero;
+
+                  switch (cell_type[cell_id]) {
+                  case CellType::Tetrahedron: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[2]], xr[cell_node_list[3]], xr[cell_node_list[3]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[3]]);
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Pyramid: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[4]],
+                                         xr[cell_node_list[4]], xr[cell_node_list[4]]);
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Prism: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[2]], xr[cell_node_list[3]], xr[cell_node_list[4]],
+                                         xr[cell_node_list[5]], xr[cell_node_list[5]]);
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Hexahedron: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]],
+                                         xr[cell_node_list[6]], xr[cell_node_list[7]]);
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Diamond: {
+                    if (cell_node_list.size() == 5) {
+                      {   // top tetrahedron
+                        CubeTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]], xr[cell_node_list[3]],
+                                              xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[4]],
+                                              xr[cell_node_list[4]], xr[cell_node_list[4]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant(xi) * f(T0(xi));
+                        }
+                      }
+                      {   // bottom tetrahedron
+                        CubeTransformation T1(xr[cell_node_list[3]], xr[cell_node_list[2]], xr[cell_node_list[1]],
+                                              xr[cell_node_list[1]], xr[cell_node_list[0]], xr[cell_node_list[0]],
+                                              xr[cell_node_list[0]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant(xi) * f(T1(xi));
+                        }
+                      }
+                    } else if (cell_node_list.size() == 6) {
+                      {   // top pyramid
+                        CubeTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]], xr[cell_node_list[3]],
+                                              xr[cell_node_list[4]], xr[cell_node_list[5]], xr[cell_node_list[5]],
+                                              xr[cell_node_list[5]], xr[cell_node_list[5]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant(xi) * f(T0(xi));
+                        }
+                      }
+                      {   // bottom pyramid
+                        CubeTransformation T1(xr[cell_node_list[4]], xr[cell_node_list[3]], xr[cell_node_list[2]],
+                                              xr[cell_node_list[1]], xr[cell_node_list[0]], xr[cell_node_list[0]],
+                                              xr[cell_node_list[0]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant(xi) * f(T1(xi));
+                        }
+                      }
+                    } else {
+                      INFO("Diamond cells with more than 6 vertices are not tested");
+                      REQUIRE(false);
+                    }
+                    break;
+                  }
+                  default: {
+                    INFO("Diamond cells not tested yet");
+                    REQUIRE(cell_type[cell_id] != CellType::Diamond);
+                  }
+                  }
+                  int_f[cell_id] = integral;
+                });
+
+              return int_f;
+            }();
+
+            SECTION("all cells")
+            {
+              SECTION("CellValue")
+              {
+                CellValue<R2> values(mesh->connectivity());
+                CellIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                auto cell_type = mesh->connectivity().cellType();
+                double error   = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2> values(mesh->numberOfCells());
+
+                CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2> values(mesh->numberOfCells());
+                CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("cell list")
+            {
+              SECTION("Array")
+              {
+                Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                Array<R2> values = CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                SmallArray<R2> values =
+                  CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("matrix")
+  {
+    using R2x2  = TinyMatrix<2>;
+    auto l2Norm = [](const R2x2& A) -> double {
+      return std::sqrt(A(0, 0) * A(0, 0) + A(1, 0) * A(1, 0) + A(0, 1) * A(0, 1) + A(1, 1) * A(1, 1));
+    };
+
+    SECTION("1D")
+    {
+      using R1 = TinyVector<1>;
+
+      const auto mesh = MeshDataBaseForTests::get().unordered1DMesh();
+      auto f          = [](const R1& x) -> R2x2 { return R2x2{x[0] * x[0] + 1, 2 * x[0], 3 - x[0], 3 * x[0] - 1}; };
+
+      Array<const R2x2> int_f_per_cell = [=] {
+        Array<R2x2> int_f(mesh->numberOfCells());
+        auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+        parallel_for(
+          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+            auto cell_node_list  = cell_to_node_matrix[cell_id];
+            auto xr              = mesh->xr();
+            const double x_left  = xr[cell_node_list[0]][0];
+            const double x_right = xr[cell_node_list[1]][0];
+            int_f[cell_id] = R2x2{1. / 3 * (x_right * x_right * x_right - x_left * x_left * x_left) + x_right - x_left,
+                                  x_right * x_right - x_left * x_left,
+                                  -0.5 * (x_right * x_right - x_left * x_left) + 3 * (x_right - x_left),
+                                  3. / 2 * (x_right * x_right - x_left * x_left) - (x_right - x_left)};
+          });
+
+        return int_f;
+      }();
+
+      SECTION("direct formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<R2x2> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R1 x) { return f(x); }, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2x2> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2x2> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<R2x2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<R2x2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+
+      SECTION("tensorial formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<R2x2> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R1 x) { return f(x); }, GaussLobattoQuadratureDescriptor(2), *mesh,
+                                        values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2x2> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2x2> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<R2x2> values = CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<R2x2> values =
+              CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      using R2 = TinyVector<2>;
+
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+
+      auto f = [](const R2& X) -> R2x2 {
+        const double x = X[0];
+        const double y = X[1];
+        return R2x2{x * x + 2 * x * y + 3 * y * y + 2, 2 * x + 3 * y * y, 2 * x - 3 * y, 3 * x * x - 2 * y};
+      };
+
+      Array<const R2x2> int_f_per_cell = [=] {
+        Array<R2x2> int_f(mesh->numberOfCells());
+        auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+        auto cell_type           = mesh->connectivity().cellType();
+
+        parallel_for(
+          mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+            auto cell_node_list = cell_to_node_matrix[cell_id];
+            auto xr             = mesh->xr();
+            R2x2 integral       = zero;
+
+            switch (cell_type[cell_id]) {
+            case CellType::Triangle: {
+              TriangleTransformation<2> T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]]);
+              auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(4));
+
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.jacobianDeterminant() * f(T(xi));
+              }
+              break;
+            }
+            case CellType::Quadrangle: {
+              SquareTransformation<2> T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                        xr[cell_node_list[3]]);
+              auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(4));
+
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+              }
+              break;
+            }
+            default: {
+              throw UnexpectedError("invalid cell type in 2d");
+            }
+            }
+            int_f[cell_id] = integral;
+          });
+
+        return int_f;
+      }();
+
+      SECTION("direct formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<R2x2> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2x2> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2x2> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<R2x2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<R2x2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+
+      SECTION("tensorial formula")
+      {
+        SECTION("all cells")
+        {
+          SECTION("CellValue")
+          {
+            CellValue<R2x2> values(mesh->connectivity());
+            CellIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussLobattoQuadratureDescriptor(2), *mesh,
+                                        values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2x2> values(mesh->numberOfCells());
+
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2x2> values(mesh->numberOfCells());
+            CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("cell list")
+        {
+          SECTION("Array")
+          {
+            Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            Array<R2x2> values = CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            SmallArray<R2x2> values =
+              CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, cell_list);
+
+            double error = 0;
+            for (size_t i = 0; i < cell_list.size(); ++i) {
+              error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> R2x2 {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return R2x2{x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1, 2 * x * y - 3 * y * z + 2 * x,
+                    x * x - 2 * x * z - 3 * y, 3 * z + x * y};
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            Array<const R2x2> int_f_per_cell = [=] {
+              Array<R2x2> int_f(mesh->numberOfCells());
+              auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+              auto cell_type           = mesh->connectivity().cellType();
+
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                  auto cell_node_list = cell_to_node_matrix[cell_id];
+                  auto xr             = mesh->xr();
+                  R2x2 integral       = zero;
+
+                  switch (cell_type[cell_id]) {
+                  case CellType::Tetrahedron: {
+                    TetrahedronTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                                xr[cell_node_list[3]]);
+                    auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant() * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Pyramid: {
+                    PyramidTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                            xr[cell_node_list[3]], xr[cell_node_list[4]]);
+                    auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Prism: {
+                    PrismTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                          xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]]);
+                    auto qf = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Hexahedron: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]],
+                                         xr[cell_node_list[6]], xr[cell_node_list[7]]);
+                    auto qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(4));
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Diamond: {
+                    if (cell_node_list.size() == 5) {
+                      auto qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(4));
+                      {   // top tetrahedron
+                        TetrahedronTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                                     xr[cell_node_list[3]], xr[cell_node_list[4]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant() * f(T0(xi));
+                        }
+                      }
+                      {   // bottom tetrahedron
+                        TetrahedronTransformation T1(xr[cell_node_list[3]], xr[cell_node_list[2]],
+                                                     xr[cell_node_list[1]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant() * f(T1(xi));
+                        }
+                      }
+                    } else if (cell_node_list.size() == 6) {
+                      auto qf = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(4));
+                      {   // top pyramid
+                        PyramidTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]], xr[cell_node_list[3]],
+                                                 xr[cell_node_list[4]], xr[cell_node_list[5]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant(xi) * f(T0(xi));
+                        }
+                      }
+                      {   // bottom pyramid
+                        PyramidTransformation T1(xr[cell_node_list[4]], xr[cell_node_list[3]], xr[cell_node_list[2]],
+                                                 xr[cell_node_list[1]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant(xi) * f(T1(xi));
+                        }
+                      }
+                    } else {
+                      INFO("Diamond cells with more than 6 vertices are not tested");
+                      REQUIRE(false);
+                    }
+                    break;
+                  }
+                  default: {
+                    INFO("Diamond cells not tested yet");
+                    REQUIRE(cell_type[cell_id] != CellType::Diamond);
+                  }
+                  }
+                  int_f[cell_id] = integral;
+                });
+
+              return int_f;
+            }();
+
+            SECTION("all cells")
+            {
+              SECTION("CellValue")
+              {
+                CellValue<R2x2> values(mesh->connectivity());
+                CellIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == 0);
+              }
+
+              SECTION("Array")
+              {
+                Array<R2x2> values(mesh->numberOfCells());
+
+                CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == 0);
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2x2> values(mesh->numberOfCells());
+                CellIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == 0);
+              }
+            }
+
+            SECTION("cell list")
+            {
+              SECTION("Array")
+              {
+                Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                Array<R2x2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == 0);
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                SmallArray<R2x2> values = CellIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == 0);
+              }
+            }
+          }
+
+          SECTION("tensorial formula")
+          {
+            Array<const R2x2> int_f_per_cell = [=] {
+              Array<R2x2> int_f(mesh->numberOfCells());
+              auto cell_to_node_matrix = mesh->connectivity().cellToNodeMatrix();
+              auto cell_type           = mesh->connectivity().cellType();
+
+              auto qf = QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(4));
+
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                  auto cell_node_list = cell_to_node_matrix[cell_id];
+                  auto xr             = mesh->xr();
+                  R2x2 integral       = zero;
+
+                  switch (cell_type[cell_id]) {
+                  case CellType::Tetrahedron: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[2]], xr[cell_node_list[3]], xr[cell_node_list[3]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[3]]);
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Pyramid: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[4]],
+                                         xr[cell_node_list[4]], xr[cell_node_list[4]]);
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Prism: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[2]], xr[cell_node_list[3]], xr[cell_node_list[4]],
+                                         xr[cell_node_list[5]], xr[cell_node_list[5]]);
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Hexahedron: {
+                    CubeTransformation T(xr[cell_node_list[0]], xr[cell_node_list[1]], xr[cell_node_list[2]],
+                                         xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[5]],
+                                         xr[cell_node_list[6]], xr[cell_node_list[7]]);
+
+                    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                      const auto& xi = qf.point(i);
+                      integral += qf.weight(i) * T.jacobianDeterminant(xi) * f(T(xi));
+                    }
+                    break;
+                  }
+                  case CellType::Diamond: {
+                    if (cell_node_list.size() == 5) {
+                      {   // top tetrahedron
+                        CubeTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]], xr[cell_node_list[3]],
+                                              xr[cell_node_list[3]], xr[cell_node_list[4]], xr[cell_node_list[4]],
+                                              xr[cell_node_list[4]], xr[cell_node_list[4]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant(xi) * f(T0(xi));
+                        }
+                      }
+                      {   // bottom tetrahedron
+                        CubeTransformation T1(xr[cell_node_list[3]], xr[cell_node_list[2]], xr[cell_node_list[1]],
+                                              xr[cell_node_list[1]], xr[cell_node_list[0]], xr[cell_node_list[0]],
+                                              xr[cell_node_list[0]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant(xi) * f(T1(xi));
+                        }
+                      }
+                    } else if (cell_node_list.size() == 6) {
+                      {   // top pyramid
+                        CubeTransformation T0(xr[cell_node_list[1]], xr[cell_node_list[2]], xr[cell_node_list[3]],
+                                              xr[cell_node_list[4]], xr[cell_node_list[5]], xr[cell_node_list[5]],
+                                              xr[cell_node_list[5]], xr[cell_node_list[5]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T0.jacobianDeterminant(xi) * f(T0(xi));
+                        }
+                      }
+                      {   // bottom pyramid
+                        CubeTransformation T1(xr[cell_node_list[4]], xr[cell_node_list[3]], xr[cell_node_list[2]],
+                                              xr[cell_node_list[1]], xr[cell_node_list[0]], xr[cell_node_list[0]],
+                                              xr[cell_node_list[0]], xr[cell_node_list[0]]);
+
+                        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                          const auto& xi = qf.point(i);
+                          integral += qf.weight(i) * T1.jacobianDeterminant(xi) * f(T1(xi));
+                        }
+                      }
+                    } else {
+                      INFO("Diamond cells with more than 6 vertices are not tested");
+                      REQUIRE(false);
+                    }
+                    break;
+                  }
+                  default: {
+                    INFO("Diamond cells not tested yet");
+                    REQUIRE(cell_type[cell_id] != CellType::Diamond);
+                  }
+                  }
+                  int_f[cell_id] = integral;
+                });
+
+              return int_f;
+            }();
+
+            SECTION("all cells")
+            {
+              SECTION("CellValue")
+              {
+                CellValue<R2x2> values(mesh->connectivity());
+                CellIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                auto cell_type = mesh->connectivity().cellType();
+                double error   = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2x2> values(mesh->numberOfCells());
+
+                CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2x2> values(mesh->numberOfCells());
+                CellIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+                  error += l2Norm(int_f_per_cell[cell_id] - values[cell_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("cell list")
+            {
+              SECTION("Array")
+              {
+                Array<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                Array<R2x2> values =
+                  CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<CellId> cell_list{mesh->numberOfCells() / 2 + mesh->numberOfCells() % 2};
+
+                {
+                  size_t k = 0;
+                  for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++(++cell_id), ++k) {
+                    cell_list[k] = cell_id;
+                  }
+
+                  REQUIRE(k == cell_list.size());
+                }
+
+                SmallArray<R2x2> values =
+                  CellIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, cell_list);
+
+                double error = 0;
+                for (size_t i = 0; i < cell_list.size(); ++i) {
+                  error += l2Norm(int_f_per_cell[cell_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_CellType.cpp b/tests/test_CellType.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..17696985874a0a8da3a939a65028cd5af2c9ecbe
--- /dev/null
+++ b/tests/test_CellType.cpp
@@ -0,0 +1,26 @@
+#include <catch2/catch_test_macros.hpp>
+
+#include <mesh/CellType.hpp>
+
+#include <limits>
+#include <type_traits>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("CellType", "[mesh]")
+{
+  REQUIRE(name(CellType::Line) == "line");
+
+  REQUIRE(name(CellType::Triangle) == "triangle");
+  REQUIRE(name(CellType::Quadrangle) == "quadrangle");
+  REQUIRE(name(CellType::Polygon) == "polygon");
+
+  REQUIRE(name(CellType::Diamond) == "diamond");
+  REQUIRE(name(CellType::Hexahedron) == "hexahedron");
+  REQUIRE(name(CellType::Prism) == "prism");
+  REQUIRE(name(CellType::Pyramid) == "pyramid");
+  REQUIRE(name(CellType::Tetrahedron) == "tetrahedron");
+
+  REQUIRE(name(static_cast<CellType>(std::numeric_limits<std::underlying_type_t<CellType>>::max())) ==
+          "unknown cell type");
+}
diff --git a/tests/test_CubeGaussQuadrature.cpp b/tests/test_CubeGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d4221fe322dc6dc4f898ac2563dc14d463b2e71f
--- /dev/null
+++ b/tests/test_CubeGaussQuadrature.cpp
@@ -0,0 +1,513 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <algebra/TinyMatrix.hpp>
+
+#include <analysis/CubeGaussQuadrature.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <utils/Exceptions.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("CubeGaussQuadrature", "[analysis]")
+{
+  auto integrate = [](auto f, auto quadrature_formula) {
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(point_list[0]);
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(point_list[i]);
+    }
+
+    return value;
+  };
+
+  auto integrate_on_brick = [](auto f, auto quadrature_formula, const std::array<TinyVector<3>, 4>& tetrahedron) {
+    const auto& A = tetrahedron[0];
+    const auto& B = tetrahedron[1];
+    const auto& C = tetrahedron[2];
+    const auto& D = tetrahedron[3];
+
+    TinyMatrix<3> J;
+    for (size_t i = 0; i < 3; ++i) {
+      J(i, 0) = 0.5 * (B[i] - A[i]);
+      J(i, 1) = 0.5 * (C[i] - A[i]);
+      J(i, 2) = 0.5 * (D[i] - A[i]);
+    }
+    TinyVector s = 0.5 * (B + C + D - A);
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(J * (point_list[0]) + s);
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(J * (point_list[i]) + s);
+    }
+
+    return det(J) * value;
+  };
+
+  auto get_order = [&integrate, &integrate_on_brick](auto f, auto quadrature_formula, const double exact_value) {
+    using R3 = TinyVector<3>;
+
+    const double int_K_hat = integrate(f, quadrature_formula);
+    const double int_refined   //
+      = integrate_on_brick(f, quadrature_formula, {R3{-1, -1, -1}, R3{+0, -1, -1}, R3{-1, +0, -1}, R3{-1, -1, +0}}) +
+        integrate_on_brick(f, quadrature_formula, {R3{+0, -1, -1}, R3{+1, -1, -1}, R3{+0, +0, -1}, R3{+0, -1, +0}}) +
+        integrate_on_brick(f, quadrature_formula, {R3{-1, +0, -1}, R3{+0, +0, -1}, R3{-1, +1, -1}, R3{-1, +0, +0}}) +
+        integrate_on_brick(f, quadrature_formula, {R3{+0, +0, -1}, R3{+1, +0, -1}, R3{+0, +1, -1}, R3{+0, +0, +0}}) +
+        integrate_on_brick(f, quadrature_formula, {R3{-1, -1, +0}, R3{+0, -1, +0}, R3{-1, +0, +0}, R3{-1, -1, +1}}) +
+        integrate_on_brick(f, quadrature_formula, {R3{+0, -1, +0}, R3{+1, -1, +0}, R3{+0, +0, +0}, R3{+0, -1, +1}}) +
+        integrate_on_brick(f, quadrature_formula, {R3{-1, +0, +0}, R3{+0, +0, +0}, R3{-1, +1, +0}, R3{-1, +0, +1}}) +
+        integrate_on_brick(f, quadrature_formula, {R3{+0, +0, +0}, R3{+1, +0, +0}, R3{+0, +1, +0}, R3{+0, +0, +1}});
+
+    return -std::log((int_refined - exact_value) / (int_K_hat - exact_value)) / std::log(2);
+  };
+
+  auto p0 = [](const TinyVector<3>&) { return 4; };
+  auto p1 = [](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return 2 * x + 3 * y + z - 1;
+  };
+  auto p2 = [&p1](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p1(X) * (2.5 * x - 3 * y + z + 3);
+  };
+  auto p3 = [&p2](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p2(X) * (3 * x + 2 * y - 3 * z - 1);
+  };
+  auto p4 = [&p3](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p3(X) * (2 * x - 0.5 * y - 1.3 * z + 1);
+  };
+  auto p5 = [&p4](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p4(X) * (-0.1 * x + 1.3 * y - 3 * z + 1);
+  };
+  auto p6 = [&p5](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p5(X) * 7875. / 143443 * (2 * x - y + 4 * z + 1);
+  };
+  auto p7 = [&p6](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p6(X) * (0.7 * x - 2.7 * y + 1.3 * z - 2);
+  };
+  auto p8 = [&p7](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p7(X) * (0.3 * x + 1.2 * y - 0.7 * z + 0.2);
+  };
+  auto p9 = [&p8](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p8(X) * (-0.2 * x - 1.7 * y + 0.4 * z - 0.4);
+  };
+  auto p10 = [&p9](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p9(X) * (0.8 * x + 0.1 * y - 0.7 * z + 0.2);
+  };
+  auto p11 = [&p10](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p10(X) * (-0.6 * x - 0.5 * y + 0.3 * z - 0.1);
+  };
+  auto p12 = [&p11](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p11(X) * (0.4 * x - 0.7 * y - 0.6 * z + 0.7);
+  };
+  auto p13 = [&p12](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p12(X) * (-0.9 * x + 0.3 * y + 0.3 * z - 0.3);
+  };
+  auto p14 = [&p13](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p13(X) * (0.2 * x - 0.7 * y + 0.6 * z + 0.1);
+  };
+  auto p15 = [&p14](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p14(X) * (-0.5 * x - 0.3 * y + 0.7 * z - 0.4);
+  };
+  auto p16 = [&p15](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p15(X) * (0.7 * x + 0.6 * y - 0.1 * z + 0.6);
+  };
+  auto p17 = [&p16](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p16(X) * (-0.6 * x + 0.3 * y + 0.7 * z + 0.8);
+  };
+  auto p18 = [&p17](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p17(X) * (0.1 * x + 0.9 * y - 0.4 * z - 0.3);
+  };
+  auto p19 = [&p18](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p18(X) * (-0.8 * x - 0.3 * y + 0.9 * z + 0.8);
+  };
+  auto p20 = [&p19](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p19(X) * (0.3 * x - 0.7 * y - 0.8 * z + 0.7);
+  };
+  auto p21 = [&p20](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p20(X) * (-0.9 * x + 0.2 * y + 0.5 * z - 0.6);
+  };
+  auto p22 = [&p21](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p21(X) * (0.3 * x - 0.6 * y - 0.7 * z + 0.2);
+  };
+
+  SECTION("degree 0 and 1")
+  {
+    const QuadratureFormula<3>& l1 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(1));
+
+    REQUIRE(l1.numberOfPoints() == 1);
+
+    REQUIRE(integrate(p0, l1) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l1) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l1) != Catch::Approx(-32));
+
+    REQUIRE(get_order(p2, l1, -32) == Catch::Approx(2));
+  }
+
+  SECTION("degree 2 and 3")
+  {
+    const QuadratureFormula<3>& l2 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(2));
+    const QuadratureFormula<3>& l3 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(3));
+
+    REQUIRE(&l2 == &l3);
+
+    REQUIRE(l3.numberOfPoints() == 6);
+
+    REQUIRE(integrate(p0, l3) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l3) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l3) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l3) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l3) != Catch::Approx(868. / 75));
+
+    REQUIRE(get_order(p4, l3, 868. / 75) == Catch::Approx(4));
+  }
+
+  SECTION("degree 4 and 5")
+  {
+    const QuadratureFormula<3>& l4 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(4));
+    const QuadratureFormula<3>& l5 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(5));
+
+    REQUIRE(&l4 == &l5);
+
+    REQUIRE(l5.numberOfPoints() == 14);
+
+    REQUIRE(integrate(p0, l5) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l5) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l5) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l5) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l5) == Catch::Approx(868. / 75));
+    REQUIRE(integrate(p5, l5) == Catch::Approx(11176. / 225));
+    REQUIRE(integrate(p6, l5) != Catch::Approx(-34781. / 430329));
+
+    REQUIRE(get_order(p6, l5, -34781. / 430329) == Catch::Approx(6));
+  }
+
+  SECTION("degree 6 and 7")
+  {
+    const QuadratureFormula<3>& l6 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(6));
+    const QuadratureFormula<3>& l7 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(7));
+
+    REQUIRE(&l6 == &l7);
+
+    REQUIRE(l7.numberOfPoints() == 34);
+
+    REQUIRE(integrate(p0, l7) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l7) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l7) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l7) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l7) == Catch::Approx(868. / 75));
+    REQUIRE(integrate(p5, l7) == Catch::Approx(11176. / 225));
+    REQUIRE(integrate(p6, l7) == Catch::Approx(-34781. / 430329));
+    REQUIRE(integrate(p7, l7) == Catch::Approx(-37338109. / 4303290));
+    REQUIRE(integrate(p8, l7) != Catch::Approx(422437099. / 21516450));
+
+    REQUIRE(get_order(p8, l7, 422437099. / 21516450) == Catch::Approx(8));
+  }
+
+  SECTION("degree 8 and 9")
+  {
+    const QuadratureFormula<3>& l8 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(8));
+    const QuadratureFormula<3>& l9 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(9));
+
+    REQUIRE(&l8 == &l9);
+
+    REQUIRE(l9.numberOfPoints() == 58);
+
+    REQUIRE(integrate(p0, l9) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l9) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l9) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l9) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l9) == Catch::Approx(868. / 75));
+    REQUIRE(integrate(p5, l9) == Catch::Approx(11176. / 225));
+    REQUIRE(integrate(p6, l9) == Catch::Approx(-34781. / 430329));
+    REQUIRE(integrate(p7, l9) == Catch::Approx(-37338109. / 4303290));
+    REQUIRE(integrate(p8, l9) == Catch::Approx(422437099. / 21516450));
+    REQUIRE(integrate(p9, l9) == Catch::Approx(-7745999747. / 358607500));
+    REQUIRE(integrate(p10, l9) != Catch::Approx(-564286973089. / 14792559375));
+
+    REQUIRE(get_order(p10, l9, -564286973089. / 14792559375) == Catch::Approx(10));
+  }
+
+  SECTION("degree 10 and 11")
+  {
+    const QuadratureFormula<3>& l10 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(10));
+    const QuadratureFormula<3>& l11 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(11));
+
+    REQUIRE(&l10 == &l11);
+
+    REQUIRE(l11.numberOfPoints() == 90);
+
+    REQUIRE(integrate(p0, l11) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l11) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l11) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l11) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l11) == Catch::Approx(868. / 75));
+    REQUIRE(integrate(p5, l11) == Catch::Approx(11176. / 225));
+    REQUIRE(integrate(p6, l11) == Catch::Approx(-34781. / 430329));
+    REQUIRE(integrate(p7, l11) == Catch::Approx(-37338109. / 4303290));
+    REQUIRE(integrate(p8, l11) == Catch::Approx(422437099. / 21516450));
+    REQUIRE(integrate(p9, l11) == Catch::Approx(-7745999747. / 358607500));
+    REQUIRE(integrate(p10, l11) == Catch::Approx(-564286973089. / 14792559375));
+    REQUIRE(integrate(p11, l11) == Catch::Approx(5047102242313. / 59170237500));
+    REQUIRE(integrate(p12, l11) != Catch::Approx(41226980237154884. / 504796088671875));
+
+    REQUIRE(get_order(p12, l11, 41226980237154884. / 504796088671875) == Catch::Approx(12));
+  }
+
+  SECTION("degree 12 and 13")
+  {
+    const QuadratureFormula<3>& l12 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(12));
+    const QuadratureFormula<3>& l13 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(13));
+
+    REQUIRE(&l12 == &l13);
+
+    REQUIRE(l13.numberOfPoints() == 154);
+
+    REQUIRE(integrate(p0, l13) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l13) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l13) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l13) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l13) == Catch::Approx(868. / 75));
+    REQUIRE(integrate(p5, l13) == Catch::Approx(11176. / 225));
+    REQUIRE(integrate(p6, l13) == Catch::Approx(-34781. / 430329));
+    REQUIRE(integrate(p7, l13) == Catch::Approx(-37338109. / 4303290));
+    REQUIRE(integrate(p8, l13) == Catch::Approx(422437099. / 21516450));
+    REQUIRE(integrate(p9, l13) == Catch::Approx(-7745999747. / 358607500));
+    REQUIRE(integrate(p10, l13) == Catch::Approx(-564286973089. / 14792559375));
+    REQUIRE(integrate(p11, l13) == Catch::Approx(5047102242313. / 59170237500));
+    REQUIRE(integrate(p12, l13) == Catch::Approx(41226980237154884. / 504796088671875));
+    REQUIRE(integrate(p13, l13) == Catch::Approx(-2061220959094693133. / 26922458062500000));
+    REQUIRE(integrate(p14, l13) != Catch::Approx(9658346476244058223. / 134612290312500000));
+
+    REQUIRE(get_order(p14, l13, 9658346476244058223. / 134612290312500000) == Catch::Approx(14));
+  }
+
+  SECTION("degree 14 and 15")
+  {
+    const QuadratureFormula<3>& l14 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(14));
+    const QuadratureFormula<3>& l15 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(15));
+
+    REQUIRE(&l14 == &l15);
+
+    REQUIRE(l15.numberOfPoints() == 256);
+
+    REQUIRE(integrate(p0, l15) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l15) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l15) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l15) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l15) == Catch::Approx(868. / 75));
+    REQUIRE(integrate(p5, l15) == Catch::Approx(11176. / 225));
+    REQUIRE(integrate(p6, l15) == Catch::Approx(-34781. / 430329));
+    REQUIRE(integrate(p7, l15) == Catch::Approx(-37338109. / 4303290));
+    REQUIRE(integrate(p8, l15) == Catch::Approx(422437099. / 21516450));
+    REQUIRE(integrate(p9, l15) == Catch::Approx(-7745999747. / 358607500));
+    REQUIRE(integrate(p10, l15) == Catch::Approx(-564286973089. / 14792559375));
+    REQUIRE(integrate(p11, l15) == Catch::Approx(5047102242313. / 59170237500));
+    REQUIRE(integrate(p12, l15) == Catch::Approx(41226980237154884. / 504796088671875));
+    REQUIRE(integrate(p13, l15) == Catch::Approx(-2061220959094693133. / 26922458062500000));
+    REQUIRE(integrate(p14, l15) == Catch::Approx(9658346476244058223. / 134612290312500000));
+    REQUIRE(integrate(p15, l15) == Catch::Approx(-74949066496612419191. / 673061451562500000.));
+    REQUIRE(integrate(p16, l15) != Catch::Approx(-46230170725718969828017. / 228840893531250000000.));
+
+    REQUIRE(get_order(p16, l15, -46230170725718969828017. / 228840893531250000000.) == Catch::Approx(16));
+  }
+
+  SECTION("degree 16 and 17")
+  {
+    const QuadratureFormula<3>& l16 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(16));
+    const QuadratureFormula<3>& l17 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(17));
+
+    REQUIRE(&l16 == &l17);
+
+    REQUIRE(l17.numberOfPoints() == 346);
+
+    REQUIRE(integrate(p0, l17) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l17) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l17) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l17) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l17) == Catch::Approx(868. / 75));
+    REQUIRE(integrate(p5, l17) == Catch::Approx(11176. / 225));
+    REQUIRE(integrate(p6, l17) == Catch::Approx(-34781. / 430329));
+    REQUIRE(integrate(p7, l17) == Catch::Approx(-37338109. / 4303290));
+    REQUIRE(integrate(p8, l17) == Catch::Approx(422437099. / 21516450));
+    REQUIRE(integrate(p9, l17) == Catch::Approx(-7745999747. / 358607500));
+    REQUIRE(integrate(p10, l17) == Catch::Approx(-564286973089. / 14792559375));
+    REQUIRE(integrate(p11, l17) == Catch::Approx(5047102242313. / 59170237500));
+    REQUIRE(integrate(p12, l17) == Catch::Approx(41226980237154884. / 504796088671875));
+    REQUIRE(integrate(p13, l17) == Catch::Approx(-2061220959094693133. / 26922458062500000));
+    REQUIRE(integrate(p14, l17) == Catch::Approx(9658346476244058223. / 134612290312500000));
+    REQUIRE(integrate(p15, l17) == Catch::Approx(-74949066496612419191. / 673061451562500000.));
+    REQUIRE(integrate(p16, l17) == Catch::Approx(-46230170725718969828017. / 228840893531250000000.));
+    REQUIRE(integrate(p17, l17) == Catch::Approx(2946902633453348474221. / 190700744609375000000.));
+    REQUIRE(integrate(p18, l17) != Catch::Approx(904723313909284441962799. / 47555998186962890625000.));
+
+    REQUIRE(get_order(p18, l17, 904723313909284441962799. / 47555998186962890625000.) == Catch::Approx(18));
+  }
+
+  SECTION("degree 18 and 19")
+  {
+    const QuadratureFormula<3>& l18 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(18));
+    const QuadratureFormula<3>& l19 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(19));
+
+    REQUIRE(&l18 == &l19);
+
+    REQUIRE(l19.numberOfPoints() == 454);
+
+    REQUIRE(integrate(p0, l19) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l19) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l19) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l19) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l19) == Catch::Approx(868. / 75));
+    REQUIRE(integrate(p5, l19) == Catch::Approx(11176. / 225));
+    REQUIRE(integrate(p6, l19) == Catch::Approx(-34781. / 430329));
+    REQUIRE(integrate(p7, l19) == Catch::Approx(-37338109. / 4303290));
+    REQUIRE(integrate(p8, l19) == Catch::Approx(422437099. / 21516450));
+    REQUIRE(integrate(p9, l19) == Catch::Approx(-7745999747. / 358607500));
+    REQUIRE(integrate(p10, l19) == Catch::Approx(-564286973089. / 14792559375));
+    REQUIRE(integrate(p11, l19) == Catch::Approx(5047102242313. / 59170237500));
+    REQUIRE(integrate(p12, l19) == Catch::Approx(41226980237154884. / 504796088671875));
+    REQUIRE(integrate(p13, l19) == Catch::Approx(-2061220959094693133. / 26922458062500000));
+    REQUIRE(integrate(p14, l19) == Catch::Approx(9658346476244058223. / 134612290312500000));
+    REQUIRE(integrate(p15, l19) == Catch::Approx(-74949066496612419191. / 673061451562500000.));
+    REQUIRE(integrate(p16, l19) == Catch::Approx(-46230170725718969828017. / 228840893531250000000.));
+    REQUIRE(integrate(p17, l19) == Catch::Approx(2946902633453348474221. / 190700744609375000000.));
+    REQUIRE(integrate(p18, l19) == Catch::Approx(904723313909284441962799. / 47555998186962890625000.));
+    REQUIRE(integrate(p19, l19) == Catch::Approx(-91477977618647751170958517. / 22826879129742187500000000.));
+    REQUIRE(integrate(p20, l19) != Catch::Approx(-11898262429946164522483495921. / 836985568090546875000000000.));
+
+    REQUIRE(get_order(p20, l19, -11898262429946164522483495921. / 836985568090546875000000000.) == Catch::Approx(20));
+  }
+
+  SECTION("degree 20 and 21")
+  {
+    const QuadratureFormula<3>& l20 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(20));
+    const QuadratureFormula<3>& l21 = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(21));
+
+    REQUIRE(&l20 == &l21);
+
+    REQUIRE(l21.numberOfPoints() == 580);
+
+    REQUIRE(integrate(p0, l21) == Catch::Approx(32));
+    REQUIRE(integrate(p1, l21) == Catch::Approx(-8));
+    REQUIRE(integrate(p2, l21) == Catch::Approx(-32));
+    REQUIRE(integrate(p3, l21) == Catch::Approx(108));
+    REQUIRE(integrate(p4, l21) == Catch::Approx(868. / 75));
+    REQUIRE(integrate(p5, l21) == Catch::Approx(11176. / 225));
+    REQUIRE(integrate(p6, l21) == Catch::Approx(-34781. / 430329));
+    REQUIRE(integrate(p7, l21) == Catch::Approx(-37338109. / 4303290));
+    REQUIRE(integrate(p8, l21) == Catch::Approx(422437099. / 21516450));
+    REQUIRE(integrate(p9, l21) == Catch::Approx(-7745999747. / 358607500));
+    REQUIRE(integrate(p10, l21) == Catch::Approx(-564286973089. / 14792559375));
+    REQUIRE(integrate(p11, l21) == Catch::Approx(5047102242313. / 59170237500));
+    REQUIRE(integrate(p12, l21) == Catch::Approx(41226980237154884. / 504796088671875));
+    REQUIRE(integrate(p13, l21) == Catch::Approx(-2061220959094693133. / 26922458062500000));
+    REQUIRE(integrate(p14, l21) == Catch::Approx(9658346476244058223. / 134612290312500000));
+    REQUIRE(integrate(p15, l21) == Catch::Approx(-74949066496612419191. / 673061451562500000.));
+    REQUIRE(integrate(p16, l21) == Catch::Approx(-46230170725718969828017. / 228840893531250000000.));
+    REQUIRE(integrate(p17, l21) == Catch::Approx(2946902633453348474221. / 190700744609375000000.));
+    REQUIRE(integrate(p18, l21) == Catch::Approx(904723313909284441962799. / 47555998186962890625000.));
+    REQUIRE(integrate(p19, l21) == Catch::Approx(-91477977618647751170958517. / 22826879129742187500000000.));
+    REQUIRE(integrate(p20, l21) == Catch::Approx(-11898262429946164522483495921. / 836985568090546875000000000.));
+    REQUIRE(integrate(p21, l21) == Catch::Approx(7694483338683700814691225463. / 224192562881396484375000000.));
+    REQUIRE(integrate(p22, l21) != Catch::Approx(10761048146311587678467825954981. / 481266701652064453125000000000.));
+
+    REQUIRE(get_order(p22, l21, 10761048146311587678467825954981. / 481266701652064453125000000000.) ==
+            Catch::Approx(22));
+  }
+
+  SECTION("max implemented degree")
+  {
+    REQUIRE(QuadratureManager::instance().maxCubeDegree(QuadratureType::Gauss) == CubeGaussQuadrature::max_degree);
+    REQUIRE_THROWS_WITH(QuadratureManager::instance().getCubeFormula(
+                          GaussQuadratureDescriptor(CubeGaussQuadrature ::max_degree + 1)),
+                        "error: Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(CubeGaussQuadrature ::max_degree) + " on cubes");
+  }
+
+  SECTION("Access functions")
+  {
+    const QuadratureFormula<3>& quadrature_formula =
+      QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(7));
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    REQUIRE(point_list.size() == quadrature_formula.numberOfPoints());
+    REQUIRE(weight_list.size() == quadrature_formula.numberOfPoints());
+
+    for (size_t i = 0; i < quadrature_formula.numberOfPoints(); ++i) {
+      REQUIRE(&point_list[i] == &quadrature_formula.point(i));
+      REQUIRE(&weight_list[i] == &quadrature_formula.weight(i));
+    }
+  }
+}
diff --git a/tests/test_CubeTransformation.cpp b/tests/test_CubeTransformation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..346dd3c4e9b1f3412bb53503c6880ea07a1508e6
--- /dev/null
+++ b/tests/test_CubeTransformation.cpp
@@ -0,0 +1,298 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/CubeTransformation.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("CubeTransformation", "[geometry]")
+{
+  using R3 = TinyVector<3>;
+
+  SECTION("invertible transformation")
+  {
+    const R3 a_hat{-1, -1, -1};
+    const R3 b_hat{+1, -1, -1};
+    const R3 c_hat{+1, +1, -1};
+    const R3 d_hat{-1, +1, -1};
+    const R3 e_hat{-1, -1, +1};
+    const R3 f_hat{+1, -1, +1};
+    const R3 g_hat{+1, +1, +1};
+    const R3 h_hat{-1, +1, +1};
+
+    const R3 m_hat = zero;
+
+    const R3 a{0, 0, 0};
+    const R3 b{3, 1, 3};
+    const R3 c{2, 5, 2};
+    const R3 d{0, 3, 1};
+    const R3 e{1, 2, 5};
+    const R3 f{3, 1, 7};
+    const R3 g{2, 5, 5};
+    const R3 h{0, 3, 6};
+
+    const R3 m = 0.125 * (a + b + c + d + e + f + g + h);
+
+    const CubeTransformation t(a, b, c, d, e, f, g, h);
+
+    SECTION("values")
+    {
+      REQUIRE(l2Norm(t(a_hat) - a) == Catch::Approx(0));
+      REQUIRE(l2Norm(t(b_hat) - b) == Catch::Approx(0));
+      REQUIRE(l2Norm(t(c_hat) - c) == Catch::Approx(0));
+      REQUIRE(l2Norm(t(d_hat) - d) == Catch::Approx(0));
+      REQUIRE(l2Norm(t(e_hat) - e) == Catch::Approx(0));
+      REQUIRE(l2Norm(t(f_hat) - f) == Catch::Approx(0));
+      REQUIRE(l2Norm(t(g_hat) - g) == Catch::Approx(0));
+      REQUIRE(l2Norm(t(h_hat) - h) == Catch::Approx(0));
+
+      REQUIRE(l2Norm(t(m_hat) - m) == Catch::Approx(0));
+    }
+
+    SECTION("Jacobian determinant")
+    {
+      SECTION("at points")
+      {
+        auto detJ = [](const R3& X) {
+          const double x = X[0];
+          const double y = X[1];
+          const double z = X[2];
+
+          return -(3 * x * y * y * z + 9 * y * y * z - 6 * x * x * y * z + 6 * x * y * z - 96 * y * z + 14 * x * x * z -
+                   49 * x * z + 119 * z - 21 * x * y * y + 21 * y * y + 90 * x * y - 10 * y + 40 * x * x - 109 * x -
+                   491) /
+                 128;
+        };
+
+        REQUIRE(t.jacobianDeterminant(a_hat) == Catch::Approx(detJ(a_hat)));
+        REQUIRE(t.jacobianDeterminant(b_hat) == Catch::Approx(detJ(b_hat)));
+        REQUIRE(t.jacobianDeterminant(c_hat) == Catch::Approx(detJ(c_hat)));
+        REQUIRE(t.jacobianDeterminant(d_hat) == Catch::Approx(detJ(d_hat)));
+        REQUIRE(t.jacobianDeterminant(e_hat) == Catch::Approx(detJ(e_hat)));
+        REQUIRE(t.jacobianDeterminant(f_hat) == Catch::Approx(detJ(f_hat)));
+        REQUIRE(t.jacobianDeterminant(g_hat) == Catch::Approx(detJ(g_hat)));
+        REQUIRE(t.jacobianDeterminant(h_hat) == Catch::Approx(detJ(h_hat)));
+
+        REQUIRE(t.jacobianDeterminant(m_hat) == Catch::Approx(detJ(m_hat)));
+      }
+
+      SECTION("Gauss order 3")
+      {
+        // The jacobian determinant is of maximal degree 2 in each variable
+        const QuadratureFormula<3>& gauss =
+          QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(2));
+
+        double volume = 0;
+        for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+          volume += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i));
+        }
+
+        // 353. / 12 is actually the volume of the hexahedron
+        REQUIRE(volume == Catch::Approx(353. / 12));
+      }
+
+      auto p = [](const R3& X) {
+        const double& x = X[0];
+        const double& y = X[1];
+        const double& z = X[2];
+
+        return 2 * x * x + 3 * y * z + z * z + 2 * x - 3 * y + 0.5 * z + 3;
+      };
+
+      SECTION("Gauss order 6")
+      {
+        // Jacbian determinant is a degree 2 polynomial, so the
+        // following formula is required to reach exactness
+        const QuadratureFormula<3>& gauss = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(6));
+
+        double integral = 0;
+        for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+          integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+        }
+
+        REQUIRE(integral == Catch::Approx(488879. / 360));
+      }
+
+      SECTION("Gauss-Legendre order 4")
+      {
+        // Jacbian determinant is a degree 2 polynomial, so the
+        // following formula is required to reach exactness
+        const QuadratureFormula<3>& gauss =
+          QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(4));
+
+        double integral = 0;
+        for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+          integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+        }
+
+        REQUIRE(integral == Catch::Approx(488879. / 360));
+      }
+
+      SECTION("Gauss-Lobatto order 4")
+      {
+        // Jacbian determinant is a degree 2 polynomial, so the
+        // following formula is required to reach exactness
+        const QuadratureFormula<3>& gauss =
+          QuadratureManager::instance().getCubeFormula(GaussLobattoQuadratureDescriptor(4));
+
+        double integral = 0;
+        for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+          integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+        }
+
+        REQUIRE(integral == Catch::Approx(488879. / 360));
+      }
+    }
+  }
+
+  SECTION("degenerate to tetrahedron")
+  {
+    using R3 = TinyVector<3>;
+
+    const R3 a{1, 2, 1};
+    const R3 b{3, 1, 3};
+    const R3 c{2, 5, 2};
+    const R3 d{2, 3, 4};
+
+    const CubeTransformation t(a, b, c, c, d, d, d, d);
+
+    auto p = [](const R3& X) {
+      const double x = X[0];
+      const double y = X[1];
+      const double z = X[2];
+      return 2 * x * x + 3 * x * y + y * y + 3 * y - z * z + 2 * x * z + 2 * z;
+    };
+
+    SECTION("Polynomial Gauss integral")
+    {
+      QuadratureFormula<3> qf = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(6));
+
+      double sum = 0;
+      for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+        const R3 xi = qf.point(i);
+        sum += qf.weight(i) * t.jacobianDeterminant(xi) * p(t(xi));
+      }
+
+      REQUIRE(sum == Catch::Approx(231. / 2));
+    }
+
+    SECTION("Polynomial Gauss-Lobatto integral")
+    {
+      QuadratureFormula<3> qf = QuadratureManager::instance().getCubeFormula(GaussLobattoQuadratureDescriptor(4));
+
+      double sum = 0;
+      for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+        const R3 xi = qf.point(i);
+        sum += qf.weight(i) * t.jacobianDeterminant(xi) * p(t(xi));
+      }
+
+      REQUIRE(sum == Catch::Approx(231. / 2));
+    }
+
+    SECTION("Polynomial Gauss-Legendre integral")
+    {
+      QuadratureFormula<3> qf = QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(4));
+
+      double sum = 0;
+      for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+        const R3 xi = qf.point(i);
+        sum += qf.weight(i) * t.jacobianDeterminant(xi) * p(t(xi));
+      }
+
+      REQUIRE(sum == Catch::Approx(231. / 2));
+    }
+  }
+
+  SECTION("degenerate to prism")
+  {
+    const R3 a{1, 2, 0};
+    const R3 b{3, 1, 3};
+    const R3 c{2, 5, 2};
+    const R3 d{0, 3, 1};
+    const R3 e{1, 2, 5};
+    const R3 f{3, 1, 7};
+
+    const CubeTransformation t(a, b, c, c, d, e, f, f);
+
+    auto p = [](const R3& X) {
+      const double x = X[0];
+      const double y = X[1];
+      const double z = X[2];
+
+      return 3 * x * x + 2 * y * y + 3 * z * z + 4 * x + 3 * y + 2 * z + 1;
+    };
+
+    SECTION("Polynomial Gauss integral")
+    {
+      // 8 is the minimum quadrature rule to integrate the polynomial
+      // on the prism due to the jacobian determinant expression
+      const QuadratureFormula<3>& gauss = QuadratureManager::instance().getCubeFormula(GaussQuadratureDescriptor(8));
+
+      double integral = 0;
+      for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+        integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+      }
+
+      REQUIRE(integral == Catch::Approx(30377. / 90));
+    }
+
+    SECTION("Polynomial Gauss-Legendre integral")
+    {
+      const QuadratureFormula<3>& gauss =
+        QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(4));
+
+      double integral = 0;
+      for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+        integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+      }
+
+      REQUIRE(integral == Catch::Approx(30377. / 90));
+    }
+
+    SECTION("Polynomial Gauss-Lobatto integral")
+    {
+      const QuadratureFormula<3>& gauss =
+        QuadratureManager::instance().getCubeFormula(GaussLobattoQuadratureDescriptor(4));
+
+      double integral = 0;
+      for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+        integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+      }
+
+      REQUIRE(integral == Catch::Approx(30377. / 90));
+    }
+  }
+
+  SECTION("degenerate to pyramid")
+  {
+    const R3 a{1, 2, 0};
+    const R3 b{3, 1, 3};
+    const R3 c{2, 5, 2};
+    const R3 d{0, 3, 1};
+    const R3 e{1, 2, 5};
+
+    const CubeTransformation t(a, b, c, d, e, e, e, e);
+
+    auto p = [](const R3& X) {
+      const double x = X[0];
+      const double y = X[1];
+      const double z = X[2];
+
+      return 3 * x * x + 2 * y * y + 3 * z * z + 4 * x + 3 * y + 2 * z + 1;
+    };
+
+    // 4 is the minimum quadrature rule to integrate the polynomial on the pyramid
+    const QuadratureFormula<3>& gauss =
+      QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(20));
+    double integral = 0;
+    for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+      integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+    }
+
+    REQUIRE(integral == Catch::Approx(7238. / 15));
+  }
+}
diff --git a/tests/test_DataVariant.cpp b/tests/test_DataVariant.cpp
index 0db6af9b2fbe55081a8dc43d359a189cae4f5d5a..dab7d6436cc547b7163baccd369068f20103f7c5 100644
--- a/tests/test_DataVariant.cpp
+++ b/tests/test_DataVariant.cpp
@@ -11,11 +11,12 @@ TEST_CASE("DataVariant", "[language]")
 {
   SECTION("AggregateDataVariant")
   {
-    AggregateDataVariant aggregate{std::vector<DataVariant>{double{1.3}, int64_t{-3}, std::vector<double>{1, 2.7}}};
+    AggregateDataVariant aggregate{
+      std::vector<DataVariant>{double{1.3}, int64_t{-3}, std::vector<double>{1, 2.7}, bool{true}}};
 
     SECTION("size")
     {
-      REQUIRE(aggregate.size() == 3);
+      REQUIRE(aggregate.size() == 4);
     }
 
     SECTION("output")
@@ -24,7 +25,8 @@ TEST_CASE("DataVariant", "[language]")
       aggregate_output << aggregate;
 
       std::stringstream expected_output;
-      expected_output << '(' << double{1.3} << ", " << int64_t{-3} << ", (" << 1 << ", " << 2.7 << "))";
+      expected_output << '(' << double{1.3} << ", " << int64_t{-3} << ", (" << 1 << ", " << 2.7 << "), "
+                      << std::boolalpha << true << ")";
       REQUIRE(aggregate_output.str() == expected_output.str());
     }
 
diff --git a/tests/test_DiamondDualConnectivityBuilder.cpp b/tests/test_DiamondDualConnectivityBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..70b8f75440061530f386654a4bbf9e3f5342d33e
--- /dev/null
+++ b/tests/test_DiamondDualConnectivityBuilder.cpp
@@ -0,0 +1,345 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+
+template <ItemType item_type, typename ConnectivityType>
+inline auto
+get_item_ref_ids(const ConnectivityType& connectivity)
+{
+  std::map<std::string, size_t> ref_id_set;
+  for (size_t i = 0; i < connectivity.template numberOfRefItemList<item_type>(); ++i) {
+    const auto& ref_id_list = connectivity.template refItemList<item_type>(i);
+    std::ostringstream os;
+    os << ref_id_list.refId();
+    ItemValue<size_t, item_type> item_tag{connectivity};
+    item_tag.fill(0);
+    for (size_t i_item = 0; i_item < ref_id_list.list().size(); ++i_item) {
+      item_tag[ref_id_list.list()[i_item]] = 1;
+    }
+
+    ref_id_set[os.str()] = sum(item_tag);
+  }
+  return ref_id_set;
+}
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("DiamondDualConnectivityBuilder", "[mesh]")
+{
+  SECTION("2D")
+  {
+    constexpr static size_t Dimension = 2;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh();
+    const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+    REQUIRE(primal_connectivity.numberOfNodes() == 53);
+    REQUIRE(primal_connectivity.numberOfFaces() == 110);
+    REQUIRE(primal_connectivity.numberOfCells() == 58);
+
+    std::shared_ptr p_diamond_dual_connectivity =
+      DualConnectivityManager::instance().getDiamondDualConnectivity(primal_connectivity);
+    const ConnectivityType& dual_connectivity = *p_diamond_dual_connectivity;
+
+    REQUIRE(dual_connectivity.numberOfNodes() == 111);
+    REQUIRE(dual_connectivity.numberOfFaces() == 220);
+    REQUIRE(dual_connectivity.numberOfCells() == 110);
+
+    SECTION("ref node list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::node>() == 4);
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::node>() == 4);
+
+      const auto primal_ref_node_list = get_item_ref_ids<ItemType::node>(primal_connectivity);
+      REQUIRE(primal_ref_node_list.size() == 4);
+
+      REQUIRE(primal_ref_node_list.count("XMAXYMAX(11)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMAXYMIN(10)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMINYMAX(9)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMINYMIN(8)") == 1);
+
+      REQUIRE(primal_ref_node_list.at("XMAXYMAX(11)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMAXYMIN(10)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMINYMAX(9)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMINYMIN(8)") == 1);
+
+      const auto dual_ref_node_list = get_item_ref_ids<ItemType::node>(dual_connectivity);
+      REQUIRE(dual_ref_node_list.size() == 4);
+
+      REQUIRE(dual_ref_node_list.count("XMAXYMAX(11)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMAXYMIN(10)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMINYMAX(9)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMINYMIN(8)") == 1);
+
+      REQUIRE(dual_ref_node_list.at("XMAXYMAX(11)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMAXYMIN(10)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMINYMAX(9)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMINYMIN(8)") == 1);
+    }
+
+    SECTION("ref face list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::face>() == 5);
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::face>() == 4);
+
+      const auto primal_ref_face_list = get_item_ref_ids<ItemType::face>(primal_connectivity);
+      REQUIRE(primal_ref_face_list.size() == 5);
+
+      REQUIRE(primal_ref_face_list.count("INTERFACE(5)") == 1);
+      REQUIRE(primal_ref_face_list.count("XMAX(2)") == 1);
+      REQUIRE(primal_ref_face_list.count("XMIN(1)") == 1);
+      REQUIRE(primal_ref_face_list.count("YMAX(3)") == 1);
+      REQUIRE(primal_ref_face_list.count("YMIN(4)") == 1);
+
+      REQUIRE(primal_ref_face_list.at("INTERFACE(5)") == 4);
+      REQUIRE(primal_ref_face_list.at("XMAX(2)") == 4);
+      REQUIRE(primal_ref_face_list.at("XMIN(1)") == 4);
+      REQUIRE(primal_ref_face_list.at("YMAX(3)") == 8);
+      REQUIRE(primal_ref_face_list.at("YMIN(4)") == 8);
+
+      const auto dual_ref_face_list = get_item_ref_ids<ItemType::face>(dual_connectivity);
+      REQUIRE(dual_ref_face_list.size() == 4);
+
+      REQUIRE(dual_ref_face_list.count("XMAX(2)") == 1);
+      REQUIRE(dual_ref_face_list.count("XMIN(1)") == 1);
+      REQUIRE(dual_ref_face_list.count("YMAX(3)") == 1);
+      REQUIRE(dual_ref_face_list.count("YMIN(4)") == 1);
+
+      REQUIRE(dual_ref_face_list.at("XMAX(2)") == 4);
+      REQUIRE(dual_ref_face_list.at("XMIN(1)") == 4);
+      REQUIRE(dual_ref_face_list.at("YMAX(3)") == 8);
+      REQUIRE(dual_ref_face_list.at("YMIN(4)") == 8);
+    }
+
+    SECTION("ref cell list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::cell>() == 2);
+      // Diamond dual cell references are not computed
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::cell>() == 0);
+
+      const auto primal_ref_cell_list = get_item_ref_ids<ItemType::cell>(primal_connectivity);
+      REQUIRE(primal_ref_cell_list.size() == 2);
+
+      REQUIRE(primal_ref_cell_list.count("LEFT(6)") == 1);
+      REQUIRE(primal_ref_cell_list.count("RIGHT(7)") == 1);
+
+      REQUIRE(primal_ref_cell_list.at("LEFT(6)") == 22);
+      REQUIRE(primal_ref_cell_list.at("RIGHT(7)") == 36);
+
+      const auto dual_ref_cell_list = get_item_ref_ids<ItemType::cell>(dual_connectivity);
+      REQUIRE(dual_ref_cell_list.size() == 0);
+    }
+  }
+
+  SECTION("3D")
+  {
+    constexpr static size_t Dimension = 3;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid3DMesh();
+    const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+    REQUIRE(primal_connectivity.numberOfNodes() == 132);
+    REQUIRE(primal_connectivity.numberOfEdges() == 452);
+    REQUIRE(primal_connectivity.numberOfFaces() == 520);
+    REQUIRE(primal_connectivity.numberOfCells() == 199);
+
+    std::shared_ptr p_diamond_dual_connectivity =
+      DualConnectivityManager::instance().getDiamondDualConnectivity(primal_connectivity);
+    const ConnectivityType& dual_connectivity = *p_diamond_dual_connectivity;
+
+    REQUIRE(dual_connectivity.numberOfNodes() == 331);
+    REQUIRE(dual_connectivity.numberOfEdges() == 1461);
+    REQUIRE(dual_connectivity.numberOfFaces() == 1651);
+    REQUIRE(dual_connectivity.numberOfCells() == 520);
+
+    SECTION("ref node list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::node>() == 8);
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::node>() == 8);
+
+      const auto primal_ref_node_list = get_item_ref_ids<ItemType::node>(primal_connectivity);
+      REQUIRE(primal_ref_node_list.size() == 8);
+
+      REQUIRE(primal_ref_node_list.count("XMAXYMAXZMAX(47)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMAXYMAXZMIN(51)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMAXYMINZMAX(45)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMAXYMINZMIN(41)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMINYMAXZMAX(43)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMINYMAXZMIN(42)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMINYMINZMAX(44)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMINYMINZMIN(40)") == 1);
+
+      REQUIRE(primal_ref_node_list.at("XMAXYMAXZMAX(47)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMAXYMAXZMIN(51)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMAXYMINZMAX(45)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMAXYMINZMIN(41)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMINYMAXZMAX(43)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMINYMAXZMIN(42)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMINYMINZMAX(44)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMINYMINZMIN(40)") == 1);
+
+      const auto dual_ref_node_list = get_item_ref_ids<ItemType::node>(dual_connectivity);
+      REQUIRE(dual_ref_node_list.size() == 8);
+
+      REQUIRE(dual_ref_node_list.count("XMAXYMAXZMAX(47)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMAXYMAXZMIN(51)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMAXYMINZMAX(45)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMAXYMINZMIN(41)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMINYMAXZMAX(43)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMINYMAXZMIN(42)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMINYMINZMAX(44)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMINYMINZMIN(40)") == 1);
+
+      REQUIRE(dual_ref_node_list.at("XMAXYMAXZMAX(47)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMAXYMAXZMIN(51)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMAXYMINZMAX(45)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMAXYMINZMIN(41)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMINYMAXZMAX(43)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMINYMAXZMIN(42)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMINYMINZMAX(44)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMINYMINZMIN(40)") == 1);
+    }
+
+    SECTION("ref edge list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::edge>() == 12);
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::edge>() == 12);
+
+      const auto primal_ref_edge_list = get_item_ref_ids<ItemType::edge>(primal_connectivity);
+      REQUIRE(primal_ref_edge_list.size() == 12);
+
+      REQUIRE(primal_ref_edge_list.count("XMAXYMAX(34)") == 1);
+      REQUIRE(primal_ref_edge_list.count("XMAXYMIN(35)") == 1);
+      REQUIRE(primal_ref_edge_list.count("XMAXZMAX(33)") == 1);
+      REQUIRE(primal_ref_edge_list.count("XMAXZMIN(32)") == 1);
+      REQUIRE(primal_ref_edge_list.count("XMINYMAX(30)") == 1);
+      REQUIRE(primal_ref_edge_list.count("XMINYMIN(31)") == 1);
+      REQUIRE(primal_ref_edge_list.count("XMINZMAX(29)") == 1);
+      REQUIRE(primal_ref_edge_list.count("XMINZMIN(28)") == 1);
+      REQUIRE(primal_ref_edge_list.count("YMAXZMAX(39)") == 1);
+      REQUIRE(primal_ref_edge_list.count("YMAXZMIN(38)") == 1);
+      REQUIRE(primal_ref_edge_list.count("YMINZMAX(37)") == 1);
+      REQUIRE(primal_ref_edge_list.count("YMINZMIN(36)") == 1);
+
+      REQUIRE(primal_ref_edge_list.at("XMAXYMAX(34)") == 2);
+      REQUIRE(primal_ref_edge_list.at("XMAXYMIN(35)") == 2);
+      REQUIRE(primal_ref_edge_list.at("XMAXZMAX(33)") == 2);
+      REQUIRE(primal_ref_edge_list.at("XMAXZMIN(32)") == 2);
+      REQUIRE(primal_ref_edge_list.at("XMINYMAX(30)") == 3);
+      REQUIRE(primal_ref_edge_list.at("XMINYMIN(31)") == 3);
+      REQUIRE(primal_ref_edge_list.at("XMINZMAX(29)") == 4);
+      REQUIRE(primal_ref_edge_list.at("XMINZMIN(28)") == 4);
+      REQUIRE(primal_ref_edge_list.at("YMAXZMAX(39)") == 5);
+      REQUIRE(primal_ref_edge_list.at("YMAXZMIN(38)") == 5);
+      REQUIRE(primal_ref_edge_list.at("YMINZMAX(37)") == 6);
+      REQUIRE(primal_ref_edge_list.at("YMINZMIN(36)") == 6);
+
+      const auto dual_ref_edge_list = get_item_ref_ids<ItemType::edge>(dual_connectivity);
+      REQUIRE(dual_ref_edge_list.size() == 12);
+
+      REQUIRE(dual_ref_edge_list.count("XMAXYMAX(34)") == 1);
+      REQUIRE(dual_ref_edge_list.count("XMAXYMIN(35)") == 1);
+      REQUIRE(dual_ref_edge_list.count("XMAXZMAX(33)") == 1);
+      REQUIRE(dual_ref_edge_list.count("XMAXZMIN(32)") == 1);
+      REQUIRE(dual_ref_edge_list.count("XMINYMAX(30)") == 1);
+      REQUIRE(dual_ref_edge_list.count("XMINYMIN(31)") == 1);
+      REQUIRE(dual_ref_edge_list.count("XMINZMAX(29)") == 1);
+      REQUIRE(dual_ref_edge_list.count("XMINZMIN(28)") == 1);
+      REQUIRE(dual_ref_edge_list.count("YMAXZMAX(39)") == 1);
+      REQUIRE(dual_ref_edge_list.count("YMAXZMIN(38)") == 1);
+      REQUIRE(dual_ref_edge_list.count("YMINZMAX(37)") == 1);
+      REQUIRE(dual_ref_edge_list.count("YMINZMIN(36)") == 1);
+
+      REQUIRE(dual_ref_edge_list.at("XMAXYMAX(34)") == 2);
+      REQUIRE(dual_ref_edge_list.at("XMAXYMIN(35)") == 2);
+      REQUIRE(dual_ref_edge_list.at("XMAXZMAX(33)") == 2);
+      REQUIRE(dual_ref_edge_list.at("XMAXZMIN(32)") == 2);
+      REQUIRE(dual_ref_edge_list.at("XMINYMAX(30)") == 3);
+      REQUIRE(dual_ref_edge_list.at("XMINYMIN(31)") == 3);
+      REQUIRE(dual_ref_edge_list.at("XMINZMAX(29)") == 4);
+      REQUIRE(dual_ref_edge_list.at("XMINZMIN(28)") == 4);
+      REQUIRE(dual_ref_edge_list.at("YMAXZMAX(39)") == 5);
+      REQUIRE(dual_ref_edge_list.at("YMAXZMIN(38)") == 5);
+      REQUIRE(dual_ref_edge_list.at("YMINZMAX(37)") == 6);
+      REQUIRE(dual_ref_edge_list.at("YMINZMIN(36)") == 6);
+    }
+
+    SECTION("ref face list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::face>() == 8);
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::face>() == 6);
+
+      const auto primal_ref_face_list = get_item_ref_ids<ItemType::face>(primal_connectivity);
+      REQUIRE(primal_ref_face_list.size() == 8);
+
+      REQUIRE(primal_ref_face_list.count("INTERFACE1(55)") == 1);
+      REQUIRE(primal_ref_face_list.count("INTERFACE2(56)") == 1);
+      REQUIRE(primal_ref_face_list.count("XMAX(23)") == 1);
+      REQUIRE(primal_ref_face_list.count("XMIN(22)") == 1);
+      REQUIRE(primal_ref_face_list.count("YMAX(26)") == 1);
+      REQUIRE(primal_ref_face_list.count("YMIN(27)") == 1);
+      REQUIRE(primal_ref_face_list.count("ZMAX(24)") == 1);
+      REQUIRE(primal_ref_face_list.count("ZMIN(25)") == 1);
+
+      REQUIRE(primal_ref_face_list.at("INTERFACE1(55)") == 12);
+      REQUIRE(primal_ref_face_list.at("INTERFACE2(56)") == 9);
+      REQUIRE(primal_ref_face_list.at("XMAX(23)") == 12);
+      REQUIRE(primal_ref_face_list.at("XMIN(22)") == 12);
+      REQUIRE(primal_ref_face_list.at("YMAX(26)") == 18);
+      REQUIRE(primal_ref_face_list.at("YMIN(27)") == 21);
+      REQUIRE(primal_ref_face_list.at("ZMAX(24)") == 35);
+      REQUIRE(primal_ref_face_list.at("ZMIN(25)") == 35);
+
+      const auto dual_ref_face_list = get_item_ref_ids<ItemType::face>(dual_connectivity);
+      REQUIRE(dual_ref_face_list.size() == 6);
+
+      REQUIRE(dual_ref_face_list.count("XMAX(23)") == 1);
+      REQUIRE(dual_ref_face_list.count("XMIN(22)") == 1);
+      REQUIRE(dual_ref_face_list.count("YMAX(26)") == 1);
+      REQUIRE(dual_ref_face_list.count("YMIN(27)") == 1);
+      REQUIRE(dual_ref_face_list.count("ZMAX(24)") == 1);
+      REQUIRE(dual_ref_face_list.count("ZMIN(25)") == 1);
+
+      REQUIRE(dual_ref_face_list.at("XMAX(23)") == 12);
+      REQUIRE(dual_ref_face_list.at("XMIN(22)") == 12);
+      REQUIRE(dual_ref_face_list.at("YMAX(26)") == 18);
+      REQUIRE(dual_ref_face_list.at("YMIN(27)") == 21);
+      REQUIRE(dual_ref_face_list.at("ZMAX(24)") == 35);
+      REQUIRE(dual_ref_face_list.at("ZMIN(25)") == 35);
+    }
+
+    SECTION("ref cell list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::cell>() == 3);
+
+      const auto primal_ref_cell_list = get_item_ref_ids<ItemType::cell>(primal_connectivity);
+      REQUIRE(primal_ref_cell_list.size() == 3);
+
+      REQUIRE(primal_ref_cell_list.count("LEFT(52)") == 1);
+      REQUIRE(primal_ref_cell_list.count("MIDDLE(53)") == 1);
+      REQUIRE(primal_ref_cell_list.count("RIGHT(54)") == 1);
+
+      REQUIRE(primal_ref_cell_list.at("LEFT(52)") == 30);
+      REQUIRE(primal_ref_cell_list.at("MIDDLE(53)") == 42);
+      REQUIRE(primal_ref_cell_list.at("RIGHT(54)") == 127);
+
+      // Diamond dual cell references are not computed
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::cell>() == 0);
+
+      const auto dual_ref_cell_list = get_item_ref_ids<ItemType::cell>(dual_connectivity);
+      REQUIRE(dual_ref_cell_list.size() == 0);
+    }
+  }
+}
diff --git a/tests/test_DiamondDualMeshBuilder.cpp b/tests/test_DiamondDualMeshBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3b9b2564eadd3ca97aee05651a2bd769b28bb1c0
--- /dev/null
+++ b/tests/test_DiamondDualMeshBuilder.cpp
@@ -0,0 +1,153 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("DiamondDualMeshBuilder", "[mesh]")
+{
+  SECTION("2D")
+  {
+    constexpr static size_t Dimension = 2;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr p_mesh      = MeshDataBaseForTests::get().hybrid2DMesh();
+    const MeshType& primal_mesh = *p_mesh;
+
+    REQUIRE(primal_mesh.numberOfNodes() == 53);
+    REQUIRE(primal_mesh.numberOfFaces() == 110);
+    REQUIRE(primal_mesh.numberOfCells() == 58);
+
+    std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(primal_mesh);
+    const MeshType& dual_mesh           = *p_diamond_dual_mesh;
+
+    REQUIRE(dual_mesh.numberOfNodes() == 111);
+    REQUIRE(dual_mesh.numberOfFaces() == 220);
+    REQUIRE(dual_mesh.numberOfCells() == 110);
+
+    auto dual_cell_volume = MeshDataManager::instance().getMeshData(dual_mesh).Vj();
+
+    REQUIRE(sum(dual_cell_volume) == Catch::Approx(2));
+
+    auto coord_comp = [](const TinyVector<Dimension>& a, const TinyVector<Dimension>& b) -> bool {
+      return (a[0] < b[0]) or ((a[0] == b[0]) and (a[1] < b[1]));
+    };
+
+    auto dual_cell_to_node   = dual_mesh.connectivity().cellToNodeMatrix();
+    auto primal_face_to_node = primal_mesh.connectivity().faceToNodeMatrix();
+    auto primal_face_to_cell = primal_mesh.connectivity().faceToCellMatrix();
+
+    auto dual_xr   = dual_mesh.xr();
+    auto primal_xr = primal_mesh.xr();
+    auto primal_xj = MeshDataManager::instance().getMeshData(primal_mesh).xj();
+
+    using CoordSet = std::set<TinyVector<Dimension>,
+                              std::function<bool(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b)>>;
+
+    CellId dual_cell_id   = 0;
+    FaceId primal_face_id = 0;
+    for (; dual_cell_id < dual_mesh.numberOfCells(); ++dual_cell_id, ++primal_face_id) {
+      CoordSet dual_cell_node_coords{coord_comp};
+      {
+        auto dual_cell_node_list = dual_cell_to_node[dual_cell_id];
+        for (size_t i_node = 0; i_node < dual_cell_node_list.size(); ++i_node) {
+          dual_cell_node_coords.insert(dual_xr[dual_cell_node_list[i_node]]);
+        }
+      }
+
+      CoordSet primal_coords{coord_comp};
+      {
+        auto primal_face_node_list = primal_face_to_node[primal_face_id];
+        for (size_t i_node = 0; i_node < primal_face_node_list.size(); ++i_node) {
+          primal_coords.insert(primal_xr[primal_face_node_list[i_node]]);
+        }
+        auto primal_face_cell_list = primal_face_to_cell[primal_face_id];
+        for (size_t i_cell = 0; i_cell < primal_face_cell_list.size(); ++i_cell) {
+          primal_coords.insert(primal_xj[primal_face_cell_list[i_cell]]);
+        }
+      }
+
+      REQUIRE(primal_coords == dual_cell_node_coords);
+    }
+  }
+
+  SECTION("3D")
+  {
+    constexpr static size_t Dimension = 3;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr p_mesh      = MeshDataBaseForTests::get().hybrid3DMesh();
+    const MeshType& primal_mesh = *p_mesh;
+
+    REQUIRE(primal_mesh.numberOfNodes() == 132);
+    REQUIRE(primal_mesh.numberOfEdges() == 452);
+    REQUIRE(primal_mesh.numberOfFaces() == 520);
+    REQUIRE(primal_mesh.numberOfCells() == 199);
+
+    std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(primal_mesh);
+    const MeshType& dual_mesh           = *p_diamond_dual_mesh;
+
+    REQUIRE(dual_mesh.numberOfNodes() == 331);
+    REQUIRE(dual_mesh.numberOfEdges() == 1461);
+    REQUIRE(dual_mesh.numberOfFaces() == 1651);
+    REQUIRE(dual_mesh.numberOfCells() == 520);
+
+    auto dual_cell_volume = MeshDataManager::instance().getMeshData(dual_mesh).Vj();
+
+    REQUIRE(sum(dual_cell_volume) == Catch::Approx(2));
+
+    auto coord_comp = [](const TinyVector<Dimension>& a, const TinyVector<Dimension>& b) -> bool {
+      return (a[0] < b[0]) or ((a[0] == b[0]) and (a[1] < b[1])) or
+             ((a[0] == b[0]) and (a[1] == b[1]) and (a[2] < b[2]));
+    };
+
+    auto dual_cell_to_node   = dual_mesh.connectivity().cellToNodeMatrix();
+    auto primal_face_to_node = primal_mesh.connectivity().faceToNodeMatrix();
+    auto primal_face_to_cell = primal_mesh.connectivity().faceToCellMatrix();
+
+    auto dual_xr   = dual_mesh.xr();
+    auto primal_xr = primal_mesh.xr();
+    auto primal_xj = MeshDataManager::instance().getMeshData(primal_mesh).xj();
+
+    using CoordSet = std::set<TinyVector<Dimension>,
+                              std::function<bool(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b)>>;
+
+    CellId dual_cell_id   = 0;
+    FaceId primal_face_id = 0;
+    for (; dual_cell_id < dual_mesh.numberOfCells(); ++dual_cell_id, ++primal_face_id) {
+      CoordSet dual_cell_node_coords{coord_comp};
+      {
+        auto dual_cell_node_list = dual_cell_to_node[dual_cell_id];
+        for (size_t i_node = 0; i_node < dual_cell_node_list.size(); ++i_node) {
+          dual_cell_node_coords.insert(dual_xr[dual_cell_node_list[i_node]]);
+        }
+      }
+
+      CoordSet primal_coords{coord_comp};
+      {
+        auto primal_face_node_list = primal_face_to_node[primal_face_id];
+        for (size_t i_node = 0; i_node < primal_face_node_list.size(); ++i_node) {
+          primal_coords.insert(primal_xr[primal_face_node_list[i_node]]);
+        }
+        auto primal_face_cell_list = primal_face_to_cell[primal_face_id];
+        for (size_t i_cell = 0; i_cell < primal_face_cell_list.size(); ++i_cell) {
+          primal_coords.insert(primal_xj[primal_face_cell_list[i_cell]]);
+        }
+      }
+
+      REQUIRE(primal_coords == dual_cell_node_coords);
+    }
+  }
+}
diff --git a/tests/test_DiscreteFunctionInterpoler.cpp b/tests/test_DiscreteFunctionInterpoler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..809ca559ca254cbbb5d90c63f8dcd943a5df23c8
--- /dev/null
+++ b/tests/test_DiscreteFunctionInterpoler.cpp
@@ -0,0 +1,940 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/ast/ASTBuilder.hpp>
+#include <language/ast/ASTModulesImporter.hpp>
+#include <language/ast/ASTNodeDataTypeBuilder.hpp>
+#include <language/ast/ASTNodeExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionEvaluationExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionExpressionBuilder.hpp>
+#include <language/ast/ASTNodeTypeCleaner.hpp>
+#include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/PugsFunctionAdapter.hpp>
+#include <language/utils/SymbolTable.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+
+#include <scheme/DiscreteFunctionDescriptorP0.hpp>
+#include <scheme/DiscreteFunctionInterpoler.hpp>
+#include <scheme/DiscreteFunctionP0.hpp>
+
+#include <pegtl/string_input.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("DiscreteFunctionInterpoler", "[scheme]")
+{
+  auto same_cell_value = [](auto f, auto g) -> bool {
+    using ItemIdType = typename decltype(f)::index_type;
+    for (ItemIdType item_id = 0; item_id < f.numberOfItems(); ++item_id) {
+      if (f[item_id] != g[item_id]) {
+        return false;
+      }
+    }
+
+    return true;
+  };
+
+  SECTION("1D")
+  {
+    constexpr size_t Dimension = 1;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_1d = named_mesh.mesh();
+
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+        std::string_view data = R"(
+import math;
+let B_scalar_non_linear_1d: R^1 -> B, x -> (exp(2 * x[0]) + 3 > 4);
+let N_scalar_non_linear_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2);
+let Z_scalar_non_linear_1d: R^1 -> Z, x -> floor(exp(2 * x[0]) - 1);
+let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+let R1_non_linear_1d: R^1 -> R^1, x -> 2 * exp(x[0]);
+let R2_non_linear_1d: R^1 -> R^2, x -> (2 * exp(x[0]), -3*x[0]);
+let R3_non_linear_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
+let R1x1_non_linear_1d: R^1 -> R^1x1, x -> (2 * exp(x[0]) * sin(x[0]) + 3);
+let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
+let R3x3_non_linear_1d: R^1 -> R^3x3, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0], -4*x[0], 2*x[0]+1, 3, -6*x[0], exp(x[0]));
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        SECTION("B_scalar_non_linear_1d")
+        {
+          auto [i_symbol, found] = symbol_table->find("B_scalar_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("N_scalar_non_linear_1d")
+        {
+          auto [i_symbol, found] = symbol_table->find("N_scalar_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * x[0] * x[0] + 2);
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("Z_scalar_non_linear_1d")
+        {
+          auto [i_symbol, found] = symbol_table->find("Z_scalar_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 1);
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("R_scalar_non_linear_1d")
+        {
+          auto [i_symbol, found] = symbol_table->find("R_scalar_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0]) + 3;
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("R1_non_linear_1d")
+        {
+          using DataType = TinyVector<1>;
+
+          auto [i_symbol, found] = symbol_table->find("R1_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0])};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R2_non_linear_1d")
+        {
+          using DataType = TinyVector<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]), -3 * x[0]};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R3_non_linear_1d")
+        {
+          using DataType = TinyVector<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + 3, x[0] - 2, 3};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R1x1_non_linear_1d")
+        {
+          using DataType = TinyMatrix<1>;
+
+          auto [i_symbol, found] = symbol_table->find("R1x1_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R2x2_non_linear_1d")
+        {
+          using DataType = TinyMatrix<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id] =
+                DataType{2 * std::exp(x[0]) * std::sin(x[0]) + 3, std::sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R3x3_non_linear_1d")
+        {
+          using DataType = TinyMatrix<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * exp(x[0]) * std::sin(x[0]) + 3,
+                                             std::sin(x[0] - 2 * x[0]),
+                                             3,
+                                             x[0] * x[0],
+                                             -4 * x[0],
+                                             2 * x[0] + 1,
+                                             3,
+                                             -6 * x[0],
+                                             std::exp(x[0])};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+      }
+    }
+  }
+
+  SECTION("2D")
+  {
+    constexpr size_t Dimension = 2;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_2d = named_mesh.mesh();
+
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+
+        std::string_view data = R"(
+import math;
+let B_scalar_non_linear_2d: R^2 -> B, x -> (exp(2 * x[0])< 2*x[1]);
+let N_scalar_non_linear_2d: R^2 -> N, x -> floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + 2);
+let Z_scalar_non_linear_2d: R^2 -> Z, x -> floor(exp(2 * x[0]) - 3 * x[1]);
+let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0]) + 3 * x[1];
+let R1_non_linear_2d: R^2 -> R^1, x -> 2 * exp(x[0]);
+let R2_non_linear_2d: R^2 -> R^2, x -> (2 * exp(x[0]), -3*x[1]);
+let R3_non_linear_2d: R^2 -> R^3, x -> (2 * exp(x[0]) + 3, x[1] - 2, 3);
+let R1x1_non_linear_2d: R^2 -> R^1x1, x -> (2 * exp(x[0]) * sin(x[1]) + 3);
+let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3, sin(x[1] - 2 * x[0]), 3, x[1] * x[0]);
+let R3x3_non_linear_2d: R^2 -> R^3x3, x -> (2 * exp(x[0]) * sin(x[1]) + 3, sin(x[1] - 2 * x[0]), 3, x[1] * x[0], -4*x[1], 2*x[0]+1, 3, -6*x[0], exp(x[1]));
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        SECTION("B_scalar_non_linear_2d")
+        {
+          auto [i_symbol, found] = symbol_table->find("B_scalar_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0]) < 2 * x[1];
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("N_scalar_non_linear_2d")
+        {
+          auto [i_symbol, found] = symbol_table->find("N_scalar_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + 2);
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("Z_scalar_non_linear_2d")
+        {
+          auto [i_symbol, found] = symbol_table->find("Z_scalar_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 3 * x[1]);
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("R_scalar_non_linear_2d")
+        {
+          auto [i_symbol, found] = symbol_table->find("R_scalar_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0]) + 3 * x[1];
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("R1_non_linear_2d")
+        {
+          using DataType = TinyVector<1>;
+
+          auto [i_symbol, found] = symbol_table->find("R1_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0])};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R2_non_linear_2d")
+        {
+          using DataType = TinyVector<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]), -3 * x[1]};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R3_non_linear_2d")
+        {
+          using DataType = TinyVector<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + 3, x[1] - 2, 3};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R1x1_non_linear_2d")
+        {
+          using DataType = TinyMatrix<1>;
+
+          auto [i_symbol, found] = symbol_table->find("R1x1_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R2x2_non_linear_2d")
+        {
+          using DataType = TinyMatrix<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id] =
+                DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, std::sin(x[1] - 2 * x[0]), 3, x[1] * x[0]};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R3x3_non_linear_2d")
+        {
+          using DataType = TinyMatrix<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+
+              cell_value[cell_id] = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3,
+                                             std::sin(x[1] - 2 * x[0]),
+                                             3,
+                                             x[1] * x[0],
+                                             -4 * x[1],
+                                             2 * x[0] + 1,
+                                             3,
+                                             -6 * x[0],
+                                             std::exp(x[1])};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+      }
+    }
+  }
+
+  SECTION("3D")
+  {
+    constexpr size_t Dimension = 3;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
+
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+
+        std::string_view data = R"(
+import math;
+let B_scalar_non_linear_3d: R^3 -> B, x -> (exp(2 * x[0])< 2*x[1]+x[2]);
+let N_scalar_non_linear_3d: R^3 -> N, x -> floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + x[2] * x[2]);
+let Z_scalar_non_linear_3d: R^3 -> Z, x -> floor(exp(2 * x[0]) - 3 * x[1] + x[2]);
+let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]+x[2]) + 3 * x[1];
+let R1_non_linear_3d: R^3 -> R^1, x -> 2 * exp(x[0])+sin(x[1] + x[2]);
+let R2_non_linear_3d: R^3 -> R^2, x -> (2 * exp(x[0]), -3*x[1] * x[2]);
+let R3_non_linear_3d: R^3 -> R^3, x -> (2 * exp(x[0]) + 3, x[1] - 2, 3 * x[2]);
+let R1x1_non_linear_3d: R^3 -> R^1x1, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * x[2]);
+let R2x2_non_linear_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3, sin(x[2] - 2 * x[0]), 3, x[1] * x[0] - x[2]);
+let R3x3_non_linear_3d: R^3 -> R^3x3, x -> (2 * exp(x[0]) * sin(x[1]) + 3, sin(x[1] - 2 * x[2]), 3, x[1] * x[2], -4*x[1], 2*x[2]+1, 3, -6*x[2], exp(x[1] + x[2]));
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        SECTION("B_scalar_non_linear_3d")
+        {
+          auto [i_symbol, found] = symbol_table->find("B_scalar_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0]) < 2 * x[1] + x[2];
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("N_scalar_non_linear_3d")
+        {
+          auto [i_symbol, found] = symbol_table->find("N_scalar_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * (x[0] + x[1]) * (x[0] + x[1]) + x[2] * x[2]);
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("Z_scalar_non_linear_3d")
+        {
+          auto [i_symbol, found] = symbol_table->find("Z_scalar_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 3 * x[1] + x[2]);
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("R_scalar_non_linear_3d")
+        {
+          auto [i_symbol, found] = symbol_table->find("R_scalar_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0] + x[2]) + 3 * x[1];
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*discrete_function)));
+        }
+
+        SECTION("R1_non_linear_3d")
+        {
+          using DataType = TinyVector<1>;
+
+          auto [i_symbol, found] = symbol_table->find("R1_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + std::sin(x[1] + x[2])};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R2_non_linear_3d")
+        {
+          using DataType = TinyVector<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]), -3 * x[1] * x[2]};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R3_non_linear_3d")
+        {
+          using DataType = TinyVector<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]) + 3, x[1] - 2, 3 * x[2]};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R1x1_non_linear_3d")
+        {
+          using DataType = TinyMatrix<1>;
+
+          auto [i_symbol, found] = symbol_table->find("R1x1_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3 * x[2]};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R2x2_non_linear_3d")
+        {
+          using DataType = TinyMatrix<2>;
+
+          auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id] =
+                DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3, std::sin(x[2] - 2 * x[0]), 3, x[1] * x[0] - x[2]};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+
+        SECTION("R3x3_non_linear_3d")
+        {
+          using DataType = TinyMatrix<3>;
+
+          auto [i_symbol, found] = symbol_table->find("R3x3_non_linear_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<DataType> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+
+              cell_value[cell_id] = DataType{2 * std::exp(x[0]) * std::sin(x[1]) + 3,
+                                             std::sin(x[1] - 2 * x[2]),
+                                             3,
+                                             x[1] * x[2],
+                                             -4 * x[1],
+                                             2 * x[2] + 1,
+                                             3,
+                                             -6 * x[2],
+                                             std::exp(x[1] + x[2])};
+            });
+
+          DiscreteFunctionInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(),
+                                                function_symbol_id);
+          std::shared_ptr discrete_function = interpoler.interpolate();
+
+          REQUIRE(same_cell_value(cell_value,
+                                  dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*discrete_function)));
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_DiscreteFunctionP0.cpp b/tests/test_DiscreteFunctionP0.cpp
index 8b7e48ea8f999b7d3d166a2f0b6eefc2a363bc75..7073def1e438a3a19df04723ba88349d3bb7d7b1 100644
--- a/tests/test_DiscreteFunctionP0.cpp
+++ b/tests/test_DiscreteFunctionP0.cpp
@@ -22,128 +22,147 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
   {
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D();
-
       constexpr size_t Dimension = 1;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          REQUIRE(f.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
 
-      REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.mesh().get() == mesh.get());
 
-      DiscreteFunctionP0 g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
+          DiscreteFunctionP0 g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
 
-      CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
-      h_values.fill(ZeroType{});
+          CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
+          h_values.fill(ZeroType{});
 
-      DiscreteFunctionP0 zero{mesh, [&] {
-                                CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
-                                cell_value.fill(ZeroType{});
-                                return cell_value;
-                              }()};
+          DiscreteFunctionP0 zero{mesh, [&] {
+                                    CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
+                                    cell_value.fill(ZeroType{});
+                                    return cell_value;
+                                  }()};
 
-      DiscreteFunctionP0 h{mesh, h_values};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_values));
+          DiscreteFunctionP0 h{mesh, h_values};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_values));
 
-      DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
-      shallow_h = h;
+          DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
+          shallow_h = h;
 
-      copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
+          copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
 
-      REQUIRE(same_values(shallow_h, h_values));
-      REQUIRE(same_values(h, h_values));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(shallow_h, h_values));
+          REQUIRE(same_values(h, h_values));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0 moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_values));
+          DiscreteFunctionP0 moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_values));
+        }
+      }
     }
 
     SECTION("2D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D();
-
       constexpr size_t Dimension = 2;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          REQUIRE(f.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
 
-      REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.mesh().get() == mesh.get());
 
-      DiscreteFunctionP0 g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
+          DiscreteFunctionP0 g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
 
-      CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
-      h_values.fill(ZeroType{});
+          CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
+          h_values.fill(ZeroType{});
 
-      DiscreteFunctionP0 zero{mesh, [&] {
-                                CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
-                                cell_value.fill(ZeroType{});
-                                return cell_value;
-                              }()};
+          DiscreteFunctionP0 zero{mesh, [&] {
+                                    CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
+                                    cell_value.fill(ZeroType{});
+                                    return cell_value;
+                                  }()};
 
-      DiscreteFunctionP0 h{mesh, h_values};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_values));
+          DiscreteFunctionP0 h{mesh, h_values};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_values));
 
-      DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
-      shallow_h = h;
+          DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
+          shallow_h = h;
 
-      copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
+          copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
 
-      REQUIRE(same_values(shallow_h, h_values));
-      REQUIRE(same_values(h, h_values));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(shallow_h, h_values));
+          REQUIRE(same_values(h, h_values));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0 moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_values));
+          DiscreteFunctionP0 moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_values));
+        }
+      }
     }
 
     SECTION("3D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D();
-
       constexpr size_t Dimension = 3;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          REQUIRE(f.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0);
 
-      REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.mesh().get() == mesh.get());
 
-      DiscreteFunctionP0 g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
+          DiscreteFunctionP0 g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0);
 
-      CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
-      h_values.fill(ZeroType{});
+          CellValue<TinyVector<Dimension>> h_values{mesh->connectivity()};
+          h_values.fill(ZeroType{});
 
-      DiscreteFunctionP0 zero{mesh, [&] {
-                                CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
-                                cell_value.fill(ZeroType{});
-                                return cell_value;
-                              }()};
+          DiscreteFunctionP0 zero{mesh, [&] {
+                                    CellValue<TinyVector<Dimension>> cell_value{mesh->connectivity()};
+                                    cell_value.fill(ZeroType{});
+                                    return cell_value;
+                                  }()};
 
-      DiscreteFunctionP0 h{mesh, h_values};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_values));
+          DiscreteFunctionP0 h{mesh, h_values};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_values));
 
-      DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
-      shallow_h = h;
+          DiscreteFunctionP0<Dimension, TinyVector<Dimension>> shallow_h{mesh};
+          shallow_h = h;
 
-      copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
+          copy_to(MeshDataManager::instance().getMeshData(*mesh).xj(), h_values);
 
-      REQUIRE(same_values(shallow_h, h_values));
-      REQUIRE(same_values(h, h_values));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(shallow_h, h_values));
+          REQUIRE(same_values(h, h_values));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0 moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_values));
+          DiscreteFunctionP0 moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_values));
+        }
+      }
     }
   }
 
@@ -161,65 +180,89 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("1D")
     {
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesianMesh1D();
       constexpr size_t Dimension = 1;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      f.fill(3);
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          f.fill(3);
 
-      REQUIRE(all_values_equal(f, 3));
+          REQUIRE(all_values_equal(f, 3));
 
-      DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-      v.fill(TinyVector<3>{1, 2, 3});
+          DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+          v.fill(TinyVector<3>{1, 2, 3});
 
-      REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
+          REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
 
-      DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
-      A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
+          DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
+          A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
 
-      REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
+          REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
+        }
+      }
     }
 
     SECTION("2D")
     {
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesianMesh2D();
       constexpr size_t Dimension = 2;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      f.fill(3);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          f.fill(3);
 
-      REQUIRE(all_values_equal(f, 3));
+          REQUIRE(all_values_equal(f, 3));
 
-      DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-      v.fill(TinyVector<3>{1, 2, 3});
+          DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+          v.fill(TinyVector<3>{1, 2, 3});
 
-      REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
+          REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
 
-      DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
-      A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
+          DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
+          A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
 
-      REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
+          REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
+        }
+      }
     }
 
     SECTION("3D")
     {
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesianMesh3D();
       constexpr size_t Dimension = 3;
 
-      DiscreteFunctionP0<Dimension, double> f{mesh};
-      f.fill(3);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          DiscreteFunctionP0<Dimension, double> f{mesh};
+          f.fill(3);
 
-      REQUIRE(all_values_equal(f, 3));
+          REQUIRE(all_values_equal(f, 3));
 
-      DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-      v.fill(TinyVector<3>{1, 2, 3});
+          DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+          v.fill(TinyVector<3>{1, 2, 3});
 
-      REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
+          REQUIRE(all_values_equal(v, TinyVector<3>{1, 2, 3}));
 
-      DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
-      A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
+          DiscreteFunctionP0<Dimension, TinyMatrix<3>> A{mesh};
+          A.fill(TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9});
 
-      REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
+          REQUIRE(all_values_equal(A, TinyMatrix<3>{1, 2, 3, 4, 5, 6, 7, 8, 9}));
+        }
+      }
     }
   }
 
@@ -237,286 +280,307 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
 
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D();
-
       constexpr size_t Dimension = 1;
 
-      SECTION("scalar")
-      {
-        const size_t value = parallel::rank() + 1;
-        const size_t zero  = 0;
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
 
-        DiscreteFunctionP0<Dimension, size_t> f{mesh};
-        f.fill(value);
+          SECTION("scalar")
+          {
+            const size_t value = parallel::rank() + 1;
+            const size_t zero  = 0;
 
-        REQUIRE(all_values_equal(f, value));
+            DiscreteFunctionP0<Dimension, size_t> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("vector")
-      {
-        const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
-        const TinyVector<2, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
 
-        REQUIRE(all_values_equal(f, value));
+          SECTION("vector")
+          {
+            const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
+            const TinyVector<2, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("matrix")
-      {
-        const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
-        const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
+
+          SECTION("matrix")
+          {
+            const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
+            const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
+            f.fill(value);
 
-        REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(f, value));
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        copy_to(g, f);
-        g.fill(zero);
+            copy_to(g, f);
+            g.fill(zero);
 
-        DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
+            DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        copy_to(h, g);
+            copy_to(h, g);
 
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(g, value));
+          }
+        }
       }
     }
 
     SECTION("2D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D();
-
       constexpr size_t Dimension = 2;
 
-      SECTION("scalar")
-      {
-        const size_t value = parallel::rank() + 1;
-        const size_t zero  = 0;
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
 
-        DiscreteFunctionP0<Dimension, size_t> f{mesh};
-        f.fill(value);
+          SECTION("scalar")
+          {
+            const size_t value = parallel::rank() + 1;
+            const size_t zero  = 0;
 
-        REQUIRE(all_values_equal(f, value));
+            DiscreteFunctionP0<Dimension, size_t> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("vector")
-      {
-        const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
-        const TinyVector<2, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
 
-        REQUIRE(all_values_equal(f, value));
+          SECTION("vector")
+          {
+            const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
+            const TinyVector<2, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("matrix")
-      {
-        const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
-        const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
+
+          SECTION("matrix")
+          {
+            const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
+            const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
+            f.fill(value);
 
-        REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(f, value));
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        copy_to(g, f);
-        g.fill(zero);
+            copy_to(g, f);
+            g.fill(zero);
 
-        DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
+            DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        copy_to(h, g);
+            copy_to(h, g);
 
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(g, value));
+          }
+        }
       }
     }
 
     SECTION("3D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D();
-
       constexpr size_t Dimension = 3;
 
-      SECTION("scalar")
-      {
-        const size_t value = parallel::rank() + 1;
-        const size_t zero  = 0;
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
 
-        DiscreteFunctionP0<Dimension, size_t> f{mesh};
-        f.fill(value);
+          SECTION("scalar")
+          {
+            const size_t value = parallel::rank() + 1;
+            const size_t zero  = 0;
 
-        REQUIRE(all_values_equal(f, value));
+            DiscreteFunctionP0<Dimension, size_t> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const size_t> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("vector")
-      {
-        const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
-        const TinyVector<2, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
 
-        REQUIRE(all_values_equal(f, value));
+          SECTION("vector")
+          {
+            const TinyVector<2, size_t> value{parallel::rank() + 1, 3};
+            const TinyVector<2, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyVector<2, size_t>> f{mesh};
+            f.fill(value);
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        copy_to(g, f);
-        g.fill(zero);
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
+            copy_to(g, f);
+            g.fill(zero);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            DiscreteFunctionP0<Dimension, const TinyVector<2, size_t>> h = copy(f);
 
-        copy_to(h, g);
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(all_values_equal(g, value));
-      }
+            copy_to(h, g);
 
-      SECTION("matrix")
-      {
-        const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
-        const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
-        DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
-        f.fill(value);
+            REQUIRE(all_values_equal(g, value));
+          }
+
+          SECTION("matrix")
+          {
+            const TinyMatrix<3, 3, size_t> value{1, 2, 3, 4, 5, 6, 7, 8, 9};
+            const TinyMatrix<3, 3, size_t> zero{ZeroType{}};
+            DiscreteFunctionP0<Dimension, TinyMatrix<3, 3, size_t>> f{mesh};
+            f.fill(value);
 
-        REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(f, value));
 
-        DiscreteFunctionP0 g = copy(f);
-        f.fill(zero);
+            DiscreteFunctionP0 g = copy(f);
+            f.fill(zero);
 
-        REQUIRE(all_values_equal(f, zero));
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(f, zero));
+            REQUIRE(all_values_equal(g, value));
 
-        copy_to(g, f);
-        g.fill(zero);
+            copy_to(g, f);
+            g.fill(zero);
 
-        DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
+            DiscreteFunctionP0<Dimension, const TinyMatrix<3, 3, size_t>> h = copy(f);
 
-        REQUIRE(all_values_equal(f, value));
-        REQUIRE(all_values_equal(g, zero));
-        REQUIRE(all_values_equal(h, value));
+            REQUIRE(all_values_equal(f, value));
+            REQUIRE(all_values_equal(g, zero));
+            REQUIRE(all_values_equal(h, value));
 
-        copy_to(h, g);
+            copy_to(h, g);
 
-        REQUIRE(all_values_equal(g, value));
+            REQUIRE(all_values_equal(g, value));
+          }
+        }
       }
     }
   }
@@ -525,75 +589,81 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
   {
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D();
-
       constexpr size_t Dimension = 1;
+      std::array mesh_list       = MeshDataBaseForTests::get().all1DMeshes();
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      SECTION("unary minus")
-      {
-        SECTION("scalar functions")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              f[cell_id]     = 2 * x + 1;
-            });
+          auto mesh = named_mesh.mesh();
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          Array<double> minus_values{mesh->numberOfCells()};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
+          SECTION("unary minus")
+          {
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0<Dimension, double> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  f[cell_id]     = 2 * x + 1;
+                });
 
-          REQUIRE(same_values(-f, minus_values));
-          REQUIRE(same_values(-const_f, minus_values));
-        }
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
+              Array<double> minus_values{mesh->numberOfCells()};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyVector<VectorDimension> X{x, 2 - x};
-              f[cell_id] = 2 * X + TinyVector<2>{1, 2};
-            });
+              REQUIRE(same_values(-f, minus_values));
+              REQUIRE(same_values(-const_f, minus_values));
+            }
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
 
-          Array<TinyVector<VectorDimension>> minus_values{mesh->numberOfCells()};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyVector<VectorDimension> X{x, 2 - x};
+                  f[cell_id] = 2 * X + TinyVector<2>{1, 2};
+                });
 
-          REQUIRE(same_values(-f, minus_values));
-          REQUIRE(same_values(-const_f, minus_values));
-        }
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+              Array<TinyVector<VectorDimension>> minus_values{mesh->numberOfCells()};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyMatrix<MatrixDimension> A{x, 2 - x, 2 * x, x * x - 3};
-              f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
-            });
+              REQUIRE(same_values(-f, minus_values));
+              REQUIRE(same_values(-const_f, minus_values));
+            }
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
 
-          Array<TinyMatrix<MatrixDimension>> minus_values{mesh->numberOfCells()};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyMatrix<MatrixDimension> A{x, 2 - x, 2 * x, x * x - 3};
+                  f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
+                });
+
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+
+              Array<TinyMatrix<MatrixDimension>> minus_values{mesh->numberOfCells()};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { minus_values[cell_id] = -f[cell_id]; });
 
-          REQUIRE(same_values(-f, minus_values));
-          REQUIRE(same_values(-const_f, minus_values));
+              REQUIRE(same_values(-f, minus_values));
+              REQUIRE(same_values(-const_f, minus_values));
+            }
+          }
         }
       }
     }
@@ -603,1706 +673,2847 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
   {
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D();
-
       constexpr size_t Dimension = 1;
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              f[cell_id]     = 2 * x + 1;
-            });
+          auto mesh = named_mesh.mesh();
 
-          DiscreteFunctionP0<Dimension, double> g{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              g[cell_id]     = std::abs((x + 1) * (x - 2)) + 1;
-            });
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
-          DiscreteFunctionP0<Dimension, const double> const_g{g};
-
-          SECTION("sum")
+          SECTION("inner operators")
           {
-            Array<double> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
-
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
-          }
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0<Dimension, double> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  f[cell_id]     = 2 * x + 1;
+                });
 
-          SECTION("difference")
-          {
-            Array<double> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+              DiscreteFunctionP0<Dimension, double> g{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  g[cell_id]     = std::abs((x + 1) * (x - 2)) + 1;
+                });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
-          }
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<double> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<double> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<double> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
+
+              SECTION("ratio")
+              {
+                Array<double> ratio_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+
+                REQUIRE(same_values(f / g, ratio_values));
+                REQUIRE(same_values(const_f / g, ratio_values));
+                REQUIRE(same_values(f / const_g, ratio_values));
+                REQUIRE(same_values(const_f / const_g, ratio_values));
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyVector<VectorDimension> X{x, 2 - x};
+                  f[cell_id] = 2 * X + TinyVector<2>{1, 2};
+                });
 
-          SECTION("product")
-          {
-            Array<double> product_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyVector<VectorDimension> X{3 * x + 1, 2 + x};
+                  g[cell_id] = X;
+                });
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
-          }
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
 
-          SECTION("ratio")
-          {
-            Array<double> ratio_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+              SECTION("sum")
+              {
+                Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
 
-            REQUIRE(same_values(f / g, ratio_values));
-            REQUIRE(same_values(const_f / g, ratio_values));
-            REQUIRE(same_values(f / const_g, ratio_values));
-            REQUIRE(same_values(const_f / const_g, ratio_values));
-          }
-        }
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
+              SECTION("difference")
+              {
+                Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyVector<VectorDimension> X{x, 2 - x};
-              f[cell_id] = 2 * X + TinyVector<2>{1, 2};
-            });
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyVector<VectorDimension> X{3 * x + 1, 2 + x};
-              g[cell_id] = X;
-            });
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyMatrix<MatrixDimension> A{x, 2 - x, 2 * x, x * x - 3};
+                  f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
+                });
 
-          SECTION("sum")
-          {
-            Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyMatrix<MatrixDimension> A{3 * x + 1, 2 + x, 1 - 2 * x, 2 * x * x};
+                  g[cell_id] = A;
+                });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
+            }
           }
 
-          SECTION("difference")
+          SECTION("external operators")
           {
-            Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0<Dimension, double> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  f[cell_id]     = std::abs(2 * x) + 1;
+                });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+              const double a = 3;
+
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+
+              SECTION("sum")
+              {
+                {
+                  Array<double> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+
+                  REQUIRE(same_values(a + f, sum_values));
+                  REQUIRE(same_values(a + const_f, sum_values));
+                }
+                {
+                  Array<double> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+
+                  REQUIRE(same_values(f + a, sum_values));
+                  REQUIRE(same_values(const_f + a, sum_values));
+                }
+              }
+
+              SECTION("difference")
+              {
+                {
+                  Array<double> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = a - f[cell_id]; });
+                  REQUIRE(same_values(a - f, difference_values));
+                  REQUIRE(same_values(a - const_f, difference_values));
+                }
+
+                {
+                  Array<double> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - a; });
+                  REQUIRE(same_values(f - a, difference_values));
+                  REQUIRE(same_values(const_f - a, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  Array<double> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+                {
+                  Array<double> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
+
+                  REQUIRE(same_values(f * a, product_values));
+                  REQUIRE(same_values(const_f * a, product_values));
+                }
+
+                {
+                  Array<TinyVector<3>> product_values{mesh->numberOfCells()};
+                  const TinyVector<3> v{1, 2, 3};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v; });
+
+                  REQUIRE(same_values(f * v, product_values));
+                  REQUIRE(same_values(const_f * v, product_values));
+                }
+
+                {
+                  Array<TinyVector<3>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      v[cell_id]     = TinyVector<3>{x, 2 * x, 1 - x};
+                    });
+
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+
+                  REQUIRE(same_values(f * v, product_values));
+                  REQUIRE(same_values(const_f * v, product_values));
+                }
+
+                {
+                  Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
+                  const TinyMatrix<2> A{1, 2, 3, 4};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+
+                {
+                  Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
+                    });
+
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+
+                  REQUIRE(same_values(f * M, product_values));
+                  REQUIRE(same_values(const_f * M, product_values));
+                }
+              }
+
+              SECTION("ratio")
+              {
+                {
+                  Array<double> ratio_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
+
+                  REQUIRE(same_values(a / f, ratio_values));
+                  REQUIRE(same_values(a / const_f, ratio_values));
+                }
+                {
+                  Array<double> ratio_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+
+                  REQUIRE(same_values(f / a, ratio_values));
+                  REQUIRE(same_values(const_f / a, ratio_values));
+                }
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyVector<VectorDimension> X{x, 2 - x};
+                  f[cell_id] = 2 * X + TinyVector<2>{1, 2};
+                });
+
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
+
+                  REQUIRE(same_values(v + f, sum_values));
+                  REQUIRE(same_values(v + const_f, sum_values));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
+
+                  REQUIRE(same_values(f + v, sum_values));
+                  REQUIRE(same_values(const_f + v, sum_values));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+
+                  REQUIRE(same_values(v - f, difference_values));
+                  REQUIRE(same_values(v - const_f, difference_values));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+
+                  REQUIRE(same_values(f - v, difference_values));
+                  REQUIRE(same_values(const_f - v, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  const double a = 2.3;
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      a[cell_id]     = 2 * x * x - 1;
+                    });
+
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  const TinyMatrix<VectorDimension> A{1, 2, 3, 4};
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
+                    });
+
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(M * f, product_values));
+                  REQUIRE(same_values(M * const_f, product_values));
+                }
+              }
+            }
+
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyMatrix<MatrixDimension> X{x, 2 - x, x * x, x * 3};
+                  f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
+                });
+
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+
+                  REQUIRE(same_values(A + f, sum_values));
+                  REQUIRE(same_values(A + const_f, sum_values));
+                }
+                {
+                  Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+
+                  REQUIRE(same_values(f + A, sum_values));
+                  REQUIRE(same_values(const_f + A, sum_values));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
+
+                  REQUIRE(same_values(A - f, difference_values));
+                  REQUIRE(same_values(A - const_f, difference_values));
+                }
+                {
+                  Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
+
+                  REQUIRE(same_values(f - A, difference_values));
+                  REQUIRE(same_values(const_f - A, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  const double a = 2.3;
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      a[cell_id]     = 2 * x * x - 1;
+                    });
+
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+              }
+            }
           }
         }
+      }
+    }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyMatrix<MatrixDimension> A{x, 2 - x, 2 * x, x * x - 3};
-              f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
-            });
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyMatrix<MatrixDimension> A{3 * x + 1, 2 + x, 1 - 2 * x, 2 * x * x};
-              g[cell_id] = A;
-            });
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          SECTION("sum")
+          SECTION("inner operators")
           {
-            Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0<Dimension, double> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  f[cell_id]     = 2 * x + y + 1;
+                });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
-          }
+              DiscreteFunctionP0<Dimension, double> g{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  g[cell_id]     = std::abs((x + 1) * (x - 2) + y * (1 + y)) + 1;
+                });
 
-          SECTION("difference")
-          {
-            Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<double> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<double> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<double> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
+
+              SECTION("ratio")
+              {
+                Array<double> ratio_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+
+                REQUIRE(same_values(f / g, ratio_values));
+                REQUIRE(same_values(const_f / g, ratio_values));
+                REQUIRE(same_values(f / const_g, ratio_values));
+                REQUIRE(same_values(const_f / const_g, ratio_values));
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyVector<VectorDimension> X{x, 2 - x};
+                  f[cell_id] = 2 * X + TinyVector<2>{1, 2};
+                });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
-          }
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyVector<VectorDimension> X{3 * x + 1, 2 + x};
+                  g[cell_id] = X;
+                });
 
-          SECTION("product")
-          {
-            Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
-          }
-        }
-      }
+              SECTION("sum")
+              {
+                Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
 
-      SECTION("external operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              f[cell_id]     = std::abs(2 * x) + 1;
-            });
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
 
-          const double a = 3;
+              SECTION("difference")
+              {
+                Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
 
-          SECTION("sum")
-          {
+            SECTION("matrix functions")
             {
-              Array<double> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+              constexpr std::uint64_t MatrixDimension = 2;
 
-              REQUIRE(same_values(a + f, sum_values));
-              REQUIRE(same_values(a + const_f, sum_values));
-            }
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyMatrix<MatrixDimension> A{x, 2 - x, 2 * x, x * x - 3};
+                  f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
+                });
 
-              REQUIRE(same_values(f + a, sum_values));
-              REQUIRE(same_values(const_f + a, sum_values));
-            }
-          }
-
-          SECTION("difference")
-          {
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = a - f[cell_id]; });
-              REQUIRE(same_values(a - f, difference_values));
-              REQUIRE(same_values(a - const_f, difference_values));
-            }
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyMatrix<MatrixDimension> A{3 * x + 1, 2 + x, 1 - 2 * x, 2 * x * x};
+                  g[cell_id] = A;
+                });
 
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - a; });
-              REQUIRE(same_values(f - a, difference_values));
-              REQUIRE(same_values(const_f - a, difference_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
             }
           }
 
-          SECTION("product")
+          SECTION("external operators")
           {
+            SECTION("scalar functions")
             {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-            {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
-
-              REQUIRE(same_values(f * a, product_values));
-              REQUIRE(same_values(const_f * a, product_values));
-            }
-
-            {
-              Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-              const TinyVector<3> v{1, 2, 3};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v; });
-
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
-
-            {
-              Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+              DiscreteFunctionP0<Dimension, double> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
-                  v[cell_id]     = TinyVector<3>{x, 2 * x, 1 - x};
+                  const double y = xj[cell_id][1];
+                  f[cell_id]     = std::abs(2 * x + y) + 1;
                 });
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
-
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
-
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              const TinyMatrix<2> A{1, 2, 3, 4};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
-
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
-
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+              const double a = 3;
+
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+
+              SECTION("sum")
+              {
+                {
+                  Array<double> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+
+                  REQUIRE(same_values(a + f, sum_values));
+                  REQUIRE(same_values(a + const_f, sum_values));
+                }
+                {
+                  Array<double> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+
+                  REQUIRE(same_values(f + a, sum_values));
+                  REQUIRE(same_values(const_f + a, sum_values));
+                }
+              }
+
+              SECTION("difference")
+              {
+                {
+                  Array<double> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = a - f[cell_id]; });
+                  REQUIRE(same_values(a - f, difference_values));
+                  REQUIRE(same_values(a - const_f, difference_values));
+                }
+
+                {
+                  Array<double> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - a; });
+                  REQUIRE(same_values(f - a, difference_values));
+                  REQUIRE(same_values(const_f - a, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  Array<double> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+                {
+                  Array<double> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
+
+                  REQUIRE(same_values(f * a, product_values));
+                  REQUIRE(same_values(const_f * a, product_values));
+                }
+
+                {
+                  Array<TinyVector<3>> product_values{mesh->numberOfCells()};
+                  const TinyVector<3> v{1, 2, 3};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v; });
+
+                  REQUIRE(same_values(f * v, product_values));
+                  REQUIRE(same_values(const_f * v, product_values));
+                }
+
+                {
+                  Array<TinyVector<3>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      v[cell_id]     = TinyVector<3>{x, 2 * x, 1 - x};
+                    });
+
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+
+                  REQUIRE(same_values(f * v, product_values));
+                  REQUIRE(same_values(const_f * v, product_values));
+                }
+
+                {
+                  Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
+                  const TinyMatrix<2> A{1, 2, 3, 4};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+
+                {
+                  Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
+                    });
+
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+
+                  REQUIRE(same_values(f * M, product_values));
+                  REQUIRE(same_values(const_f * M, product_values));
+                }
+              }
+
+              SECTION("ratio")
+              {
+                {
+                  Array<double> ratio_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
+
+                  REQUIRE(same_values(a / f, ratio_values));
+                  REQUIRE(same_values(a / const_f, ratio_values));
+                }
+                {
+                  Array<double> ratio_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+
+                  REQUIRE(same_values(f / a, ratio_values));
+                  REQUIRE(same_values(const_f / a, ratio_values));
+                }
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
-                  M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
+                  const double y = xj[cell_id][1];
+                  const TinyVector<VectorDimension> X{x + y, 2 - x * y};
+                  f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
+
+                  REQUIRE(same_values(v + f, sum_values));
+                  REQUIRE(same_values(v + const_f, sum_values));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
+
+                  REQUIRE(same_values(f + v, sum_values));
+                  REQUIRE(same_values(const_f + v, sum_values));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+
+                  REQUIRE(same_values(v - f, difference_values));
+                  REQUIRE(same_values(v - const_f, difference_values));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+
+                  REQUIRE(same_values(f - v, difference_values));
+                  REQUIRE(same_values(const_f - v, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  const double a = 2.3;
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      a[cell_id]     = 2 * x * x - 1;
+                    });
+
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  const TinyMatrix<VectorDimension> A{1, 2, 3, 4};
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
+                    });
+
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(M * f, product_values));
+                  REQUIRE(same_values(M * const_f, product_values));
+                }
+              }
+            }
+
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  const TinyMatrix<MatrixDimension> X{x, 2 - y, x * y, y * 3};
+                  f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
+                });
 
-              REQUIRE(same_values(f * M, product_values));
-              REQUIRE(same_values(const_f * M, product_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+
+                  REQUIRE(same_values(A + f, sum_values));
+                  REQUIRE(same_values(A + const_f, sum_values));
+                }
+                {
+                  Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+
+                  REQUIRE(same_values(f + A, sum_values));
+                  REQUIRE(same_values(const_f + A, sum_values));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
+
+                  REQUIRE(same_values(A - f, difference_values));
+                  REQUIRE(same_values(A - const_f, difference_values));
+                }
+                {
+                  Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
+
+                  REQUIRE(same_values(f - A, difference_values));
+                  REQUIRE(same_values(const_f - A, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  const double a = 2.3;
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      a[cell_id]     = 2 * x * x - 1;
+                    });
+
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+              }
             }
           }
+        }
+      }
+    }
 
-          SECTION("ratio")
-          {
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
-
-              REQUIRE(same_values(a / f, ratio_values));
-              REQUIRE(same_values(a / const_f, ratio_values));
-            }
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
 
-              REQUIRE(same_values(f / a, ratio_values));
-              REQUIRE(same_values(const_f / a, ratio_values));
-            }
-          }
-        }
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        SECTION("vector functions")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          constexpr std::uint64_t VectorDimension = 2;
-
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyVector<VectorDimension> X{x, 2 - x};
-              f[cell_id] = 2 * X + TinyVector<2>{1, 2};
-            });
+          auto mesh = named_mesh.mesh();
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          SECTION("sum")
+          SECTION("inner operators")
           {
-            const TinyVector<VectorDimension> v{1, 2};
+            SECTION("scalar functions")
             {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, double> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  f[cell_id]     = 2 * x + y - z;
+                });
 
-              REQUIRE(same_values(v + f, sum_values));
-              REQUIRE(same_values(v + const_f, sum_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, double> g{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
-
-              REQUIRE(same_values(f + v, sum_values));
-              REQUIRE(same_values(const_f + v, sum_values));
-            }
-          }
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  g[cell_id]     = std::abs((x + 1) * (x - 2) + y * (1 + y) + 2 * z) + 1;
+                });
 
-          SECTION("difference")
-          {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+              DiscreteFunctionP0<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<double> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<double> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<double> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
+
+              SECTION("ratio")
+              {
+                Array<double> ratio_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+
+                REQUIRE(same_values(f / g, ratio_values));
+                REQUIRE(same_values(const_f / g, ratio_values));
+                REQUIRE(same_values(f / const_g, ratio_values));
+                REQUIRE(same_values(const_f / const_g, ratio_values));
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyVector<VectorDimension> X{x, 2 - x};
+                  f[cell_id] = 2 * X + TinyVector<2>{1, 2};
+                });
 
-              REQUIRE(same_values(v - f, difference_values));
-              REQUIRE(same_values(v - const_f, difference_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyVector<VectorDimension> X{3 * x + 1, 2 + x};
+                  g[cell_id] = X;
+                });
 
-              REQUIRE(same_values(f - v, difference_values));
-              REQUIRE(same_values(const_f - v, difference_values));
-            }
-          }
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
 
-          SECTION("product")
-          {
-            {
-              const double a = 2.3;
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+              SECTION("sum")
+              {
+                Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
             }
 
+            SECTION("matrix functions")
             {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
+              constexpr std::uint64_t MatrixDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
+                  const TinyMatrix<MatrixDimension> A{x, 2 - x, 2 * x, x * x - 3};
+                  f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
                 });
 
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
               parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const TinyMatrix<MatrixDimension> A{3 * x + 1, 2 + x, 1 - 2 * x, 2 * x * x};
+                  g[cell_id] = A;
+                });
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+
+              SECTION("sum")
+              {
+                Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+
+              SECTION("product")
+              {
+                Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                parallel_for(
+                  mesh->numberOfCells(),
+                  PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+
+                REQUIRE(same_values(f * g, product_values));
+                REQUIRE(same_values(const_f * g, product_values));
+                REQUIRE(same_values(f * const_g, product_values));
+                REQUIRE(same_values(const_f * const_g, product_values));
+              }
             }
+          }
 
+          SECTION("external operators")
+          {
+            SECTION("scalar functions")
             {
-              const TinyMatrix<VectorDimension> A{1, 2, 3, 4};
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+              DiscreteFunctionP0<Dimension, double> f{mesh};
               parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
-
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  f[cell_id]     = std::abs(2 * x + y * z) + 1;
+                });
 
-            {
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+              const double a = 3;
+
+              DiscreteFunctionP0<Dimension, const double> const_f = f;
+
+              SECTION("sum")
+              {
+                {
+                  Array<double> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+
+                  REQUIRE(same_values(a + f, sum_values));
+                  REQUIRE(same_values(a + const_f, sum_values));
+                }
+                {
+                  Array<double> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+
+                  REQUIRE(same_values(f + a, sum_values));
+                  REQUIRE(same_values(const_f + a, sum_values));
+                }
+              }
+
+              SECTION("difference")
+              {
+                {
+                  Array<double> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = a - f[cell_id]; });
+                  REQUIRE(same_values(a - f, difference_values));
+                  REQUIRE(same_values(a - const_f, difference_values));
+                }
+
+                {
+                  Array<double> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - a; });
+                  REQUIRE(same_values(f - a, difference_values));
+                  REQUIRE(same_values(const_f - a, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  Array<double> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+                {
+                  Array<double> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
+
+                  REQUIRE(same_values(f * a, product_values));
+                  REQUIRE(same_values(const_f * a, product_values));
+                }
+
+                {
+                  Array<TinyVector<3>> product_values{mesh->numberOfCells()};
+                  const TinyVector<3> v{1, 2, 3};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v; });
+
+                  REQUIRE(same_values(f * v, product_values));
+                  REQUIRE(same_values(const_f * v, product_values));
+                }
+
+                {
+                  Array<TinyVector<3>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      v[cell_id]     = TinyVector<3>{x, 2 * x, 1 - x};
+                    });
+
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+
+                  REQUIRE(same_values(f * v, product_values));
+                  REQUIRE(same_values(const_f * v, product_values));
+                }
+
+                {
+                  Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
+                  const TinyMatrix<2> A{1, 2, 3, 4};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+
+                {
+                  Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
+                    });
+
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+
+                  REQUIRE(same_values(f * M, product_values));
+                  REQUIRE(same_values(const_f * M, product_values));
+                }
+              }
+
+              SECTION("ratio")
+              {
+                {
+                  Array<double> ratio_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
+
+                  REQUIRE(same_values(a / f, ratio_values));
+                  REQUIRE(same_values(a / const_f, ratio_values));
+                }
+                {
+                  Array<double> ratio_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+
+                  REQUIRE(same_values(f / a, ratio_values));
+                  REQUIRE(same_values(const_f / a, ratio_values));
+                }
+              }
+            }
+
+            SECTION("vector functions")
+            {
+              constexpr std::uint64_t VectorDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
               parallel_for(
                 mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                   const double x = xj[cell_id][0];
-                  M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  const TinyVector<VectorDimension> X{x + y - z, 2 - x * y};
+                  f[cell_id] = 2 * X + TinyVector<2>{1, 2};
                 });
 
+              DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
+
+                  REQUIRE(same_values(v + f, sum_values));
+                  REQUIRE(same_values(v + const_f, sum_values));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
+
+                  REQUIRE(same_values(f + v, sum_values));
+                  REQUIRE(same_values(const_f + v, sum_values));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyVector<VectorDimension> v{1, 2};
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+
+                  REQUIRE(same_values(v - f, difference_values));
+                  REQUIRE(same_values(v - const_f, difference_values));
+                }
+                {
+                  Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+
+                  REQUIRE(same_values(f - v, difference_values));
+                  REQUIRE(same_values(const_f - v, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  const double a = 2.3;
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      a[cell_id]     = 2 * x * x - 1;
+                    });
+
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  const TinyMatrix<VectorDimension> A{1, 2, 3, 4};
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
+                  DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
+                    });
+
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(M * f, product_values));
+                  REQUIRE(same_values(M * const_f, product_values));
+                }
+              }
+            }
+
+            SECTION("matrix functions")
+            {
+              constexpr std::uint64_t MatrixDimension = 2;
+
+              DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
               parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  const TinyMatrix<MatrixDimension> X{x, 2 - y, x * y, y * z + 3};
+                  f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
+                });
 
-              REQUIRE(same_values(M * f, product_values));
-              REQUIRE(same_values(M * const_f, product_values));
+              DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+
+              SECTION("sum")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+
+                  REQUIRE(same_values(A + f, sum_values));
+                  REQUIRE(same_values(A + const_f, sum_values));
+                }
+                {
+                  Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+
+                  REQUIRE(same_values(f + A, sum_values));
+                  REQUIRE(same_values(const_f + A, sum_values));
+                }
+              }
+
+              SECTION("difference")
+              {
+                const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                {
+                  Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
+
+                  REQUIRE(same_values(A - f, difference_values));
+                  REQUIRE(same_values(A - const_f, difference_values));
+                }
+                {
+                  Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
+
+                  REQUIRE(same_values(f - A, difference_values));
+                  REQUIRE(same_values(const_f - A, difference_values));
+                }
+              }
+
+              SECTION("product")
+              {
+                {
+                  const double a = 2.3;
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  DiscreteFunctionP0<Dimension, double> a{mesh};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                      const double x = xj[cell_id][0];
+                      a[cell_id]     = 2 * x * x - 1;
+                    });
+
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(),
+                    PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+
+                  REQUIRE(same_values(a * f, product_values));
+                  REQUIRE(same_values(a * const_f, product_values));
+                }
+
+                {
+                  const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+
+                  REQUIRE(same_values(A * f, product_values));
+                  REQUIRE(same_values(A * const_f, product_values));
+                }
+
+                {
+                  const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
+                  Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+                  parallel_for(
+                    mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+
+                  REQUIRE(same_values(f * A, product_values));
+                  REQUIRE(same_values(const_f * A, product_values));
+                }
+              }
             }
           }
         }
+      }
+    }
+  }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+  SECTION("math functions")
+  {
+#define CHECK_STD_MATH_FUNCTION(data_expression, FCT)                           \
+  {                                                                             \
+    DiscreteFunctionP0 data   = data_expression;                                \
+    DiscreteFunctionP0 result = FCT(data);                                      \
+    bool is_same              = true;                                           \
+    parallel_for(data.cellValues().numberOfItems(), [&](const CellId cell_id) { \
+      if (result[cell_id] != std::FCT(data[cell_id])) {                         \
+        is_same = false;                                                        \
+      }                                                                         \
+    });                                                                         \
+    REQUIRE(is_same);                                                           \
+  }
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyMatrix<MatrixDimension> X{x, 2 - x, x * x, x * 3};
-              f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
-            });
+#define CHECK_STD_BINARY_MATH_FUNCTION(lhs_expression, rhs_expression, FCT)    \
+  {                                                                            \
+    DiscreteFunctionP0 lhs    = lhs_expression;                                \
+    DiscreteFunctionP0 rhs    = rhs_expression;                                \
+    DiscreteFunctionP0 result = FCT(lhs, rhs);                                 \
+    using namespace std;                                                       \
+    bool is_same = true;                                                       \
+    parallel_for(lhs.cellValues().numberOfItems(), [&](const CellId cell_id) { \
+      if (result[cell_id] != FCT(lhs[cell_id], rhs[cell_id])) {                \
+        is_same = false;                                                       \
+      }                                                                        \
+    });                                                                        \
+    REQUIRE(is_same);                                                          \
+  }
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+#define CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(lhs, rhs_expression, FCT) \
+  {                                                                             \
+    DiscreteFunctionP0 rhs    = rhs_expression;                                 \
+    DiscreteFunctionP0 result = FCT(lhs, rhs);                                  \
+    bool is_same              = true;                                           \
+    using namespace std;                                                        \
+    parallel_for(rhs.cellValues().numberOfItems(), [&](const CellId cell_id) {  \
+      if (result[cell_id] != FCT(lhs, rhs[cell_id])) {                          \
+        is_same = false;                                                        \
+      }                                                                         \
+    });                                                                         \
+    REQUIRE(is_same);                                                           \
+  }
 
-          SECTION("sum")
-          {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+#define CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(lhs_expression, rhs, FCT) \
+  {                                                                             \
+    DiscreteFunctionP0 lhs    = lhs_expression;                                 \
+    DiscreteFunctionP0 result = FCT(lhs, rhs);                                  \
+    bool is_same              = true;                                           \
+    using namespace std;                                                        \
+    parallel_for(lhs.cellValues().numberOfItems(), [&](const CellId cell_id) {  \
+      if (result[cell_id] != FCT(lhs[cell_id], rhs)) {                          \
+        is_same = false;                                                        \
+      }                                                                         \
+    });                                                                         \
+    REQUIRE(is_same);                                                           \
+  }
 
-              REQUIRE(same_values(A + f, sum_values));
-              REQUIRE(same_values(A + const_f, sum_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+    SECTION("1D")
+    {
+      constexpr size_t Dimension = 1;
+      std::array mesh_list       = MeshDataBaseForTests::get().all1DMeshes();
 
-              REQUIRE(same_values(f + A, sum_values));
-              REQUIRE(same_values(const_f + A, sum_values));
-            }
-          }
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
 
-          SECTION("difference")
-          {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-              REQUIRE(same_values(A - f, difference_values));
-              REQUIRE(same_values(A - const_f, difference_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
+          DiscreteFunctionP0<Dimension, double> positive_function{mesh};
 
-              REQUIRE(same_values(f - A, difference_values));
-              REQUIRE(same_values(const_f - A, difference_values));
+          parallel_for(
+            mesh->numberOfCells(),
+            PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); });
+
+          const double min_value = min(positive_function);
+          SECTION("min")
+          {
+            double local_min = std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_min = std::min(local_min, positive_function[cell_id]);
             }
+            REQUIRE(min_value == parallel::allReduceMin(local_min));
           }
 
-          SECTION("product")
+          const double max_value = max(positive_function);
+          SECTION("max")
           {
-            {
-              const double a = 2.3;
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
+            double local_max = -std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_max = std::max(local_max, positive_function[cell_id]);
             }
+            REQUIRE(max_value == parallel::allReduceMax(local_max));
+          }
 
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
-                });
-
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          REQUIRE(min_value < max_value);
 
-            {
-              const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+          DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value);
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+          SECTION("sqrt")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sqrt);
+          }
 
-            {
-              const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+          SECTION("abs")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, abs);
+          }
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+          SECTION("cos")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cos);
           }
-        }
-      }
-    }
 
-    SECTION("2D")
-    {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D();
+          SECTION("sin")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sin);
+          }
 
-      constexpr size_t Dimension = 2;
+          SECTION("tan")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tan);
+          }
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+          DiscreteFunctionP0<Dimension, double> unit_function{mesh};
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
           parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              f[cell_id]     = 2 * x + y + 1;
+            mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+              unit_function[cell_id] =
+                (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95;
             });
 
-          DiscreteFunctionP0<Dimension, double> g{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              g[cell_id]     = std::abs((x + 1) * (x - 2) + y * (1 + y)) + 1;
-            });
+          SECTION("acos")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, acos);
+          }
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
-          DiscreteFunctionP0<Dimension, const double> const_g{g};
+          SECTION("asin")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, asin);
+          }
 
-          SECTION("sum")
+          SECTION("atan")
           {
-            Array<double> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(unit_function, atan);
+          }
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+          SECTION("cosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cosh);
           }
 
-          SECTION("difference")
+          SECTION("sinh")
           {
-            Array<double> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, sinh);
+          }
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+          SECTION("tanh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tanh);
           }
 
-          SECTION("product")
+          SECTION("acosh")
           {
-            Array<double> product_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, acosh);
+          }
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
+          SECTION("asinh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, asinh);
           }
 
-          SECTION("ratio")
+          SECTION("atanh")
           {
-            Array<double> ratio_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(unit_function, atanh);
+          }
 
-            REQUIRE(same_values(f / g, ratio_values));
-            REQUIRE(same_values(const_f / g, ratio_values));
-            REQUIRE(same_values(f / const_g, ratio_values));
-            REQUIRE(same_values(const_f / const_g, ratio_values));
+          SECTION("exp")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, exp);
           }
-        }
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
+          SECTION("log")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, log);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyVector<VectorDimension> X{x, 2 - x};
-              f[cell_id] = 2 * X + TinyVector<2>{1, 2};
-            });
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyVector<VectorDimension> X{3 * x + 1, 2 + x};
-              g[cell_id] = X;
-            });
+          SECTION("max(0.2,vh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max);
+          }
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
+          SECTION("max(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max);
+          }
 
-          SECTION("sum")
+          SECTION("atan2(uh,hv)")
           {
-            Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2);
+          }
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+          SECTION("atan2(0.5,uh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2);
           }
 
-          SECTION("difference")
+          SECTION("atan2(uh,0.2)")
           {
-            Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2);
+          }
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+          SECTION("pow(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow);
           }
-        }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+          SECTION("pow(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyMatrix<MatrixDimension> A{x, 2 - x, 2 * x, x * x - 3};
-              f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
-            });
+          SECTION("pow(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyMatrix<MatrixDimension> A{3 * x + 1, 2 + x, 1 - 2 * x, 2 * x * x};
-              g[cell_id] = A;
-            });
+          SECTION("min(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min);
+          }
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+          SECTION("min(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min);
+          }
 
-          SECTION("sum")
+          SECTION("min(uh,0.2)")
           {
-            Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min);
+          }
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max);
           }
 
-          SECTION("difference")
+          SECTION("min(uh,0.5)")
           {
-            Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max);
+          }
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+          SECTION("min(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max);
           }
 
-          SECTION("product")
+          SECTION("dot(uh,hv)")
           {
-            Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
             parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
+
+            CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot);
           }
-        }
-      }
 
-      SECTION("external operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              f[cell_id]     = std::abs(2 * x + y) + 1;
-            });
+          SECTION("dot(uh,v)")
+          {
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
 
-          const double a = 3;
+            const TinyVector<2> v{1, 2};
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot);
+          }
 
-          SECTION("sum")
+          SECTION("dot(u,hv)")
           {
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+            const TinyVector<2> u{3, -2};
 
-              REQUIRE(same_values(a + f, sum_values));
-              REQUIRE(same_values(a + const_f, sum_values));
-            }
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-              REQUIRE(same_values(f + a, sum_values));
-              REQUIRE(same_values(const_f + a, sum_values));
-            }
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot);
           }
 
-          SECTION("difference")
+          SECTION("scalar sum")
           {
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = a - f[cell_id]; });
-              REQUIRE(same_values(a - f, difference_values));
-              REQUIRE(same_values(a - const_f, difference_values));
-            }
+            const CellValue<const double> cell_value = positive_function.cellValues();
 
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - a; });
-              REQUIRE(same_values(f - a, difference_values));
-              REQUIRE(same_values(const_f - a, difference_values));
-            }
+            REQUIRE(sum(cell_value) == sum(positive_function));
           }
 
-          SECTION("product")
+          SECTION("vector sum")
           {
-            {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-            {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
-
-              REQUIRE(same_values(f * a, product_values));
-              REQUIRE(same_values(const_f * a, product_values));
-            }
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
+            const CellValue<const TinyVector<2>> cell_value = uh.cellValues();
 
-            {
-              Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-              const TinyVector<3> v{1, 2, 3};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v; });
+            REQUIRE(sum(cell_value) == sum(uh));
+          }
 
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+          SECTION("matrix sum")
+          {
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1};
+              });
+            const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues();
 
-            {
-              Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  v[cell_id]     = TinyVector<3>{x, 2 * x, 1 - x};
-                });
+            REQUIRE(sum(cell_value) == sum(uh));
+          }
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+          SECTION("integrate scalar")
+          {
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+            CellValue<double> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id];
+              });
 
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              const TinyMatrix<2> A{1, 2, 3, 4};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+            REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value)));
+          }
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+          SECTION("integrate vector")
+          {
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
 
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
-                });
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+            CellValue<TinyVector<2>> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
 
-              REQUIRE(same_values(f * M, product_values));
-              REQUIRE(same_values(const_f * M, product_values));
-            }
+            REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0]));
+            REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1]));
           }
 
-          SECTION("ratio")
+          SECTION("integrate matrix")
           {
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x};
+              });
 
-              REQUIRE(same_values(a / f, ratio_values));
-              REQUIRE(same_values(a / const_f, ratio_values));
-            }
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-              REQUIRE(same_values(f / a, ratio_values));
-              REQUIRE(same_values(const_f / a, ratio_values));
-            }
+            CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
+
+            REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0)));
+            REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1)));
+            REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0)));
+            REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1)));
           }
         }
+      }
+    }
+
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
+
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        SECTION("vector functions")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          constexpr std::uint64_t VectorDimension = 2;
+          auto mesh = named_mesh.mesh();
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              const TinyVector<VectorDimension> X{x + y, 2 - x * y};
-              f[cell_id] = 2 * X + TinyVector<2>{1, 2};
-            });
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+          DiscreteFunctionP0<Dimension, double> positive_function{mesh};
 
-          SECTION("sum")
-          {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
+          parallel_for(
+            mesh->numberOfCells(),
+            PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); });
 
-              REQUIRE(same_values(v + f, sum_values));
-              REQUIRE(same_values(v + const_f, sum_values));
+          const double min_value = min(positive_function);
+          SECTION("min")
+          {
+            double local_min = std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_min = std::min(local_min, positive_function[cell_id]);
             }
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
+            REQUIRE(min_value == parallel::allReduceMin(local_min));
+          }
 
-              REQUIRE(same_values(f + v, sum_values));
-              REQUIRE(same_values(const_f + v, sum_values));
+          const double max_value = max(positive_function);
+          SECTION("max")
+          {
+            double local_max = -std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_max = std::max(local_max, positive_function[cell_id]);
             }
+            REQUIRE(max_value == parallel::allReduceMax(local_max));
           }
 
-          SECTION("difference")
-          {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+          REQUIRE(min_value < max_value);
 
-              REQUIRE(same_values(v - f, difference_values));
-              REQUIRE(same_values(v - const_f, difference_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+          DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value);
 
-              REQUIRE(same_values(f - v, difference_values));
-              REQUIRE(same_values(const_f - v, difference_values));
-            }
+          SECTION("sqrt")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sqrt);
           }
 
-          SECTION("product")
+          SECTION("abs")
           {
-            {
-              const double a = 2.3;
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
-
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
-                });
+            CHECK_STD_MATH_FUNCTION(positive_function, abs);
+          }
 
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+          SECTION("cos")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cos);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          SECTION("sin")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sin);
+          }
 
-            {
-              const TinyMatrix<VectorDimension> A{1, 2, 3, 4};
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+          SECTION("tan")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tan);
+          }
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+          DiscreteFunctionP0<Dimension, double> unit_function{mesh};
 
-            {
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
-                });
+          parallel_for(
+            mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+              unit_function[cell_id] =
+                (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95;
+            });
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+          SECTION("acos")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, acos);
+          }
 
-              REQUIRE(same_values(M * f, product_values));
-              REQUIRE(same_values(M * const_f, product_values));
-            }
+          SECTION("asin")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, asin);
           }
-        }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+          SECTION("atan")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, atan);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              const TinyMatrix<MatrixDimension> X{x, 2 - y, x * y, y * 3};
-              f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
-            });
+          SECTION("cosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cosh);
+          }
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+          SECTION("sinh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sinh);
+          }
 
-          SECTION("sum")
+          SECTION("tanh")
           {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, tanh);
+          }
 
-              REQUIRE(same_values(A + f, sum_values));
-              REQUIRE(same_values(A + const_f, sum_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+          SECTION("acosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, acosh);
+          }
 
-              REQUIRE(same_values(f + A, sum_values));
-              REQUIRE(same_values(const_f + A, sum_values));
-            }
+          SECTION("asinh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, asinh);
           }
 
-          SECTION("difference")
+          SECTION("atanh")
           {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(unit_function, atanh);
+          }
 
-              REQUIRE(same_values(A - f, difference_values));
-              REQUIRE(same_values(A - const_f, difference_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
+          SECTION("exp")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, exp);
+          }
 
-              REQUIRE(same_values(f - A, difference_values));
-              REQUIRE(same_values(const_f - A, difference_values));
-            }
+          SECTION("log")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, log);
           }
 
-          SECTION("product")
+          SECTION("max(uh,hv)")
           {
-            {
-              const double a = 2.3;
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          SECTION("max(0.2,vh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max);
+          }
 
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
-                });
+          SECTION("max(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max);
+          }
 
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+          SECTION("atan2(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          SECTION("atan2(0.5,uh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2);
+          }
 
-            {
-              const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+          SECTION("atan2(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2);
+          }
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+          SECTION("pow(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow);
+          }
 
-            {
-              const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+          SECTION("pow(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow);
+          }
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+          SECTION("pow(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow);
           }
-        }
-      }
-    }
 
-    SECTION("3D")
-    {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D();
+          SECTION("min(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min);
+          }
 
-      constexpr size_t Dimension = 3;
+          SECTION("min(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min);
+          }
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+          SECTION("min(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min);
+          }
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              const double z = xj[cell_id][2];
-              f[cell_id]     = 2 * x + y - z;
-            });
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max);
+          }
 
-          DiscreteFunctionP0<Dimension, double> g{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              const double z = xj[cell_id][2];
-              g[cell_id]     = std::abs((x + 1) * (x - 2) + y * (1 + y) + 2 * z) + 1;
-            });
+          SECTION("min(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max);
+          }
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
-          DiscreteFunctionP0<Dimension, const double> const_g{g};
+          SECTION("min(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max);
+          }
 
-          SECTION("sum")
+          SECTION("dot(uh,hv)")
           {
-            Array<double> sum_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
+
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
             parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+            CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot);
           }
 
-          SECTION("difference")
+          SECTION("dot(uh,v)")
           {
-            Array<double> difference_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
             parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
+
+            const TinyVector<2> v{1, 2};
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot);
           }
 
-          SECTION("product")
+          SECTION("dot(u,hv)")
           {
-            Array<double> product_values{mesh->numberOfCells()};
+            const TinyVector<2> u{3, -2};
+
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
             parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot);
           }
 
-          SECTION("ratio")
+          SECTION("scalar sum")
           {
-            Array<double> ratio_values{mesh->numberOfCells()};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / g[cell_id]; });
+            const CellValue<const double> cell_value = positive_function.cellValues();
 
-            REQUIRE(same_values(f / g, ratio_values));
-            REQUIRE(same_values(const_f / g, ratio_values));
-            REQUIRE(same_values(f / const_g, ratio_values));
-            REQUIRE(same_values(const_f / const_g, ratio_values));
+            REQUIRE(sum(cell_value) == sum(positive_function));
           }
-        }
-
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyVector<VectorDimension> X{x, 2 - x};
-              f[cell_id] = 2 * X + TinyVector<2>{1, 2};
-            });
-
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> g{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyVector<VectorDimension> X{3 * x + 1, 2 + x};
-              g[cell_id] = X;
-            });
-
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_g{g};
-
-          SECTION("sum")
+          SECTION("vector sum")
           {
-            Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
             parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
+            const CellValue<const TinyVector<2>> cell_value = uh.cellValues();
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+            REQUIRE(sum(cell_value) == sum(uh));
           }
 
-          SECTION("difference")
+          SECTION("matrix sum")
           {
-            Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
             parallel_for(
-              mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1};
+              });
+            const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues();
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            REQUIRE(sum(cell_value) == sum(uh));
           }
-        }
-
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyMatrix<MatrixDimension> A{x, 2 - x, 2 * x, x * x - 3};
-              f[cell_id] = 2 * A + TinyMatrix<2>{1, 2, 3, 4};
-            });
+          SECTION("integrate scalar")
+          {
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> g{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const TinyMatrix<MatrixDimension> A{3 * x + 1, 2 + x, 1 - 2 * x, 2 * x * x};
-              g[cell_id] = A;
-            });
+            CellValue<double> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id];
+              });
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_g{g};
+            REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value)));
+          }
 
-          SECTION("sum")
+          SECTION("integrate vector")
           {
-            Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
             parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + g[cell_id]; });
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
-          }
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-          SECTION("difference")
-          {
-            Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
+            CellValue<TinyVector<2>> cell_value{mesh->connectivity()};
             parallel_for(
               mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - g[cell_id]; });
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0]));
+            REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1]));
           }
 
-          SECTION("product")
+          SECTION("integrate matrix")
           {
-            Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x};
+              });
+
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
+
+            CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()};
             parallel_for(
               mesh->numberOfCells(),
-              PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * g[cell_id]; });
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
 
-            REQUIRE(same_values(f * g, product_values));
-            REQUIRE(same_values(const_f * g, product_values));
-            REQUIRE(same_values(f * const_g, product_values));
-            REQUIRE(same_values(const_f * const_g, product_values));
+            REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0)));
+            REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1)));
+            REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0)));
+            REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1)));
           }
         }
       }
+    }
 
-      SECTION("external operators")
-      {
-        SECTION("scalar functions")
-        {
-          DiscreteFunctionP0<Dimension, double> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              const double z = xj[cell_id][2];
-              f[cell_id]     = std::abs(2 * x + y * z) + 1;
-            });
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
 
-          const double a = 3;
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-          DiscreteFunctionP0<Dimension, const double> const_f = f;
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
 
-          SECTION("sum")
-          {
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = a + f[cell_id]; });
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-              REQUIRE(same_values(a + f, sum_values));
-              REQUIRE(same_values(a + const_f, sum_values));
-            }
-            {
-              Array<double> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + a; });
+          DiscreteFunctionP0<Dimension, double> positive_function{mesh};
+
+          parallel_for(
+            mesh->numberOfCells(),
+            PUGS_LAMBDA(const CellId cell_id) { positive_function[cell_id] = 1 + std::abs(xj[cell_id][0]); });
 
-              REQUIRE(same_values(f + a, sum_values));
-              REQUIRE(same_values(const_f + a, sum_values));
+          const double min_value = min(positive_function);
+          SECTION("min")
+          {
+            double local_min = std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_min = std::min(local_min, positive_function[cell_id]);
             }
+            REQUIRE(min_value == parallel::allReduceMin(local_min));
           }
 
-          SECTION("difference")
+          const double max_value = max(positive_function);
+          SECTION("max")
           {
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = a - f[cell_id]; });
-              REQUIRE(same_values(a - f, difference_values));
-              REQUIRE(same_values(a - const_f, difference_values));
+            double local_max = -std::numeric_limits<double>::max();
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              local_max = std::max(local_max, positive_function[cell_id]);
             }
+            REQUIRE(max_value == parallel::allReduceMax(local_max));
+          }
 
-            {
-              Array<double> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - a; });
-              REQUIRE(same_values(f - a, difference_values));
-              REQUIRE(same_values(const_f - a, difference_values));
-            }
+          REQUIRE(min_value < max_value);
+
+          DiscreteFunctionP0 unsigned_function = positive_function - 0.5 * (min_value + max_value);
+
+          SECTION("sqrt")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sqrt);
           }
 
-          SECTION("product")
+          SECTION("abs")
           {
-            {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, abs);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
-            {
-              Array<double> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * a; });
+          SECTION("cos")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cos);
+          }
 
-              REQUIRE(same_values(f * a, product_values));
-              REQUIRE(same_values(const_f * a, product_values));
-            }
+          SECTION("sin")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sin);
+          }
 
-            {
-              Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-              const TinyVector<3> v{1, 2, 3};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v; });
+          SECTION("tan")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tan);
+          }
 
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+          DiscreteFunctionP0<Dimension, double> unit_function{mesh};
 
-            {
-              Array<TinyVector<3>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyVector<3>> v{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  v[cell_id]     = TinyVector<3>{x, 2 * x, 1 - x};
-                });
+          parallel_for(
+            mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+              unit_function[cell_id] =
+                (2 * (positive_function[cell_id] - min_value) / (max_value - min_value) - 1) * 0.95;
+            });
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * v[cell_id]; });
+          SECTION("acos")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, acos);
+          }
 
-              REQUIRE(same_values(f * v, product_values));
-              REQUIRE(same_values(const_f * v, product_values));
-            }
+          SECTION("asin")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, asin);
+          }
 
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              const TinyMatrix<2> A{1, 2, 3, 4};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+          SECTION("atan")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, atan);
+          }
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+          SECTION("cosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, cosh);
+          }
 
-            {
-              Array<TinyMatrix<2>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<2>> M{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
-                });
+          SECTION("sinh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, sinh);
+          }
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * M[cell_id]; });
+          SECTION("tanh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, tanh);
+          }
 
-              REQUIRE(same_values(f * M, product_values));
-              REQUIRE(same_values(const_f * M, product_values));
-            }
+          SECTION("acosh")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, acosh);
           }
 
-          SECTION("ratio")
+          SECTION("asinh")
           {
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = a / f[cell_id]; });
+            CHECK_STD_MATH_FUNCTION(positive_function, asinh);
+          }
 
-              REQUIRE(same_values(a / f, ratio_values));
-              REQUIRE(same_values(a / const_f, ratio_values));
-            }
-            {
-              Array<double> ratio_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { ratio_values[cell_id] = f[cell_id] / a; });
+          SECTION("atanh")
+          {
+            CHECK_STD_MATH_FUNCTION(unit_function, atanh);
+          }
 
-              REQUIRE(same_values(f / a, ratio_values));
-              REQUIRE(same_values(const_f / a, ratio_values));
-            }
+          SECTION("exp")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, exp);
           }
-        }
 
-        SECTION("vector functions")
-        {
-          constexpr std::uint64_t VectorDimension = 2;
+          SECTION("log")
+          {
+            CHECK_STD_MATH_FUNCTION(positive_function, log);
+          }
 
-          DiscreteFunctionP0<Dimension, TinyVector<VectorDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              const double z = xj[cell_id][2];
-              const TinyVector<VectorDimension> X{x + y - z, 2 - x * y};
-              f[cell_id] = 2 * X + TinyVector<2>{1, 2};
-            });
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(cos(positive_function), sin(positive_function), max);
+          }
 
-          DiscreteFunctionP0<Dimension, const TinyVector<VectorDimension>> const_f = f;
+          SECTION("max(0.2,vh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.2, sin(positive_function), max);
+          }
 
-          SECTION("sum")
+          SECTION("max(uh,0.2)")
           {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = v + f[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(cos(positive_function), 0.2, max);
+          }
 
-              REQUIRE(same_values(v + f, sum_values));
-              REQUIRE(same_values(v + const_f, sum_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + v; });
+          SECTION("atan2(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 2 + positive_function, atan2);
+          }
 
-              REQUIRE(same_values(f + v, sum_values));
-              REQUIRE(same_values(const_f + v, sum_values));
-            }
+          SECTION("atan2(0.5,uh)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, 2 + positive_function, atan2);
           }
 
-          SECTION("difference")
+          SECTION("atan2(uh,0.2)")
           {
-            const TinyVector<VectorDimension> v{1, 2};
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = v - f[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(2 + cos(positive_function), 0.2, atan2);
+          }
 
-              REQUIRE(same_values(v - f, difference_values));
-              REQUIRE(same_values(v - const_f, difference_values));
-            }
-            {
-              Array<TinyVector<VectorDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - v; });
+          SECTION("pow(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(positive_function, 0.5 * positive_function, pow);
+          }
 
-              REQUIRE(same_values(f - v, difference_values));
-              REQUIRE(same_values(const_f - v, difference_values));
-            }
+          SECTION("pow(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, positive_function, pow);
           }
 
-          SECTION("product")
+          SECTION("pow(uh,0.2)")
           {
-            {
-              const double a = 2.3;
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(positive_function, 1.3, pow);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          SECTION("min(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), min);
+          }
 
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
-                });
+          SECTION("min(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.5, cos(positive_function), min);
+          }
 
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+          SECTION("min(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.5, min);
+          }
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+          SECTION("max(uh,hv)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION(sin(positive_function), cos(positive_function), max);
+          }
 
-            {
-              const TinyMatrix<VectorDimension> A{1, 2, 3, 4};
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+          SECTION("min(uh,0.5)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(0.1, cos(positive_function), max);
+          }
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+          SECTION("min(uh,0.2)")
+          {
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(sin(positive_function), 0.1, max);
+          }
 
-            {
-              Array<TinyVector<VectorDimension>> product_values{mesh->numberOfCells()};
-              DiscreteFunctionP0<Dimension, TinyMatrix<VectorDimension>> M{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  M[cell_id]     = TinyMatrix<2>{x, 2 * x, 1 - x, 2 - x * x};
-                });
+          SECTION("dot(uh,hv)")
+          {
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
 
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = M[cell_id] * f[cell_id]; });
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-              REQUIRE(same_values(M * f, product_values));
-              REQUIRE(same_values(M * const_f, product_values));
-            }
+            CHECK_STD_BINARY_MATH_FUNCTION(uh, vh, dot);
           }
-        }
 
-        SECTION("matrix functions")
-        {
-          constexpr std::uint64_t MatrixDimension = 2;
+          SECTION("dot(uh,v)")
+          {
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
 
-          DiscreteFunctionP0<Dimension, TinyMatrix<MatrixDimension>> f{mesh};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              const double z = xj[cell_id][2];
-              const TinyMatrix<MatrixDimension> X{x, 2 - y, x * y, y * z + 3};
-              f[cell_id] = 2 * X + TinyMatrix<2>{1, 2, 3, 4};
-            });
+            const TinyVector<2> v{1, 2};
 
-          DiscreteFunctionP0<Dimension, const TinyMatrix<MatrixDimension>> const_f = f;
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_RHS_VALUE(uh, v, dot);
+          }
 
-          SECTION("sum")
+          SECTION("dot(u,hv)")
           {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = A + f[cell_id]; });
+            const TinyVector<2> u{3, -2};
 
-              REQUIRE(same_values(A + f, sum_values));
-              REQUIRE(same_values(A + const_f, sum_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> sum_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { sum_values[cell_id] = f[cell_id] + A; });
+            DiscreteFunctionP0<Dimension, TinyVector<2>> vh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                vh[cell_id]    = TinyVector<2>{2.3 * x, 1 - x};
+              });
 
-              REQUIRE(same_values(f + A, sum_values));
-              REQUIRE(same_values(const_f + A, sum_values));
-            }
+            CHECK_STD_BINARY_MATH_FUNCTION_WITH_LHS_VALUE(u, vh, dot);
           }
 
-          SECTION("difference")
+          SECTION("scalar sum")
           {
-            const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = A - f[cell_id]; });
+            const CellValue<const double> cell_value = positive_function.cellValues();
 
-              REQUIRE(same_values(A - f, difference_values));
-              REQUIRE(same_values(A - const_f, difference_values));
-            }
-            {
-              Array<TinyMatrix<MatrixDimension>> difference_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { difference_values[cell_id] = f[cell_id] - A; });
+            REQUIRE(sum(cell_value) == sum(positive_function));
+          }
 
-              REQUIRE(same_values(f - A, difference_values));
-              REQUIRE(same_values(const_f - A, difference_values));
-            }
+          SECTION("vector sum")
+          {
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
+            const CellValue<const TinyVector<2>> cell_value = uh.cellValues();
+
+            REQUIRE(sum(cell_value) == sum(uh));
           }
 
-          SECTION("product")
+          SECTION("matrix sum")
           {
-            {
-              const double a = 2.3;
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a * f[cell_id]; });
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 3 * x - 1};
+              });
+            const CellValue<const TinyMatrix<2>> cell_value = uh.cellValues();
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+            REQUIRE(sum(cell_value) == sum(uh));
+          }
 
-            {
-              DiscreteFunctionP0<Dimension, double> a{mesh};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                  const double x = xj[cell_id][0];
-                  a[cell_id]     = 2 * x * x - 1;
-                });
+          SECTION("integrate scalar")
+          {
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(),
-                PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = a[cell_id] * f[cell_id]; });
+            CellValue<double> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                cell_value[cell_id] = cell_volume[cell_id] * positive_function[cell_id];
+              });
 
-              REQUIRE(same_values(a * f, product_values));
-              REQUIRE(same_values(a * const_f, product_values));
-            }
+            REQUIRE(integrate(positive_function) == Catch::Approx(sum(cell_value)));
+          }
 
-            {
-              const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = A * f[cell_id]; });
+          SECTION("integrate vector")
+          {
+            DiscreteFunctionP0<Dimension, TinyVector<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyVector<2>{x + 1, 2 * x - 3};
+              });
 
-              REQUIRE(same_values(A * f, product_values));
-              REQUIRE(same_values(A * const_f, product_values));
-            }
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
 
-            {
-              const TinyMatrix<MatrixDimension> A{1, 2, 3, 4};
-              Array<TinyMatrix<MatrixDimension>> product_values{mesh->numberOfCells()};
-              parallel_for(
-                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) { product_values[cell_id] = f[cell_id] * A; });
+            CellValue<TinyVector<2>> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
 
-              REQUIRE(same_values(f * A, product_values));
-              REQUIRE(same_values(const_f * A, product_values));
-            }
+            REQUIRE(integrate(uh)[0] == Catch::Approx(sum(cell_value)[0]));
+            REQUIRE(integrate(uh)[1] == Catch::Approx(sum(cell_value)[1]));
+          }
+
+          SECTION("integrate matrix")
+          {
+            DiscreteFunctionP0<Dimension, TinyMatrix<2>> uh{mesh};
+            parallel_for(
+              mesh->numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+                const double x = xj[cell_id][0];
+                uh[cell_id]    = TinyMatrix<2>{x + 1, 2 * x - 3, 2 * x, 1 - x};
+              });
+
+            const CellValue<const double> cell_volume = MeshDataManager::instance().getMeshData(*mesh).Vj();
+
+            CellValue<TinyMatrix<2>> cell_value{mesh->connectivity()};
+            parallel_for(
+              mesh->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = cell_volume[cell_id] * uh[cell_id]; });
+
+            REQUIRE(integrate(uh)(0, 0) == Catch::Approx(sum(cell_value)(0, 0)));
+            REQUIRE(integrate(uh)(0, 1) == Catch::Approx(sum(cell_value)(0, 1)));
+            REQUIRE(integrate(uh)(1, 0) == Catch::Approx(sum(cell_value)(1, 0)));
+            REQUIRE(integrate(uh)(1, 1) == Catch::Approx(sum(cell_value)(1, 1)));
           }
         }
       }
@@ -2318,57 +3529,81 @@ TEST_CASE("DiscreteFunctionP0", "[scheme]")
       {
         constexpr size_t Dimension = 1;
 
-        std::shared_ptr mesh_1 = MeshDataBaseForTests::get().cartesianMesh1D();
-        std::shared_ptr mesh_2 =
-          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
+        std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_1 = named_mesh.mesh();
+
+            std::shared_ptr mesh_2 =
+              std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
 
-        DiscreteFunctionP0<Dimension, double> f1{mesh_1};
-        DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+            DiscreteFunctionP0<Dimension, double> f1{mesh_1};
+            DiscreteFunctionP0<Dimension, double> f2{mesh_2};
 
-        REQUIRE_THROWS_AS(f1 = f2, AssertError);
-        REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
-        REQUIRE_THROWS_AS(f1 + f2, AssertError);
-        REQUIRE_THROWS_AS(f1 - f2, AssertError);
-        REQUIRE_THROWS_AS(f1 * f2, AssertError);
-        REQUIRE_THROWS_AS(f1 / f2, AssertError);
+            REQUIRE_THROWS_AS(f1 = f2, AssertError);
+            REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
+            REQUIRE_THROWS_AS(f1 + f2, AssertError);
+            REQUIRE_THROWS_AS(f1 - f2, AssertError);
+            REQUIRE_THROWS_AS(f1 * f2, AssertError);
+            REQUIRE_THROWS_AS(f1 / f2, AssertError);
+          }
+        }
       }
 
       SECTION("2D")
       {
         constexpr size_t Dimension = 2;
 
-        std::shared_ptr mesh_1 = MeshDataBaseForTests::get().cartesianMesh2D();
-        std::shared_ptr mesh_2 =
-          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
+        std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_1 = named_mesh.mesh();
+
+            std::shared_ptr mesh_2 =
+              std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
 
-        DiscreteFunctionP0<Dimension, double> f1{mesh_1};
-        DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+            DiscreteFunctionP0<Dimension, double> f1{mesh_1};
+            DiscreteFunctionP0<Dimension, double> f2{mesh_2};
 
-        REQUIRE_THROWS_AS(f1 = f2, AssertError);
-        REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
-        REQUIRE_THROWS_AS(f1 + f2, AssertError);
-        REQUIRE_THROWS_AS(f1 - f2, AssertError);
-        REQUIRE_THROWS_AS(f1 * f2, AssertError);
-        REQUIRE_THROWS_AS(f1 / f2, AssertError);
+            REQUIRE_THROWS_AS(f1 = f2, AssertError);
+            REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
+            REQUIRE_THROWS_AS(f1 + f2, AssertError);
+            REQUIRE_THROWS_AS(f1 - f2, AssertError);
+            REQUIRE_THROWS_AS(f1 * f2, AssertError);
+            REQUIRE_THROWS_AS(f1 / f2, AssertError);
+          }
+        }
       }
 
       SECTION("3D")
       {
         constexpr size_t Dimension = 3;
 
-        std::shared_ptr mesh_1 = MeshDataBaseForTests::get().cartesianMesh3D();
-        std::shared_ptr mesh_2 =
-          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
+        std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_1 = named_mesh.mesh();
+
+            std::shared_ptr mesh_2 =
+              std::make_shared<Mesh<Connectivity<Dimension>>>(mesh_1->shared_connectivity(), mesh_1->xr());
 
-        DiscreteFunctionP0<Dimension, double> f1{mesh_1};
-        DiscreteFunctionP0<Dimension, double> f2{mesh_2};
+            DiscreteFunctionP0<Dimension, double> f1{mesh_1};
+            DiscreteFunctionP0<Dimension, double> f2{mesh_2};
 
-        REQUIRE_THROWS_AS(f1 = f2, AssertError);
-        REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
-        REQUIRE_THROWS_AS(f1 + f2, AssertError);
-        REQUIRE_THROWS_AS(f1 - f2, AssertError);
-        REQUIRE_THROWS_AS(f1 * f2, AssertError);
-        REQUIRE_THROWS_AS(f1 / f2, AssertError);
+            REQUIRE_THROWS_AS(f1 = f2, AssertError);
+            REQUIRE_THROWS_AS(copy_to(f1, f2), AssertError);
+            REQUIRE_THROWS_AS(f1 + f2, AssertError);
+            REQUIRE_THROWS_AS(f1 - f2, AssertError);
+            REQUIRE_THROWS_AS(f1 * f2, AssertError);
+            REQUIRE_THROWS_AS(f1 / f2, AssertError);
+          }
+        }
       }
     }
   }
diff --git a/tests/test_DiscreteFunctionP0Vector.cpp b/tests/test_DiscreteFunctionP0Vector.cpp
index c004ba5732c41dce1af56d3074b9c038b3a1bf8c..6e351c404a7b5f57d7b39e5a3f70d43cf62a5635 100644
--- a/tests/test_DiscreteFunctionP0Vector.cpp
+++ b/tests/test_DiscreteFunctionP0Vector.cpp
@@ -27,123 +27,144 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesianMesh1D();
       constexpr size_t Dimension = 1;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(f.size() == size);
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      REQUIRE(f.mesh().get() == mesh.get());
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          REQUIRE(f.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
+          REQUIRE(f.size() == size);
 
-      DiscreteFunctionP0Vector g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(g.size() == size);
+          REQUIRE(f.mesh().get() == mesh.get());
 
-      CellArray<double> h_arrays{mesh->connectivity(), size};
-      h_arrays.fill(0);
+          DiscreteFunctionP0Vector g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
+          REQUIRE(g.size() == size);
 
-      DiscreteFunctionP0Vector zero{mesh, [&] {
-                                      CellArray<double> cell_array{mesh->connectivity(), size};
-                                      cell_array.fill(0);
-                                      return cell_array;
-                                    }()};
+          CellArray<double> h_arrays{mesh->connectivity(), size};
+          h_arrays.fill(0);
 
-      DiscreteFunctionP0Vector h{mesh, h_arrays};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_arrays));
+          DiscreteFunctionP0Vector zero{mesh, [&] {
+                                          CellArray<double> cell_array{mesh->connectivity(), size};
+                                          cell_array.fill(0);
+                                          return cell_array;
+                                        }()};
 
-      h_arrays.fill(1);
+          DiscreteFunctionP0Vector h{mesh, h_arrays};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
 
-      REQUIRE(same_values(h, h_arrays));
-      REQUIRE(not same_values(h, zero));
+          h_arrays.fill(1);
 
-      DiscreteFunctionP0Vector moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_arrays));
+          REQUIRE(same_values(h, h_arrays));
+          REQUIRE(not same_values(h, zero));
+
+          DiscreteFunctionP0Vector moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_arrays));
+        }
+      }
     }
 
     SECTION("2D")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesianMesh2D();
       constexpr size_t Dimension = 2;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(f.size() == size);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      REQUIRE(f.mesh().get() == mesh.get());
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          REQUIRE(f.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
+          REQUIRE(f.size() == size);
 
-      DiscreteFunctionP0Vector g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(g.size() == size);
+          REQUIRE(f.mesh().get() == mesh.get());
 
-      CellArray<double> h_arrays{mesh->connectivity(), size};
-      h_arrays.fill(0);
+          DiscreteFunctionP0Vector g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
+          REQUIRE(g.size() == size);
 
-      DiscreteFunctionP0Vector zero{mesh, [&] {
-                                      CellArray<double> cell_array{mesh->connectivity(), size};
-                                      cell_array.fill(0);
-                                      return cell_array;
-                                    }()};
+          CellArray<double> h_arrays{mesh->connectivity(), size};
+          h_arrays.fill(0);
 
-      DiscreteFunctionP0Vector h{mesh, h_arrays};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_arrays));
+          DiscreteFunctionP0Vector zero{mesh, [&] {
+                                          CellArray<double> cell_array{mesh->connectivity(), size};
+                                          cell_array.fill(0);
+                                          return cell_array;
+                                        }()};
 
-      h_arrays.fill(1);
+          DiscreteFunctionP0Vector h{mesh, h_arrays};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
 
-      REQUIRE(same_values(h, h_arrays));
-      REQUIRE(not same_values(h, zero));
+          h_arrays.fill(1);
 
-      DiscreteFunctionP0Vector moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_arrays));
+          REQUIRE(same_values(h, h_arrays));
+          REQUIRE(not same_values(h, zero));
+
+          DiscreteFunctionP0Vector moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_arrays));
+        }
+      }
     }
 
     SECTION("3D")
     {
       const size_t size = 2;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesianMesh3D();
       constexpr size_t Dimension = 3;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      REQUIRE(f.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(f.size() == size);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          REQUIRE(f.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(f.descriptor().type() == DiscreteFunctionType::P0Vector);
+          REQUIRE(f.size() == size);
 
-      REQUIRE(f.mesh().get() == mesh.get());
+          REQUIRE(f.mesh().get() == mesh.get());
 
-      DiscreteFunctionP0Vector g{f};
-      REQUIRE(g.dataType() == ASTNodeDataType::double_t);
-      REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
-      REQUIRE(g.size() == size);
+          DiscreteFunctionP0Vector g{f};
+          REQUIRE(g.dataType() == ASTNodeDataType::double_t);
+          REQUIRE(g.descriptor().type() == DiscreteFunctionType::P0Vector);
+          REQUIRE(g.size() == size);
 
-      CellArray<double> h_arrays{mesh->connectivity(), size};
-      h_arrays.fill(0);
+          CellArray<double> h_arrays{mesh->connectivity(), size};
+          h_arrays.fill(0);
 
-      DiscreteFunctionP0Vector zero{mesh, [&] {
-                                      CellArray<double> cell_array{mesh->connectivity(), size};
-                                      cell_array.fill(0);
-                                      return cell_array;
-                                    }()};
+          DiscreteFunctionP0Vector zero{mesh, [&] {
+                                          CellArray<double> cell_array{mesh->connectivity(), size};
+                                          cell_array.fill(0);
+                                          return cell_array;
+                                        }()};
 
-      DiscreteFunctionP0Vector h{mesh, h_arrays};
-      REQUIRE(same_values(h, zero));
-      REQUIRE(same_values(h, h_arrays));
+          DiscreteFunctionP0Vector h{mesh, h_arrays};
+          REQUIRE(same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
 
-      h_arrays.fill(1);
+          h_arrays.fill(1);
 
-      REQUIRE(same_values(h, h_arrays));
-      REQUIRE(not same_values(h, zero));
+          REQUIRE(same_values(h, h_arrays));
+          REQUIRE(not same_values(h, zero));
 
-      DiscreteFunctionP0Vector moved_h{std::move(h)};
-      REQUIRE(same_values(moved_h, h_arrays));
+          DiscreteFunctionP0Vector moved_h{std::move(h)};
+          REQUIRE(same_values(moved_h, h_arrays));
+        }
+      }
     }
   }
 
@@ -166,39 +187,60 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesianMesh1D();
-      constexpr size_t Dimension = 1;
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      f.fill(3);
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh                  = named_mesh.mesh();
+          constexpr size_t Dimension = 1;
+
+          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          f.fill(3);
 
-      REQUIRE(all_values_equal(f, 3));
+          REQUIRE(all_values_equal(f, 3));
+        }
+      }
     }
 
     SECTION("2D")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesianMesh2D();
       constexpr size_t Dimension = 2;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      f.fill(2.3);
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          f.fill(2.3);
 
-      REQUIRE(all_values_equal(f, 2.3));
+          REQUIRE(all_values_equal(f, 2.3));
+        }
+      }
     }
 
     SECTION("3D")
     {
       const size_t size = 2;
 
-      std::shared_ptr mesh       = MeshDataBaseForTests::get().cartesianMesh3D();
       constexpr size_t Dimension = 3;
 
-      DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-      f.fill(3.2);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+          f.fill(3.2);
 
-      REQUIRE(all_values_equal(f, 3.2));
+          REQUIRE(all_values_equal(f, 3.2));
+        }
+      }
     }
   }
 
@@ -220,623 +262,688 @@ TEST_CASE("DiscreteFunctionP0Vector", "[scheme]")
 
     SECTION("1D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D();
-
       constexpr size_t Dimension = 1;
 
-      const size_t size  = 3;
-      const size_t value = parallel::rank() + 1;
-      const size_t zero  = 0;
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          const size_t size  = 3;
+          const size_t value = parallel::rank() + 1;
+          const size_t zero  = 0;
 
-      DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
-      f.fill(value);
+          DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
+          f.fill(value);
 
-      REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(f, value));
 
-      DiscreteFunctionP0Vector g = copy(f);
-      f.fill(zero);
+          DiscreteFunctionP0Vector g = copy(f);
+          f.fill(zero);
 
-      REQUIRE(all_values_equal(f, zero));
-      REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(f, zero));
+          REQUIRE(all_values_equal(g, value));
 
-      copy_to(g, f);
-      g.fill(zero);
+          copy_to(g, f);
+          g.fill(zero);
 
-      DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
+          DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
 
-      DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
-      shallow_g = g;
+          DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
+          shallow_g = g;
 
-      REQUIRE(all_values_equal(f, value));
-      REQUIRE(all_values_equal(g, zero));
-      REQUIRE(all_values_equal(shallow_g, zero));
-      REQUIRE(all_values_equal(h, value));
+          REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(g, zero));
+          REQUIRE(all_values_equal(shallow_g, zero));
+          REQUIRE(all_values_equal(h, value));
 
-      copy_to(h, g);
+          copy_to(h, g);
 
-      REQUIRE(all_values_equal(g, value));
-      REQUIRE(all_values_equal(shallow_g, value));
+          REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(shallow_g, value));
+        }
+      }
     }
 
     SECTION("2D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D();
-
       constexpr size_t Dimension = 2;
+      std::array mesh_list       = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
 
-      const size_t size  = 3;
-      const size_t value = parallel::rank() + 1;
-      const size_t zero  = 0;
+          const size_t size  = 3;
+          const size_t value = parallel::rank() + 1;
+          const size_t zero  = 0;
 
-      DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
-      f.fill(value);
+          DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
+          f.fill(value);
 
-      REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(f, value));
 
-      DiscreteFunctionP0Vector g = copy(f);
-      f.fill(zero);
+          DiscreteFunctionP0Vector g = copy(f);
+          f.fill(zero);
 
-      REQUIRE(all_values_equal(f, zero));
-      REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(f, zero));
+          REQUIRE(all_values_equal(g, value));
 
-      copy_to(g, f);
-      g.fill(zero);
+          copy_to(g, f);
+          g.fill(zero);
 
-      DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
+          DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
 
-      DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
-      shallow_g = g;
+          DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
+          shallow_g = g;
 
-      REQUIRE(all_values_equal(f, value));
-      REQUIRE(all_values_equal(g, zero));
-      REQUIRE(all_values_equal(shallow_g, zero));
-      REQUIRE(all_values_equal(h, value));
+          REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(g, zero));
+          REQUIRE(all_values_equal(shallow_g, zero));
+          REQUIRE(all_values_equal(h, value));
 
-      copy_to(h, g);
+          copy_to(h, g);
 
-      REQUIRE(all_values_equal(g, value));
-      REQUIRE(all_values_equal(shallow_g, value));
+          REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(shallow_g, value));
+        }
+      }
     }
 
     SECTION("3D")
     {
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D();
-
       constexpr size_t Dimension = 3;
 
-      const size_t size  = 3;
-      const size_t value = parallel::rank() + 1;
-      const size_t zero  = 0;
-
-      DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
-      f.fill(value);
-
-      REQUIRE(all_values_equal(f, value));
-
-      DiscreteFunctionP0Vector g = copy(f);
-      f.fill(zero);
-
-      REQUIRE(all_values_equal(f, zero));
-      REQUIRE(all_values_equal(g, value));
-
-      copy_to(g, f);
-      g.fill(zero);
-
-      DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
-      shallow_g = g;
-
-      REQUIRE(all_values_equal(f, value));
-      REQUIRE(all_values_equal(g, zero));
-      REQUIRE(all_values_equal(h, value));
-
-      copy_to(h, g);
-
-      REQUIRE(all_values_equal(g, value));
-      REQUIRE(all_values_equal(shallow_g, value));
-    }
-  }
-
-  SECTION("unary operators")
-  {
-    SECTION("1D")
-    {
-      const size_t size    = 3;
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D();
-
-      constexpr size_t Dimension = 1;
-
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
 
-      SECTION("unary minus")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = 2 * x + i;
-            }
-          });
+          const size_t size  = 3;
+          const size_t value = parallel::rank() + 1;
+          const size_t zero  = 0;
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+          DiscreteFunctionP0Vector<Dimension, size_t> f{mesh, size};
+          f.fill(value);
 
-        Table<double> minus_values{mesh->numberOfCells(), size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            for (size_t i = 0; i < size; ++i) {
-              minus_values[cell_id][i] = -f[cell_id][i];
-            }
-          });
+          REQUIRE(all_values_equal(f, value));
 
-        REQUIRE(same_values(-f, minus_values));
-        REQUIRE(same_values(-const_f, minus_values));
-      }
-    }
+          DiscreteFunctionP0Vector g = copy(f);
+          f.fill(zero);
 
-    SECTION("2D")
-    {
-      const size_t size    = 3;
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D();
+          REQUIRE(all_values_equal(f, zero));
+          REQUIRE(all_values_equal(g, value));
 
-      constexpr size_t Dimension = 2;
+          copy_to(g, f);
+          g.fill(zero);
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      SECTION("unary minus")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            const double y = xj[cell_id][1];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = 2 * x + i * y;
-            }
-          });
+          DiscreteFunctionP0Vector<Dimension, const size_t> h = copy(f);
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+          DiscreteFunctionP0Vector<Dimension, size_t> shallow_g{mesh, size};
+          shallow_g = g;
 
-        Table<double> minus_values{mesh->numberOfCells(), size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            for (size_t i = 0; i < size; ++i) {
-              minus_values[cell_id][i] = -f[cell_id][i];
-            }
-          });
+          REQUIRE(all_values_equal(f, value));
+          REQUIRE(all_values_equal(g, zero));
+          REQUIRE(all_values_equal(h, value));
 
-        REQUIRE(same_values(-f, minus_values));
-        REQUIRE(same_values(-const_f, minus_values));
-      }
-    }
+          copy_to(h, g);
 
-    SECTION("3D")
-    {
-      const size_t size    = 2;
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D();
-
-      constexpr size_t Dimension = 3;
-
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
-
-      SECTION("unary minus")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            const double y = xj[cell_id][1];
-            const double z = xj[cell_id][2];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = 2 * x + i * y - z;
-            }
-          });
-
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-
-        Table<double> minus_values{mesh->numberOfCells(), size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            for (size_t i = 0; i < size; ++i) {
-              minus_values[cell_id][i] = -f[cell_id][i];
-            }
-          });
-
-        REQUIRE(same_values(-f, minus_values));
-        REQUIRE(same_values(-const_f, minus_values));
+          REQUIRE(all_values_equal(g, value));
+          REQUIRE(all_values_equal(shallow_g, value));
+        }
       }
     }
   }
 
-  SECTION("binary operators")
+  SECTION("unary operators")
   {
     SECTION("1D")
     {
       const size_t size = 3;
 
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh1D();
-
       constexpr size_t Dimension = 1;
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              f[cell_id][0]  = 2 * x + 1;
-              f[cell_id][1]  = x * x - 1;
-              f[cell_id][2]  = 2 + x;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              g[cell_id][0]  = (x + 1) * (x - 2) + 1;
-              g[cell_id][1]  = 3 * (x + 2) - 1;
-              g[cell_id][2]  = (x + 3) * 5;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-          DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
-
-          SECTION("sum")
+          auto mesh = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          SECTION("unary minus")
           {
-            Table<double> sum_values{mesh->numberOfCells(), size};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                const double x = xj[cell_id][0];
                 for (size_t i = 0; i < size; ++i) {
-                  sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                  f[cell_id][i] = 2 * x + i;
                 }
               });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
-          }
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
 
-          SECTION("difference")
-          {
-            Table<double> difference_values{mesh->numberOfCells(), size};
+            Table<double> minus_values{mesh->numberOfCells(), size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                  minus_values[cell_id][i] = -f[cell_id][i];
                 }
               });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            REQUIRE(same_values(-f, minus_values));
+            REQUIRE(same_values(-const_f, minus_values));
           }
         }
       }
+    }
 
-      SECTION("external operators")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = std::abs(2 * x) + i;
-            }
-          });
+    SECTION("2D")
+    {
+      const size_t size = 3;
+
+      constexpr size_t Dimension = 2;
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        SECTION("product")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          SECTION("scalar lhs")
-          {
-            const double a = 3.2;
-            Table<double> product_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a * f[cell_id][i];
-                }
-              });
+          auto mesh = named_mesh.mesh();
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
-          }
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-          SECTION("DiscreteFunctionP0 lhs")
+          SECTION("unary minus")
           {
-            DiscreteFunctionP0<Dimension, double> a{mesh};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
-                a[cell_id]     = 2 * x + 1;
+                const double y = xj[cell_id][1];
+                for (size_t i = 0; i < size; ++i) {
+                  f[cell_id][i] = 2 * x + i * y;
+                }
               });
 
-            Table<double> product_values{mesh->numberOfCells(), size};
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+
+            Table<double> minus_values{mesh->numberOfCells(), size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                  minus_values[cell_id][i] = -f[cell_id][i];
                 }
               });
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
-
-            DiscreteFunctionP0<Dimension, const double> const_a = a;
-            REQUIRE(same_values(const_a * f, product_values));
-            REQUIRE(same_values(const_a * const_f, product_values));
+            REQUIRE(same_values(-f, minus_values));
+            REQUIRE(same_values(-const_f, minus_values));
           }
         }
       }
     }
 
-    SECTION("2D")
+    SECTION("3D")
     {
-      const size_t size = 3;
-
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh2D();
+      const size_t size = 2;
 
-      constexpr size_t Dimension = 2;
+      constexpr size_t Dimension = 3;
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              f[cell_id][0]  = 2 * x + 1;
-              f[cell_id][1]  = x * x - y;
-              f[cell_id][2]  = 2 + x * y;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              g[cell_id][0]  = (x + 1) * (y - 2) + 1;
-              g[cell_id][1]  = 3 * (x + 2) - y;
-              g[cell_id][2]  = (x + 3) + 5 * y;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-          DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
-
-          SECTION("sum")
+          auto mesh = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          SECTION("unary minus")
           {
-            Table<double> sum_values{mesh->numberOfCells(), size};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                const double x = xj[cell_id][0];
+                const double y = xj[cell_id][1];
+                const double z = xj[cell_id][2];
                 for (size_t i = 0; i < size; ++i) {
-                  sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                  f[cell_id][i] = 2 * x + i * y - z;
                 }
               });
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
-          }
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
 
-          SECTION("difference")
-          {
-            Table<double> difference_values{mesh->numberOfCells(), size};
+            Table<double> minus_values{mesh->numberOfCells(), size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                  minus_values[cell_id][i] = -f[cell_id][i];
                 }
               });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            REQUIRE(same_values(-f, minus_values));
+            REQUIRE(same_values(-const_f, minus_values));
           }
         }
       }
+    }
+  }
 
-      SECTION("external operators")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            const double y = xj[cell_id][1];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = std::abs(2 * x) + i * y;
-            }
-          });
+  SECTION("binary operators")
+  {
+    SECTION("1D")
+    {
+      const size_t size = 3;
+
+      constexpr size_t Dimension = 1;
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-        SECTION("product")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          SECTION("scalar lhs")
-          {
-            const double a = 3.2;
-            Table<double> product_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a * f[cell_id][i];
-                }
-              });
+          auto mesh = named_mesh.mesh();
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          SECTION("inner operators")
+          {
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  f[cell_id][0]  = 2 * x + 1;
+                  f[cell_id][1]  = x * x - 1;
+                  f[cell_id][2]  = 2 + x;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  g[cell_id][0]  = (x + 1) * (x - 2) + 1;
+                  g[cell_id][1]  = 3 * (x + 2) - 1;
+                  g[cell_id][2]  = (x + 3) * 5;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+              DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Table<double> sum_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Table<double> difference_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
           }
 
-          SECTION("DiscreteFunctionP0 lhs")
+          SECTION("external operators")
           {
-            DiscreteFunctionP0<Dimension, double> a{mesh};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
-                const double y = xj[cell_id][1];
-                a[cell_id]     = 2 * x + 1 - y;
-              });
-
-            Table<double> product_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                  f[cell_id][i] = std::abs(2 * x) + i;
                 }
               });
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
-
-            DiscreteFunctionP0<Dimension, const double> const_a = a;
-            REQUIRE(same_values(const_a * f, product_values));
-            REQUIRE(same_values(const_a * const_f, product_values));
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+
+            SECTION("product")
+            {
+              SECTION("scalar lhs")
+              {
+                const double a = 3.2;
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+              }
+
+              SECTION("DiscreteFunctionP0 lhs")
+              {
+                DiscreteFunctionP0<Dimension, double> a{mesh};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    const double x = xj[cell_id][0];
+                    a[cell_id]     = 2 * x + 1;
+                  });
+
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+
+                DiscreteFunctionP0<Dimension, const double> const_a = a;
+                REQUIRE(same_values(const_a * f, product_values));
+                REQUIRE(same_values(const_a * const_f, product_values));
+              }
+            }
           }
         }
       }
     }
 
-    SECTION("3D")
+    SECTION("2D")
     {
-      const size_t size = 2;
-
-      std::shared_ptr mesh = MeshDataBaseForTests::get().cartesianMesh3D();
+      const size_t size = 3;
 
-      constexpr size_t Dimension = 3;
+      constexpr size_t Dimension = 2;
 
-      auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      SECTION("inner operators")
-      {
-        SECTION("scalar functions")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              const double z = xj[cell_id][2];
-              f[cell_id][0]  = 2 * x * z + 1;
-              f[cell_id][1]  = x * z - y;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
-          parallel_for(
-            mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-              const double x = xj[cell_id][0];
-              const double y = xj[cell_id][1];
-              const double z = xj[cell_id][2];
-              g[cell_id][0]  = (x + 1) * (y - 2) + 1 - z;
-              g[cell_id][1]  = 3 * (x + 2) - y * z;
-            });
-
-          DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
-          DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
-
-          SECTION("sum")
-          {
-            Table<double> sum_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                for (size_t i = 0; i < size; ++i) {
-                  sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
-                }
-              });
+          auto mesh = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
 
-            REQUIRE(same_values(f + g, sum_values));
-            REQUIRE(same_values(const_f + g, sum_values));
-            REQUIRE(same_values(f + const_g, sum_values));
-            REQUIRE(same_values(const_f + const_g, sum_values));
+          SECTION("inner operators")
+          {
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  f[cell_id][0]  = 2 * x + 1;
+                  f[cell_id][1]  = x * x - y;
+                  f[cell_id][2]  = 2 + x * y;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  g[cell_id][0]  = (x + 1) * (y - 2) + 1;
+                  g[cell_id][1]  = 3 * (x + 2) - y;
+                  g[cell_id][2]  = (x + 3) + 5 * y;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+              DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Table<double> sum_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Table<double> difference_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
           }
 
-          SECTION("difference")
+          SECTION("external operators")
           {
-            Table<double> difference_values{mesh->numberOfCells(), size};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                const double x = xj[cell_id][0];
+                const double y = xj[cell_id][1];
                 for (size_t i = 0; i < size; ++i) {
-                  difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                  f[cell_id][i] = std::abs(2 * x) + i * y;
                 }
               });
 
-            REQUIRE(same_values(f - g, difference_values));
-            REQUIRE(same_values(const_f - g, difference_values));
-            REQUIRE(same_values(f - const_g, difference_values));
-            REQUIRE(same_values(const_f - const_g, difference_values));
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+
+            SECTION("product")
+            {
+              SECTION("scalar lhs")
+              {
+                const double a = 3.2;
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+              }
+
+              SECTION("DiscreteFunctionP0 lhs")
+              {
+                DiscreteFunctionP0<Dimension, double> a{mesh};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    const double x = xj[cell_id][0];
+                    const double y = xj[cell_id][1];
+                    a[cell_id]     = 2 * x + 1 - y;
+                  });
+
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+
+                DiscreteFunctionP0<Dimension, const double> const_a = a;
+                REQUIRE(same_values(const_a * f, product_values));
+                REQUIRE(same_values(const_a * const_f, product_values));
+              }
+            }
           }
         }
       }
+    }
 
-      SECTION("external operators")
-      {
-        DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
-        parallel_for(
-          mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-            const double x = xj[cell_id][0];
-            const double y = xj[cell_id][1];
-            const double z = xj[cell_id][2];
-            for (size_t i = 0; i < size; ++i) {
-              f[cell_id][i] = std::abs(2 * x) + i * y + z;
-            }
-          });
+    SECTION("3D")
+    {
+      const size_t size = 2;
+
+      constexpr size_t Dimension = 3;
 
-        DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        SECTION("product")
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          SECTION("scalar lhs")
-          {
-            const double a = 3.2;
-            Table<double> product_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-                for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a * f[cell_id][i];
-                }
-              });
+          auto mesh = named_mesh.mesh();
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
+          auto xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          SECTION("inner operators")
+          {
+            SECTION("scalar functions")
+            {
+              DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  f[cell_id][0]  = 2 * x * z + 1;
+                  f[cell_id][1]  = x * z - y;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, double> g{mesh, size};
+              parallel_for(
+                mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                  const double x = xj[cell_id][0];
+                  const double y = xj[cell_id][1];
+                  const double z = xj[cell_id][2];
+                  g[cell_id][0]  = (x + 1) * (y - 2) + 1 - z;
+                  g[cell_id][1]  = 3 * (x + 2) - y * z;
+                });
+
+              DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+              DiscreteFunctionP0Vector<Dimension, const double> const_g{g};
+
+              SECTION("sum")
+              {
+                Table<double> sum_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      sum_values[cell_id][i] = f[cell_id][i] + g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f + g, sum_values));
+                REQUIRE(same_values(const_f + g, sum_values));
+                REQUIRE(same_values(f + const_g, sum_values));
+                REQUIRE(same_values(const_f + const_g, sum_values));
+              }
+
+              SECTION("difference")
+              {
+                Table<double> difference_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      difference_values[cell_id][i] = f[cell_id][i] - g[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(f - g, difference_values));
+                REQUIRE(same_values(const_f - g, difference_values));
+                REQUIRE(same_values(f - const_g, difference_values));
+                REQUIRE(same_values(const_f - const_g, difference_values));
+              }
+            }
           }
 
-          SECTION("DiscreteFunctionP0 lhs")
+          SECTION("external operators")
           {
-            DiscreteFunctionP0<Dimension, double> a{mesh};
+            DiscreteFunctionP0Vector<Dimension, double> f{mesh, size};
             parallel_for(
               mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 const double x = xj[cell_id][0];
                 const double y = xj[cell_id][1];
                 const double z = xj[cell_id][2];
-                a[cell_id]     = 2 * x + 1 - y * z;
-              });
-
-            Table<double> product_values{mesh->numberOfCells(), size};
-            parallel_for(
-              mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
                 for (size_t i = 0; i < size; ++i) {
-                  product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                  f[cell_id][i] = std::abs(2 * x) + i * y + z;
                 }
               });
 
-            REQUIRE(same_values(a * f, product_values));
-            REQUIRE(same_values(a * const_f, product_values));
-
-            DiscreteFunctionP0<Dimension, const double> const_a = a;
-            REQUIRE(same_values(const_a * f, product_values));
-            REQUIRE(same_values(const_a * const_f, product_values));
+            DiscreteFunctionP0Vector<Dimension, const double> const_f = f;
+
+            SECTION("product")
+            {
+              SECTION("scalar lhs")
+              {
+                const double a = 3.2;
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+              }
+
+              SECTION("DiscreteFunctionP0 lhs")
+              {
+                DiscreteFunctionP0<Dimension, double> a{mesh};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    const double x = xj[cell_id][0];
+                    const double y = xj[cell_id][1];
+                    const double z = xj[cell_id][2];
+                    a[cell_id]     = 2 * x + 1 - y * z;
+                  });
+
+                Table<double> product_values{mesh->numberOfCells(), size};
+                parallel_for(
+                  mesh->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+                    for (size_t i = 0; i < size; ++i) {
+                      product_values[cell_id][i] = a[cell_id] * f[cell_id][i];
+                    }
+                  });
+
+                REQUIRE(same_values(a * f, product_values));
+                REQUIRE(same_values(a * const_f, product_values));
+
+                DiscreteFunctionP0<Dimension, const double> const_a = a;
+                REQUIRE(same_values(const_a * f, product_values));
+                REQUIRE(same_values(const_a * const_f, product_values));
+              }
+            }
           }
         }
       }
diff --git a/tests/test_DiscreteFunctionUtils.cpp b/tests/test_DiscreteFunctionUtils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3783a19c99cb67066565c507abfeca6ce0f5d61f
--- /dev/null
+++ b/tests/test_DiscreteFunctionUtils.cpp
@@ -0,0 +1,509 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <scheme/DiscreteFunctionP0.hpp>
+#include <scheme/DiscreteFunctionP0Vector.hpp>
+#include <scheme/DiscreteFunctionUtils.hpp>
+
+#include <mesh/CartesianMeshBuilder.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("DiscreteFunctionUtils", "[scheme]")
+{
+  SECTION("1D")
+  {
+    constexpr size_t Dimension = 1;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh = named_mesh.mesh();
+
+        std::shared_ptr mesh_copy =
+          std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+
+        SECTION("common mesh")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
+
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+
+          REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
+        }
+
+        SECTION("check discretization type")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+
+          std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
+          std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
+
+          REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0));
+          REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0));
+        }
+
+        SECTION("scalar function shallow copy")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^1 function shallow copy")
+        {
+          using DataType     = TinyVector<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^2 function shallow copy")
+        {
+          using DataType     = TinyVector<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^3 function shallow copy")
+        {
+          using DataType     = TinyVector<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^1x1 function shallow copy")
+        {
+          using DataType     = TinyMatrix<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^2x2 function shallow copy")
+        {
+          using DataType     = TinyMatrix<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^3x3 function shallow copy")
+        {
+          using DataType     = TinyMatrix<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+      }
+    }
+  }
+
+  SECTION("2D")
+  {
+    constexpr size_t Dimension = 2;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh = named_mesh.mesh();
+
+        std::shared_ptr mesh_copy =
+          std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+
+        SECTION("common mesh")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
+
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+
+          REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
+        }
+
+        SECTION("check discretization type")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+
+          std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
+          std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
+
+          REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0));
+          REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0));
+        }
+
+        SECTION("scalar function shallow copy")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^1 function shallow copy")
+        {
+          using DataType     = TinyVector<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^2 function shallow copy")
+        {
+          using DataType     = TinyVector<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^3 function shallow copy")
+        {
+          using DataType     = TinyVector<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^1x1 function shallow copy")
+        {
+          using DataType     = TinyMatrix<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^2x2 function shallow copy")
+        {
+          using DataType     = TinyMatrix<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^3x3 function shallow copy")
+        {
+          using DataType     = TinyMatrix<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+      }
+    }
+  }
+
+  SECTION("3D")
+  {
+    constexpr size_t Dimension = 3;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh = named_mesh.mesh();
+
+        std::shared_ptr mesh_copy =
+          std::make_shared<std::decay_t<decltype(*mesh)>>(mesh->shared_connectivity(), mesh->xr());
+
+        SECTION("common mesh")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr wh = std::make_shared<DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh);
+
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+
+          REQUIRE(getCommonMesh({uh, vh, wh}).get() == mesh.get());
+          REQUIRE(getCommonMesh({uh, vh, wh, qh}).use_count() == 0);
+        }
+
+        SECTION("check discretization type")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+
+          std::shared_ptr qh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_copy);
+
+          std::shared_ptr Uh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
+          std::shared_ptr Vh = std::make_shared<DiscreteFunctionP0Vector<Dimension, double>>(mesh, 3);
+
+          REQUIRE(checkDiscretizationType({uh}, DiscreteFunctionType::P0));
+          REQUIRE(checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({uh, vh, qh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0Vector));
+          REQUIRE(not checkDiscretizationType({Uh, Vh}, DiscreteFunctionType::P0));
+          REQUIRE(not checkDiscretizationType({Uh}, DiscreteFunctionType::P0));
+        }
+
+        SECTION("scalar function shallow copy")
+        {
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^1 function shallow copy")
+        {
+          using DataType     = TinyVector<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^2 function shallow copy")
+        {
+          using DataType     = TinyVector<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^3 function shallow copy")
+        {
+          using DataType     = TinyVector<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^1x1 function shallow copy")
+        {
+          using DataType     = TinyMatrix<1>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^2x2 function shallow copy")
+        {
+          using DataType     = TinyMatrix<2>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+
+        SECTION("R^3x3 function shallow copy")
+        {
+          using DataType     = TinyMatrix<3>;
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, DataType>>(mesh);
+          std::shared_ptr vh = shallowCopy(mesh, uh);
+
+          REQUIRE(uh == vh);
+
+          std::shared_ptr wh = shallowCopy(mesh_copy, uh);
+
+          REQUIRE(uh != wh);
+          REQUIRE(&(uh->cellValues()[CellId{0}]) ==
+                  &(dynamic_cast<const DiscreteFunctionP0<Dimension, DataType>&>(*wh).cellValues()[CellId{0}]));
+        }
+      }
+    }
+  }
+
+  SECTION("errors")
+  {
+    SECTION("different connectivities")
+    {
+      constexpr size_t Dimension = 1;
+
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          std::shared_ptr other_mesh =
+            CartesianMeshBuilder{TinyVector<1>{-1}, TinyVector<1>{3}, TinyVector<1, size_t>{19}}.mesh();
+
+          std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh);
+
+          REQUIRE_THROWS_WITH(shallowCopy(other_mesh, uh), "error: cannot shallow copy when connectivity changes");
+        }
+      }
+    }
+
+    SECTION("incompatible mesh dimension")
+    {
+      constexpr size_t Dimension = 1;
+
+      std::shared_ptr mesh_1d = MeshDataBaseForTests::get().cartesian1DMesh();
+      std::shared_ptr mesh_2d = MeshDataBaseForTests::get().cartesian2DMesh();
+
+      std::shared_ptr uh = std::make_shared<DiscreteFunctionP0<Dimension, double>>(mesh_1d);
+
+      REQUIRE_THROWS_WITH(shallowCopy(mesh_2d, uh), "error: incompatible mesh dimensions");
+    }
+  }
+}
diff --git a/tests/test_DiscreteFunctionVectorInterpoler.cpp b/tests/test_DiscreteFunctionVectorInterpoler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..692dd8c6c18ad85f87f5c85dcc41a6bcb0838312
--- /dev/null
+++ b/tests/test_DiscreteFunctionVectorInterpoler.cpp
@@ -0,0 +1,458 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/ast/ASTBuilder.hpp>
+#include <language/ast/ASTModulesImporter.hpp>
+#include <language/ast/ASTNodeDataTypeBuilder.hpp>
+#include <language/ast/ASTNodeExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionEvaluationExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionExpressionBuilder.hpp>
+#include <language/ast/ASTNodeTypeCleaner.hpp>
+#include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/PugsFunctionAdapter.hpp>
+#include <language/utils/SymbolTable.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+
+#include <scheme/DiscreteFunctionDescriptorP0Vector.hpp>
+#include <scheme/DiscreteFunctionP0Vector.hpp>
+#include <scheme/DiscreteFunctionVectorInterpoler.hpp>
+
+#include <pegtl/string_input.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("DiscreteFunctionVectorInterpoler", "[scheme]")
+{
+  auto same_cell_value = [](const CellValue<const double>& fi, const size_t i, const auto& f) -> bool {
+    for (CellId cell_id = 0; cell_id < fi.numberOfItems(); ++cell_id) {
+      if (fi[cell_id] != f[cell_id][i]) {
+        return false;
+      }
+    }
+
+    return true;
+  };
+
+  auto register_function = [](const TAO_PEGTL_NAMESPACE::position& position,
+                              const std::shared_ptr<SymbolTable>& symbol_table, const std::string& name,
+                              std::vector<FunctionSymbolId>& function_id_list) {
+    auto [i_symbol, found] = symbol_table->find(name, position);
+    REQUIRE(found);
+    REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+    FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+    function_id_list.push_back(function_symbol_id);
+  };
+
+  SECTION("1D")
+  {
+    constexpr size_t Dimension = 1;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_1d = named_mesh.mesh();
+
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+        std::string_view data = R"(
+import math;
+let B_scalar_non_linear_1d: R^1 -> B, x -> (exp(2 * x[0]) + 3 > 4);
+let N_scalar_non_linear_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2);
+let Z_scalar_non_linear_1d: R^1 -> Z, x -> floor(exp(2 * x[0]) - 1);
+let R_scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        std::vector<FunctionSymbolId> function_id_list;
+        register_function(position, symbol_table, "B_scalar_non_linear_1d", function_id_list);
+        register_function(position, symbol_table, "N_scalar_non_linear_1d", function_id_list);
+        register_function(position, symbol_table, "Z_scalar_non_linear_1d", function_id_list);
+        register_function(position, symbol_table, "R_scalar_non_linear_1d", function_id_list);
+
+        DiscreteFunctionVectorInterpoler interpoler(mesh_1d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                    function_id_list);
+        std::shared_ptr discrete_function = interpoler.interpolate();
+
+        size_t i = 0;
+
+        {
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * x[0] * x[0] + 2);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[0]) - 1);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_1d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0]) + 3;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        REQUIRE(i == function_id_list.size());
+      }
+    }
+  }
+
+  SECTION("2D")
+  {
+    constexpr size_t Dimension = 2;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_2d = named_mesh.mesh();
+
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+
+        std::string_view data = R"(
+import math;
+let B_scalar_non_linear_2d: R^2 -> B, x -> (exp(2 * x[0]) + 3 > 4);
+let N_scalar_non_linear_2d: R^2 -> N, x -> floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
+let Z_scalar_non_linear_2d: R^2 -> Z, x -> floor(exp(2 * x[1]) - 1);
+let R_scalar_non_linear_2d: R^2 -> R, x -> 2 * exp(x[0] + x[1]) + 3;
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        std::vector<FunctionSymbolId> function_id_list;
+        register_function(position, symbol_table, "B_scalar_non_linear_2d", function_id_list);
+        register_function(position, symbol_table, "N_scalar_non_linear_2d", function_id_list);
+        register_function(position, symbol_table, "Z_scalar_non_linear_2d", function_id_list);
+        register_function(position, symbol_table, "R_scalar_non_linear_2d", function_id_list);
+
+        DiscreteFunctionVectorInterpoler interpoler(mesh_2d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                    function_id_list);
+        std::shared_ptr discrete_function = interpoler.interpolate();
+
+        size_t i = 0;
+
+        {
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0]) + 3 > 4;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[1]) - 1);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_2d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0] + x[1]) + 3;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        REQUIRE(i == function_id_list.size());
+      }
+    }
+  }
+
+  SECTION("3D")
+  {
+    constexpr size_t Dimension = 3;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
+
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+
+        std::string_view data = R"(
+import math;
+let B_scalar_non_linear_3d: R^3 -> B, x -> (exp(2 * x[0] + x[2]) + 3 > 4);
+let N_scalar_non_linear_3d: R^3 -> N, x -> floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
+let Z_scalar_non_linear_3d: R^3 -> Z, x -> floor(exp(2 * x[1]) - x[2]);
+let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        std::vector<FunctionSymbolId> function_id_list;
+        register_function(position, symbol_table, "B_scalar_non_linear_3d", function_id_list);
+        register_function(position, symbol_table, "N_scalar_non_linear_3d", function_id_list);
+        register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
+        register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
+
+        DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                    function_id_list);
+        std::shared_ptr discrete_function = interpoler.interpolate();
+
+        size_t i = 0;
+
+        {
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::exp(2 * x[0] + x[2]) + 3 > 4;
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(3 * (x[0] * x[1]) * (x[0] * x[1]) + 2);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = std::floor(std::exp(2 * x[1]) - x[2]);
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        {
+          CellValue<double> cell_value{mesh_3d->connectivity()};
+          parallel_for(
+            cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_value[cell_id]            = 2 * std::exp(x[0] + x[1]) + 3 * x[2];
+            });
+
+          REQUIRE(
+            same_cell_value(cell_value, i++,
+                            dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*discrete_function)));
+        }
+
+        REQUIRE(i == function_id_list.size());
+      }
+    }
+  }
+
+  SECTION("errors")
+  {
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
+
+        auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+
+        std::string_view data = R"(
+import math;
+let B_scalar_non_linear_2d: R^2 -> B, x -> (exp(2 * x[0] + x[1]) + 3 > 4);
+let N_scalar_non_linear_1d: R^1 -> N, x -> floor(3 * x[0] * x[0] + 2);
+let Z_scalar_non_linear_3d: R^3 -> Z, x -> floor(exp(2 * x[1]) - x[2]);
+let R_scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0] + x[1]) + 3 * x[2];
+let R2_scalar_non_linear_3d: R^3 -> R^2, x -> (2 * exp(x[0] + x[1]) + 3 * x[2], x[0] - x[1]);
+)";
+        TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+        auto ast = ASTBuilder::build(input);
+
+        ASTModulesImporter{*ast};
+        ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+        ASTSymbolTableBuilder{*ast};
+        ASTNodeDataTypeBuilder{*ast};
+
+        ASTNodeTypeCleaner<language::var_declaration>{*ast};
+        ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+        ASTNodeExpressionBuilder{*ast};
+
+        TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+        position.byte = data.size();   // ensure that variables are declared at this point
+
+        std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+        SECTION("invalid function type")
+        {
+          std::vector<FunctionSymbolId> function_id_list;
+          register_function(position, symbol_table, "B_scalar_non_linear_2d", function_id_list);
+          register_function(position, symbol_table, "N_scalar_non_linear_1d", function_id_list);
+          register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
+          register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
+
+          DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                      function_id_list);
+
+          const std::string error_msg = R"(error: invalid function type
+note: expecting R^3 -> R
+note: provided function B_scalar_non_linear_2d: R^2 -> B)";
+
+          REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
+        }
+
+        SECTION("invalid value type")
+        {
+          std::vector<FunctionSymbolId> function_id_list;
+          register_function(position, symbol_table, "Z_scalar_non_linear_3d", function_id_list);
+          register_function(position, symbol_table, "R_scalar_non_linear_3d", function_id_list);
+          register_function(position, symbol_table, "R2_scalar_non_linear_3d", function_id_list);
+
+          DiscreteFunctionVectorInterpoler interpoler(mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0Vector>(),
+                                                      function_id_list);
+
+          const std::string error_msg = R"(error: vector functions require scalar value type.
+Invalid interpolation value type: R^2)";
+
+          REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
+        }
+
+        SECTION("invalid discrete function type")
+        {
+          const std::string error_msg = "error: invalid discrete function type for vector interpolation";
+
+          DiscreteFunctionVectorInterpoler interpoler{mesh_3d, std::make_shared<DiscreteFunctionDescriptorP0>(), {}};
+          REQUIRE_THROWS_WITH(interpoler.interpolate(), error_msg);
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_Dual1DConnectivityBuilder.cpp b/tests/test_Dual1DConnectivityBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b194a92baa821d6da4bac1e6277aaea6c1ddb1b
--- /dev/null
+++ b/tests/test_Dual1DConnectivityBuilder.cpp
@@ -0,0 +1,98 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+
+template <ItemType item_type, typename ConnectivityType>
+inline auto
+get_item_ref_ids(const ConnectivityType& connectivity)
+{
+  std::map<std::string, size_t> ref_id_set;
+  for (size_t i = 0; i < connectivity.template numberOfRefItemList<item_type>(); ++i) {
+    const auto& ref_id_list = connectivity.template refItemList<item_type>(i);
+    std::ostringstream os;
+    os << ref_id_list.refId();
+    ItemValue<size_t, item_type> item_tag{connectivity};
+    item_tag.fill(0);
+    auto& list = ref_id_list.list();
+    for (size_t i_item = 0; i_item < ref_id_list.list().size(); ++i_item) {
+      item_tag[list[i_item]] = 1;
+    }
+
+    ref_id_set[os.str()] = sum(item_tag);
+  }
+  return ref_id_set;
+}
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("Dual1DConnectivityBuilder", "[mesh]")
+{
+  constexpr static size_t Dimension = 1;
+
+  using ConnectivityType = Connectivity<Dimension>;
+  using MeshType         = Mesh<ConnectivityType>;
+
+  std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh();
+  const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+  REQUIRE(primal_connectivity.numberOfNodes() == 35);
+  REQUIRE(primal_connectivity.numberOfCells() == 34);
+
+  std::shared_ptr p_dual_1d_connectivity =
+    DualConnectivityManager::instance().getDual1DConnectivity(primal_connectivity);
+  const ConnectivityType& dual_connectivity = *p_dual_1d_connectivity;
+
+  REQUIRE(dual_connectivity.numberOfNodes() == 36);
+  REQUIRE(dual_connectivity.numberOfCells() == 35);
+
+  SECTION("ref node list")
+  {
+    REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::node>() == 3);
+    REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::node>() == 2);
+
+    const auto primal_ref_node_list = get_item_ref_ids<ItemType::node>(primal_connectivity);
+    REQUIRE(primal_ref_node_list.size() == 3);
+
+    REQUIRE(primal_ref_node_list.count("INTERFACE(3)") == 1);
+    REQUIRE(primal_ref_node_list.count("XMAX(2)") == 1);
+    REQUIRE(primal_ref_node_list.count("XMIN(1)") == 1);
+
+    REQUIRE(primal_ref_node_list.at("INTERFACE(3)") == 1);
+    REQUIRE(primal_ref_node_list.at("XMAX(2)") == 1);
+    REQUIRE(primal_ref_node_list.at("XMIN(1)") == 1);
+
+    const auto dual_ref_node_list = get_item_ref_ids<ItemType::node>(dual_connectivity);
+    REQUIRE(dual_ref_node_list.size() == 2);
+
+    REQUIRE(dual_ref_node_list.count("XMAX(2)") == 1);
+    REQUIRE(dual_ref_node_list.count("XMIN(1)") == 1);
+
+    REQUIRE(dual_ref_node_list.at("XMAX(2)") == 1);
+    REQUIRE(dual_ref_node_list.at("XMIN(1)") == 1);
+  }
+
+  SECTION("ref cell list")
+  {
+    REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::cell>() == 2);
+    // dual 1d cell references are not computed
+    REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::cell>() == 0);
+
+    const auto primal_ref_cell_list = get_item_ref_ids<ItemType::cell>(primal_connectivity);
+    REQUIRE(primal_ref_cell_list.size() == 2);
+
+    REQUIRE(primal_ref_cell_list.count("LEFT(4)") == 1);
+    REQUIRE(primal_ref_cell_list.count("RIGHT(5)") == 1);
+
+    REQUIRE(primal_ref_cell_list.at("LEFT(4)") == 26);
+    REQUIRE(primal_ref_cell_list.at("RIGHT(5)") == 8);
+
+    const auto dual_ref_cell_list = get_item_ref_ids<ItemType::cell>(dual_connectivity);
+    REQUIRE(dual_ref_cell_list.size() == 0);
+  }
+}
diff --git a/tests/test_Dual1DMeshBuilder.cpp b/tests/test_Dual1DMeshBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9471fa8816b3a6f1e3a5f2f5b737d52320006bcf
--- /dev/null
+++ b/tests/test_Dual1DMeshBuilder.cpp
@@ -0,0 +1,106 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+
+#include <cmath>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("Dual1DMeshBuilder", "[mesh]")
+{
+  constexpr static size_t Dimension = 1;
+
+  using ConnectivityType = Connectivity<Dimension>;
+  using MeshType         = Mesh<ConnectivityType>;
+
+  std::shared_ptr<const MeshType> p_primal_mesh = MeshDataBaseForTests::get().unordered1DMesh();
+
+  const MeshType& primal_mesh = *p_primal_mesh;
+
+  REQUIRE(primal_mesh.numberOfNodes() == 35);
+  REQUIRE(primal_mesh.numberOfCells() == 34);
+
+  std::shared_ptr p_dual_1d_mesh = DualMeshManager::instance().getDual1DMesh(primal_mesh);
+  const MeshType& dual_mesh      = *p_dual_1d_mesh;
+
+  REQUIRE(dual_mesh.numberOfNodes() == 36);
+  REQUIRE(dual_mesh.numberOfCells() == 35);
+
+  auto cell_volume = MeshDataManager::instance().getMeshData(dual_mesh).Vj();
+  REQUIRE(Catch::Approx(sum(cell_volume)) == 2);
+
+  auto primal_xr = primal_mesh.xr();
+  auto dual_xr   = dual_mesh.xr();
+
+  NodeId primal_node_id = std::numeric_limits<NodeId::base_type>::max();
+  for (size_t i = 0; i < primal_mesh.connectivity().numberOfRefItemList<ItemType::node>(); ++i) {
+    auto ref_item_list = primal_mesh.connectivity().refItemList<ItemType::node>(0);
+    if (ref_item_list.refId().tagName() == "XMIN") {
+      REQUIRE(ref_item_list.list().size() == 1);
+      primal_node_id = ref_item_list.list()[0];
+    }
+  }
+
+  REQUIRE(primal_node_id != std::numeric_limits<NodeId::base_type>::max());
+
+  NodeId dual_node_id = std::numeric_limits<NodeId::base_type>::max();
+  for (size_t i = 0; i < dual_mesh.connectivity().numberOfRefItemList<ItemType::node>(); ++i) {
+    auto ref_item_list = dual_mesh.connectivity().refItemList<ItemType::node>(0);
+    if (ref_item_list.refId().tagName() == "XMIN") {
+      REQUIRE(ref_item_list.list().size() == 1);
+      dual_node_id = ref_item_list.list()[0];
+    }
+  }
+
+  REQUIRE(dual_node_id != std::numeric_limits<NodeId::base_type>::max());
+
+  REQUIRE(dual_xr[dual_node_id] == primal_xr[primal_node_id]);
+
+  auto primal_node_to_cell_matrix = primal_mesh.connectivity().nodeToCellMatrix();
+  auto dual_node_to_cell_matrix   = dual_mesh.connectivity().nodeToCellMatrix();
+
+  REQUIRE(primal_node_to_cell_matrix[primal_node_id].size() == 1);
+  REQUIRE(dual_node_to_cell_matrix[dual_node_id].size() == 1);
+
+  auto primal_xj = MeshDataManager::instance().getMeshData(primal_mesh).xj();
+
+  auto primal_cell_to_node_matrix = primal_mesh.connectivity().cellToNodeMatrix();
+  auto dual_cell_to_node_matrix   = dual_mesh.connectivity().cellToNodeMatrix();
+
+  CellId primal_cell_id = primal_node_to_cell_matrix[primal_node_id][0];
+  CellId dual_cell_id   = dual_node_to_cell_matrix[dual_node_id][0];
+
+  bool finished = false;
+  do {
+    NodeId right_dual_node_id   = dual_cell_to_node_matrix[dual_cell_id][1];
+    NodeId right_primal_node_id = primal_cell_to_node_matrix[primal_cell_id][1];
+    REQUIRE(right_dual_node_id != dual_node_id);
+    REQUIRE(primal_xj[primal_cell_id] == dual_xr[right_dual_node_id]);
+
+    dual_node_id = right_dual_node_id;
+    dual_cell_id = [&] {
+      const size_t i = (dual_node_to_cell_matrix[dual_node_id][0] == dual_cell_id) ? 1 : 0;
+      return dual_node_to_cell_matrix[dual_node_id][i];
+    }();
+
+    if (primal_node_to_cell_matrix[right_primal_node_id].size() == 1) {
+      right_dual_node_id = dual_cell_to_node_matrix[dual_cell_id][1];
+      REQUIRE(primal_xr[right_primal_node_id] == dual_xr[right_dual_node_id]);
+      finished = true;
+    } else {
+      primal_cell_id = [&] {
+        const size_t i = (primal_node_to_cell_matrix[right_primal_node_id][0] == primal_cell_id) ? 1 : 0;
+        return primal_node_to_cell_matrix[right_primal_node_id][i];
+      }();
+      primal_node_id = right_primal_node_id;
+    }
+  } while (not finished);
+}
diff --git a/tests/test_DualConnectivityManager.cpp b/tests/test_DualConnectivityManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..00e42e44fa592b4f1c1cbede7fb37b4b5ad64dfc
--- /dev/null
+++ b/tests/test_DualConnectivityManager.cpp
@@ -0,0 +1,136 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("DualConnectivityManager", "[mesh]")
+{
+  using ConnectivityType = Connectivity<2>;
+  using MeshType         = Mesh<ConnectivityType>;
+
+  std::shared_ptr<const MeshType> mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+  const ConnectivityType& connectivity = mesh->connectivity();
+
+  SECTION("diamond dual connectivity access")
+  {
+    std::shared_ptr p_diamond_dual_connectivity =
+      DualConnectivityManager::instance().getDiamondDualConnectivity(connectivity);
+
+    std::shared_ptr p_diamond_dual_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(connectivity);
+
+    const auto ref_counter        = p_diamond_dual_connectivity.use_count();
+    const auto ref_counter_mapper = p_diamond_dual_connectivity_mapper.use_count();
+
+    {
+      std::shared_ptr p_diamond_dual_connectivity2 =
+        DualConnectivityManager::instance().getDiamondDualConnectivity(connectivity);
+      std::shared_ptr p_diamond_dual_connectivity_mapper2 =
+        DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(connectivity);
+
+      REQUIRE(p_diamond_dual_connectivity == p_diamond_dual_connectivity2);
+      REQUIRE(p_diamond_dual_connectivity.use_count() == ref_counter + 1);
+      REQUIRE(p_diamond_dual_connectivity_mapper == p_diamond_dual_connectivity_mapper2);
+      REQUIRE(p_diamond_dual_connectivity_mapper.use_count() == ref_counter_mapper + 1);
+    }
+
+    REQUIRE(p_diamond_dual_connectivity.use_count() == ref_counter);
+
+    DualConnectivityManager::instance().deleteConnectivity(&connectivity);
+    REQUIRE(p_diamond_dual_connectivity.use_count() == ref_counter - 1);
+
+    // Can delete connectivity from the list again. This means that no
+    // dual connectivity associated with it is managed.
+    REQUIRE_NOTHROW(DualConnectivityManager::instance().deleteConnectivity(&connectivity));
+    REQUIRE(p_diamond_dual_connectivity.use_count() == ref_counter - 1);
+
+    // A new dual connectivity is built
+    std::shared_ptr p_diamond_dual_connectivity_rebuilt =
+      DualConnectivityManager::instance().getDiamondDualConnectivity(connectivity);
+    REQUIRE(p_diamond_dual_connectivity != p_diamond_dual_connectivity_rebuilt);
+    REQUIRE(p_diamond_dual_connectivity.get() != p_diamond_dual_connectivity_rebuilt.get());
+
+    // Exactly two references to the dual connectivity. One here and
+    // one in the manager.
+    REQUIRE(p_diamond_dual_connectivity_rebuilt.use_count() == 2);
+  }
+
+  SECTION("median dual connectivity access")
+  {
+    std::shared_ptr p_median_dual_connectivity =
+      DualConnectivityManager::instance().getMedianDualConnectivity(connectivity);
+
+    std::shared_ptr p_median_dual_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToMedianDualConnectivityDataMapper(connectivity);
+
+    const auto ref_counter        = p_median_dual_connectivity.use_count();
+    const auto ref_counter_mapper = p_median_dual_connectivity_mapper.use_count();
+
+    {
+      std::shared_ptr p_median_dual_connectivity2 =
+        DualConnectivityManager::instance().getMedianDualConnectivity(connectivity);
+
+      std::shared_ptr p_median_dual_connectivity_mapper2 =
+        DualConnectivityManager::instance().getPrimalToMedianDualConnectivityDataMapper(connectivity);
+
+      REQUIRE(p_median_dual_connectivity == p_median_dual_connectivity2);
+      REQUIRE(p_median_dual_connectivity.use_count() == ref_counter + 1);
+      REQUIRE(p_median_dual_connectivity_mapper == p_median_dual_connectivity_mapper2);
+      REQUIRE(p_median_dual_connectivity_mapper.use_count() == ref_counter_mapper + 1);
+    }
+
+    REQUIRE(p_median_dual_connectivity.use_count() == ref_counter);
+
+    DualConnectivityManager::instance().deleteConnectivity(&connectivity);
+    REQUIRE(p_median_dual_connectivity.use_count() == ref_counter - 1);
+
+    // Can delete connectivity from the list again. This means that no
+    // dual connectivity associated with it is managed.
+    REQUIRE_NOTHROW(DualConnectivityManager::instance().deleteConnectivity(&connectivity));
+    REQUIRE(p_median_dual_connectivity.use_count() == ref_counter - 1);
+
+    // A new dual connectivity is built
+    std::shared_ptr p_median_dual_connectivity_rebuilt =
+      DualConnectivityManager::instance().getMedianDualConnectivity(connectivity);
+    REQUIRE(p_median_dual_connectivity != p_median_dual_connectivity_rebuilt);
+    REQUIRE(p_median_dual_connectivity.get() != p_median_dual_connectivity_rebuilt.get());
+
+    // Exactly two references to the dual connectivity. One here and
+    // one in the manager.
+    REQUIRE(p_median_dual_connectivity_rebuilt.use_count() == 2);
+  }
+
+  SECTION("check multiple dual connectivities using/freeing")
+  {
+    std::shared_ptr p_median_dual_connectivity =
+      DualConnectivityManager::instance().getMedianDualConnectivity(connectivity);
+    std::shared_ptr p_diamond_dual_connectivity =
+      DualConnectivityManager::instance().getDiamondDualConnectivity(connectivity);
+
+    std::shared_ptr p_median_dual_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToMedianDualConnectivityDataMapper(connectivity);
+    std::shared_ptr p_diamond_dual_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(connectivity);
+
+    const auto median_ref_counter  = p_median_dual_connectivity.use_count();
+    const auto diamond_ref_counter = p_diamond_dual_connectivity.use_count();
+
+    const auto median_mapper_ref_counter  = p_median_dual_connectivity_mapper.use_count();
+    const auto diamond_mapper_ref_counter = p_diamond_dual_connectivity_mapper.use_count();
+
+    REQUIRE(p_median_dual_connectivity != p_diamond_dual_connectivity);
+    REQUIRE(size_t(p_median_dual_connectivity_mapper.get()) != size_t(p_diamond_dual_connectivity_mapper.get()));
+
+    REQUIRE_NOTHROW(DualConnectivityManager::instance().deleteConnectivity(&connectivity));
+    REQUIRE(p_median_dual_connectivity.use_count() == median_ref_counter - 1);
+    REQUIRE(p_diamond_dual_connectivity.use_count() == diamond_ref_counter - 1);
+    REQUIRE(p_median_dual_connectivity_mapper.use_count() == median_mapper_ref_counter - 1);
+    REQUIRE(p_diamond_dual_connectivity_mapper.use_count() == diamond_mapper_ref_counter - 1);
+  }
+}
diff --git a/tests/test_DualMeshManager.cpp b/tests/test_DualMeshManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1d05b996ae569ea30e995e5b1c8422b1edb9aeef
--- /dev/null
+++ b/tests/test_DualMeshManager.cpp
@@ -0,0 +1,159 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualMeshManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("DualMeshManager", "[mesh]")
+{
+  SECTION("same 1D dual connectivities ")
+  {
+    using ConnectivityType = Connectivity<1>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh = MeshDataBaseForTests::get().unordered1DMesh();
+
+    std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+    std::shared_ptr p_median_dual_mesh  = DualMeshManager::instance().getMedianDualMesh(*mesh);
+    std::shared_ptr p_dual1d_mesh       = DualMeshManager::instance().getDual1DMesh(*mesh);
+
+    // In 1d all these dual meshes are the same
+    REQUIRE(p_dual1d_mesh.get() == p_diamond_dual_mesh.get());
+    REQUIRE(p_dual1d_mesh.get() == p_median_dual_mesh.get());
+  }
+
+  SECTION("2D")
+  {
+    using ConnectivityType = Connectivity<2>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+
+    SECTION("diamond dual mesh access")
+    {
+      std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+
+      const auto ref_counter = p_diamond_dual_mesh.use_count();
+
+      {
+        std::shared_ptr p_diamond_dual_mesh2 = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+
+        REQUIRE(p_diamond_dual_mesh == p_diamond_dual_mesh2);
+        REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter + 1);
+      }
+
+      REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter);
+
+      DualMeshManager::instance().deleteMesh(mesh.get());
+      REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter - 1);
+
+      // Can delete mesh from the list again. This means that no
+      // dual mesh associated with it is managed.
+      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh.get()));
+      REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter - 1);
+
+      // A new dual mesh is built
+      std::shared_ptr p_diamond_dual_mesh_rebuilt = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+      REQUIRE(p_diamond_dual_mesh != p_diamond_dual_mesh_rebuilt);
+      REQUIRE(p_diamond_dual_mesh.get() != p_diamond_dual_mesh_rebuilt.get());
+
+      // Exactly two references to the dual mesh. One here and
+      // one in the manager.
+      REQUIRE(p_diamond_dual_mesh_rebuilt.use_count() == 2);
+    }
+
+    SECTION("median dual mesh access")
+    {
+      std::shared_ptr p_median_dual_mesh = DualMeshManager::instance().getMedianDualMesh(*mesh);
+
+      const auto ref_counter = p_median_dual_mesh.use_count();
+
+      {
+        std::shared_ptr p_median_dual_mesh2 = DualMeshManager::instance().getMedianDualMesh(*mesh);
+
+        REQUIRE(p_median_dual_mesh == p_median_dual_mesh2);
+        REQUIRE(p_median_dual_mesh.use_count() == ref_counter + 1);
+      }
+
+      REQUIRE(p_median_dual_mesh.use_count() == ref_counter);
+
+      DualMeshManager::instance().deleteMesh(mesh.get());
+      REQUIRE(p_median_dual_mesh.use_count() == ref_counter - 1);
+
+      // Can delete mesh from the list again. This means that no
+      // dual mesh associated with it is managed.
+      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh.get()));
+      REQUIRE(p_median_dual_mesh.use_count() == ref_counter - 1);
+
+      // A new dual mesh is built
+      std::shared_ptr p_median_dual_mesh_rebuilt = DualMeshManager::instance().getMedianDualMesh(*mesh);
+      REQUIRE(p_median_dual_mesh != p_median_dual_mesh_rebuilt);
+      REQUIRE(p_median_dual_mesh.get() != p_median_dual_mesh_rebuilt.get());
+
+      // Exactly two references to the dual mesh. One here and
+      // one in the manager.
+      REQUIRE(p_median_dual_mesh_rebuilt.use_count() == 2);
+    }
+
+    SECTION("check multiple dual mesh using/freeing")
+    {
+      std::shared_ptr p_median_dual_mesh  = DualMeshManager::instance().getMedianDualMesh(*mesh);
+      std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+
+      const auto median_ref_counter  = p_median_dual_mesh.use_count();
+      const auto diamond_ref_counter = p_diamond_dual_mesh.use_count();
+
+      REQUIRE(p_median_dual_mesh != p_diamond_dual_mesh);
+
+      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh.get()));
+      REQUIRE(p_median_dual_mesh.use_count() == median_ref_counter - 1);
+      REQUIRE(p_diamond_dual_mesh.use_count() == diamond_ref_counter - 1);
+    }
+  }
+
+  SECTION("3D")
+  {
+    using ConnectivityType = Connectivity<3>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+    SECTION("diamond dual mesh access")
+    {
+      std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+
+      const auto ref_counter = p_diamond_dual_mesh.use_count();
+
+      {
+        std::shared_ptr p_diamond_dual_mesh2 = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+
+        REQUIRE(p_diamond_dual_mesh == p_diamond_dual_mesh2);
+        REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter + 1);
+      }
+
+      REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter);
+
+      DualMeshManager::instance().deleteMesh(mesh.get());
+      REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter - 1);
+
+      // Can delete mesh from the list again. This means that no
+      // dual mesh associated with it is managed.
+      REQUIRE_NOTHROW(DualMeshManager::instance().deleteMesh(mesh.get()));
+      REQUIRE(p_diamond_dual_mesh.use_count() == ref_counter - 1);
+
+      // A new dual mesh is built
+      std::shared_ptr p_diamond_dual_mesh_rebuilt = DualMeshManager::instance().getDiamondDualMesh(*mesh);
+      REQUIRE(p_diamond_dual_mesh != p_diamond_dual_mesh_rebuilt);
+      REQUIRE(p_diamond_dual_mesh.get() != p_diamond_dual_mesh_rebuilt.get());
+
+      // Exactly two references to the dual mesh. One here and
+      // one in the manager.
+      REQUIRE(p_diamond_dual_mesh_rebuilt.use_count() == 2);
+    }
+  }
+}
diff --git a/tests/test_DualMeshType.cpp b/tests/test_DualMeshType.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0c5b932bacdad06e67500cfd7a0045c47f96cb20
--- /dev/null
+++ b/tests/test_DualMeshType.cpp
@@ -0,0 +1,14 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <mesh/DualMeshType.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("DualMeshType", "[mesh]")
+{
+  REQUIRE(name(DualMeshType::Diamond) == "diamond");
+  REQUIRE(name(DualMeshType::Dual1D) == "dual 1d");
+  REQUIRE(name(DualMeshType::Median) == "median");
+  REQUIRE_THROWS_WITH(name(DualMeshType{-1}), "unexpected error: unexpected dual mesh type");
+}
diff --git a/tests/test_EdgeIntegrator.cpp b/tests/test_EdgeIntegrator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5dfccbc6c7427a2bd27634c49957209c61c6f3ce
--- /dev/null
+++ b/tests/test_EdgeIntegrator.cpp
@@ -0,0 +1,764 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <algebra/TinyMatrix.hpp>
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/ItemValue.hpp>
+#include <mesh/Mesh.hpp>
+#include <scheme/EdgeIntegrator.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("EdgeIntegrator", "[scheme]")
+{
+  SECTION("scalar")
+  {
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> double {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1;
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        Array<const double> int_f_per_edge = [=] {
+          Array<double> int_f(mesh->numberOfEdges());
+          auto edge_to_node_matrix = mesh->connectivity().edgeToNodeMatrix();
+
+          parallel_for(
+            mesh->numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) {
+              auto edge_node_list = edge_to_node_matrix[edge_id];
+              auto xr             = mesh->xr();
+              double integral     = 0;
+
+              LineTransformation<3> T(xr[edge_node_list[0]], xr[edge_node_list[1]]);
+              auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
+              }
+
+              int_f[edge_id] = integral;
+            });
+
+          return int_f;
+        }();
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            SECTION("all edges")
+            {
+              SECTION("EdgeValue")
+              {
+                EdgeValue<double> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<double> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<double> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("edge list")
+            {
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<double> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<double> values =
+                  EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+
+          SECTION("tensorial formula")
+          {
+            SECTION("all edges")
+            {
+              SECTION("EdgeValue")
+              {
+                EdgeValue<double> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<double> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<double> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += std::abs(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("edge list")
+            {
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<double> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<double> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += std::abs(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("vector")
+  {
+    using R2 = TinyVector<2>;
+
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> R2 {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return R2{x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1, 2 * x + 3 * y - 1};
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        Array<const R2> int_f_per_edge = [=] {
+          Array<R2> int_f(mesh->numberOfEdges());
+          auto edge_to_node_matrix = mesh->connectivity().edgeToNodeMatrix();
+
+          parallel_for(
+            mesh->numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) {
+              auto edge_node_list = edge_to_node_matrix[edge_id];
+              auto xr             = mesh->xr();
+              R2 integral         = zero;
+
+              LineTransformation<3> T(xr[edge_node_list[0]], xr[edge_node_list[1]]);
+              auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
+              }
+
+              int_f[edge_id] = integral;
+            });
+
+          return int_f;
+        }();
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            SECTION("all edges")
+            {
+              SECTION("EdgeValue")
+              {
+                EdgeValue<R2> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("edge list")
+            {
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<R2> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<R2> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+
+          SECTION("tensorial formula")
+          {
+            SECTION("all edges")
+            {
+              SECTION("EdgeValue")
+              {
+                EdgeValue<R2> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("edge list")
+            {
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<R2> values = EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<R2> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("matrix")
+  {
+    using R2x2  = TinyMatrix<2>;
+    auto l2Norm = [](const R2x2& A) -> double {
+      return std::sqrt(A(0, 0) * A(0, 0) + A(1, 0) * A(1, 0) + A(0, 1) * A(0, 1) + A(1, 1) * A(1, 1));
+    };
+
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> R2x2 {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return R2x2{x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1, 2 * x + 3 * y - 1, 2 * x * x + 3 * x * y - z * z,
+                    3 - 2 * x + 3 * y};
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        Array<const R2x2> int_f_per_edge = [=] {
+          Array<R2x2> int_f(mesh->numberOfEdges());
+          auto edge_to_node_matrix = mesh->connectivity().edgeToNodeMatrix();
+
+          parallel_for(
+            mesh->numberOfEdges(), PUGS_LAMBDA(const EdgeId edge_id) {
+              auto edge_node_list = edge_to_node_matrix[edge_id];
+              auto xr             = mesh->xr();
+              R2x2 integral       = zero;
+
+              LineTransformation<3> T(xr[edge_node_list[0]], xr[edge_node_list[1]]);
+              auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+              for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                const auto& xi = qf.point(i);
+                integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
+              }
+
+              int_f[edge_id] = integral;
+            });
+
+          return int_f;
+        }();
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            SECTION("all edges")
+            {
+              SECTION("EdgeValue")
+              {
+                EdgeValue<R2x2> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2x2> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2x2> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("edge list")
+            {
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<R2x2> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<R2x2> values = EdgeIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+
+          SECTION("tensorial formula")
+          {
+            SECTION("all edges")
+            {
+              SECTION("EdgeValue")
+              {
+                EdgeValue<R2x2> values(mesh->connectivity());
+                EdgeIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2x2> values(mesh->numberOfEdges());
+
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2x2> values(mesh->numberOfEdges());
+                EdgeIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++edge_id) {
+                  error += l2Norm(int_f_per_edge[edge_id] - values[edge_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("edge list")
+            {
+              SECTION("Array")
+              {
+                Array<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                Array<R2x2> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<EdgeId> edge_list{mesh->numberOfEdges() / 2 + mesh->numberOfEdges() % 2};
+
+                {
+                  size_t k = 0;
+                  for (EdgeId edge_id = 0; edge_id < mesh->numberOfEdges(); ++(++edge_id), ++k) {
+                    edge_list[k] = edge_id;
+                  }
+
+                  REQUIRE(k == edge_list.size());
+                }
+
+                SmallArray<R2x2> values =
+                  EdgeIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, edge_list);
+
+                double error = 0;
+                for (size_t i = 0; i < edge_list.size(); ++i) {
+                  error += l2Norm(int_f_per_edge[edge_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp b/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a7f0cc0d74eb79f0897e2ea2a2a4b7212dbc306e
--- /dev/null
+++ b/tests/test_EmbeddedIDiscreteFunctionMathFunctions.cpp
@@ -0,0 +1,1635 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+
+#include <language/utils/EmbeddedIDiscreteFunctionMathFunctions.hpp>
+#include <scheme/DiscreteFunctionP0.hpp>
+#include <scheme/DiscreteFunctionP0Vector.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+#define CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(P_U, FCT)          \
+  {                                                                         \
+    using DiscreteFunctionType = const std::decay_t<decltype(*P_U)>;        \
+    std::shared_ptr p_fu       = ::FCT(P_U);                                \
+                                                                            \
+    REQUIRE(p_fu.use_count() > 0);                                          \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fu));      \
+                                                                            \
+    const auto& fu = dynamic_cast<const DiscreteFunctionType&>(*p_fu);      \
+                                                                            \
+    auto values  = P_U->cellValues();                                       \
+    bool is_same = true;                                                    \
+    for (CellId cell_id = 0; cell_id < values.numberOfItems(); ++cell_id) { \
+      if (fu[cell_id] != std::FCT(values[cell_id])) {                       \
+        is_same = false;                                                    \
+        break;                                                              \
+      }                                                                     \
+    }                                                                       \
+                                                                            \
+    REQUIRE(is_same);                                                       \
+  }
+
+#define CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(P_LHS, P_RHS, FCT)             \
+  {                                                                                 \
+    using DiscreteFunctionType = const std::decay_t<decltype(FCT(*P_LHS, *P_RHS))>; \
+    std::shared_ptr p_fuv      = ::FCT(P_LHS, P_RHS);                               \
+                                                                                    \
+    REQUIRE(p_fuv.use_count() > 0);                                                 \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv));             \
+                                                                                    \
+    const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv);            \
+                                                                                    \
+    auto lhs_values = P_LHS->cellValues();                                          \
+    auto rhs_values = P_RHS->cellValues();                                          \
+    bool is_same    = true;                                                         \
+    for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {     \
+      using namespace std;                                                          \
+      if (fuv[cell_id] != FCT(lhs_values[cell_id], rhs_values[cell_id])) {          \
+        is_same = false;                                                            \
+        break;                                                                      \
+      }                                                                             \
+    }                                                                               \
+                                                                                    \
+    REQUIRE(is_same);                                                               \
+  }
+
+#define CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(P_LHS, RHS, FCT)           \
+  {                                                                              \
+    using DiscreteFunctionType = const std::decay_t<decltype(FCT(*P_LHS, RHS))>; \
+    std::shared_ptr p_fuv      = ::FCT(P_LHS, RHS);                              \
+                                                                                 \
+    REQUIRE(p_fuv.use_count() > 0);                                              \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv));          \
+                                                                                 \
+    const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv);         \
+                                                                                 \
+    auto lhs_values = P_LHS->cellValues();                                       \
+    bool is_same    = true;                                                      \
+    for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {  \
+      using namespace std;                                                       \
+      if (fuv[cell_id] != FCT(lhs_values[cell_id], RHS)) {                       \
+        is_same = false;                                                         \
+        break;                                                                   \
+      }                                                                          \
+    }                                                                            \
+                                                                                 \
+    REQUIRE(is_same);                                                            \
+  }
+
+#define CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(LHS, P_RHS, FCT)           \
+  {                                                                              \
+    using DiscreteFunctionType = const std::decay_t<decltype(FCT(LHS, *P_RHS))>; \
+    std::shared_ptr p_fuv      = ::FCT(LHS, P_RHS);                              \
+                                                                                 \
+    REQUIRE(p_fuv.use_count() > 0);                                              \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv));          \
+                                                                                 \
+    const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv);         \
+                                                                                 \
+    auto rhs_values = P_RHS->cellValues();                                       \
+    bool is_same    = true;                                                      \
+    for (CellId cell_id = 0; cell_id < rhs_values.numberOfItems(); ++cell_id) {  \
+      using namespace std;                                                       \
+      if (fuv[cell_id] != FCT(LHS, rhs_values[cell_id])) {                       \
+        is_same = false;                                                         \
+        break;                                                                   \
+      }                                                                          \
+    }                                                                            \
+                                                                                 \
+    REQUIRE(is_same);                                                            \
+  }
+
+TEST_CASE("EmbeddedIDiscreteFunctionMathFunctions", "[scheme]")
+{
+  SECTION("1D")
+  {
+    constexpr size_t Dimension = 1;
+
+    using Rd = TinyVector<Dimension>;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh = named_mesh.mesh();
+
+        std::shared_ptr other_mesh =
+          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+        CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+        CellValue<double> values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> positive_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> bounded_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        std::shared_ptr p_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values);
+        std::shared_ptr p_other_mesh_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values);
+        std::shared_ptr p_positive_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values);
+        std::shared_ptr p_bounded_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values);
+
+        std::shared_ptr p_R1_u = [=] {
+          CellValue<TinyVector<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R1_v = [=] {
+          CellValue<TinyVector<1>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R1_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+        constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+          if constexpr (Dimension == 1) {
+            return TinyVector<2>{x[0], 1 + x[0] * x[0]};
+          } else if constexpr (Dimension == 2) {
+            return TinyVector<2>{x[0], x[1]};
+          } else if constexpr (Dimension == 3) {
+            return TinyVector<2>{x[0], x[1] + x[2]};
+          }
+        };
+
+        std::shared_ptr p_R2_u = [=] {
+          CellValue<TinyVector<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              uj[cell_id]           = TinyVector<2>{2 * x[0] + 1, 1 - x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2_v = [=] {
+          CellValue<TinyVector<2>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              vj[cell_id]           = TinyVector<2>{x[0] * x[1] + 1, 2 * x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R2_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+        constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+          if constexpr (Dimension == 1) {
+            return TinyVector<3>{x[0], 1 + x[0] * x[0], 2 - x[0]};
+          } else if constexpr (Dimension == 2) {
+            return TinyVector<3>{x[0], x[1], x[0] + x[1]};
+          } else if constexpr (Dimension == 3) {
+            return TinyVector<3>{x[0], x[1], x[2]};
+          }
+        };
+
+        std::shared_ptr p_R3_u = [=] {
+          CellValue<TinyVector<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj[cell_id]           = TinyVector<3>{2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3_v = [=] {
+          CellValue<TinyVector<3>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj[cell_id]           = TinyVector<3>{x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R3_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+        std::shared_ptr p_R1x1_u = [=] {
+          CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = TinyMatrix<1>{2 * xj[cell_id][0] + 1}; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2x2_u = [=] {
+          CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+
+              uj[cell_id] = TinyMatrix<2>{2 * x[0] + 1, 1 - x[1],   //
+                                          2 * x[1], -x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3x3_u = [=] {
+          CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+
+              uj[cell_id] = TinyMatrix<3>{2 * x[0] + 1,    1 - x[1],        3,             //
+                                          2 * x[1],        -x[0],           x[0] - x[1],   //
+                                          3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_Vector3_u = [=] {
+          CellArray<double> uj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj_vector[cell_id][0] = 2 * x[0] + 1;
+              uj_vector[cell_id][1] = 1 - x[1] * x[2];
+              uj_vector[cell_id][2] = x[0] + x[2];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+        }();
+
+        std::shared_ptr p_Vector3_v = [=] {
+          CellArray<double> vj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj_vector[cell_id][0] = x[0] * x[1] + 1;
+              vj_vector[cell_id][1] = 2 * x[1];
+              vj_vector[cell_id][2] = x[2] * x[0];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+        }();
+
+        std::shared_ptr p_Vector2_w = [=] {
+          CellArray<double> wj_vector{mesh->connectivity(), 2};
+          parallel_for(
+            wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              wj_vector[cell_id][0] = x[0] + x[1] * 2;
+              wj_vector[cell_id][1] = x[0] * x[1];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+        }();
+
+        SECTION("sqrt Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt);
+          REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("abs Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs);
+          REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("sin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin);
+          REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("cos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos);
+          REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("tan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan);
+          REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("asin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin);
+          REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("acos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos);
+          REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("atan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan);
+          REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("sinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh);
+          REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("cosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh);
+          REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("tanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh);
+          REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("asinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh);
+          REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("acosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh);
+          REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("atanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh);
+          REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("exp Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp);
+          REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("log Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log);
+          REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("atan2 Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+          REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+        }
+
+        SECTION("atan2 Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("atan2 R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("min Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min);
+          REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
+
+        SECTION("min Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min);
+          REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("min R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min);
+          REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("min Vh -> R")
+        {
+          REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("max Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max);
+          REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
+
+        SECTION("max Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max);
+          REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("max Vh -> R")
+        {
+          REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("max R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max);
+          REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("pow Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow);
+          REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
+
+        SECTION("pow Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow);
+          REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("pow R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow);
+          REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("dot Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot);
+
+          {
+            auto p_UV = dot(p_Vector3_u, p_Vector3_v);
+            REQUIRE(p_UV.use_count() == 1);
+
+            auto UV        = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV);
+            auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v);
+
+            bool is_same = true;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              if (UV[cell_id] != direct_UV[cell_id]) {
+                is_same = false;
+                break;
+              }
+            }
+
+            REQUIRE(is_same);
+          }
+
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
+        }
+
+        SECTION("dot Vh*Rd -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot);
+          REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})),
+                              "error: incompatible operand types Vh(P0:R^1) and R^2");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
+                              "error: incompatible operand types Vh(P0:R^2) and R^3");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1");
+        }
+
+        SECTION("dot Rd*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot);
+          REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u),
+                              "error: incompatible operand types R^2 and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
+                              "error: incompatible operand types R^3 and Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)");
+        }
+
+        SECTION("sum_of_R* Vh -> R*")
+        {
+          REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues()));
+
+          REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+
+        SECTION("integral_of_R* Vh -> R*")
+        {
+          auto integrate_locally = [&](const auto& cell_values) {
+            const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj();
+            using DataType = decltype(double{} * cell_values[CellId{0}]);
+            CellValue<DataType> local_integral{mesh->connectivity()};
+            parallel_for(
+              local_integral.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; });
+            return local_integral;
+          };
+
+          REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues())));
+
+          REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+      }
+    }
+  }
+
+  SECTION("2D")
+  {
+    constexpr size_t Dimension = 2;
+
+    using Rd = TinyVector<Dimension>;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh = named_mesh.mesh();
+
+        std::shared_ptr other_mesh =
+          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+        CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+        CellValue<double> values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> positive_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> bounded_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        std::shared_ptr p_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values);
+        std::shared_ptr p_other_mesh_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values);
+        std::shared_ptr p_positive_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values);
+        std::shared_ptr p_bounded_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values);
+
+        std::shared_ptr p_R1_u = [=] {
+          CellValue<TinyVector<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R1_v = [=] {
+          CellValue<TinyVector<1>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R1_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+        constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+          if constexpr (Dimension == 1) {
+            return TinyVector<2>{x[0], 1 + x[0] * x[0]};
+          } else if constexpr (Dimension == 2) {
+            return TinyVector<2>{x[0], x[1]};
+          } else if constexpr (Dimension == 3) {
+            return TinyVector<2>{x[0], x[1] + x[2]};
+          }
+        };
+
+        std::shared_ptr p_R2_u = [=] {
+          CellValue<TinyVector<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              uj[cell_id]           = TinyVector<2>{2 * x[0] + 1, 1 - x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2_v = [=] {
+          CellValue<TinyVector<2>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              vj[cell_id]           = TinyVector<2>{x[0] * x[1] + 1, 2 * x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R2_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+        constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+          if constexpr (Dimension == 1) {
+            return TinyVector<3>{x[0], 1 + x[0] * x[0], 2 - x[0]};
+          } else if constexpr (Dimension == 2) {
+            return TinyVector<3>{x[0], x[1], x[0] + x[1]};
+          } else if constexpr (Dimension == 3) {
+            return TinyVector<3>{x[0], x[1], x[2]};
+          }
+        };
+
+        std::shared_ptr p_R3_u = [=] {
+          CellValue<TinyVector<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj[cell_id]           = TinyVector<3>{2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3_v = [=] {
+          CellValue<TinyVector<3>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj[cell_id]           = TinyVector<3>{x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R3_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+        std::shared_ptr p_R1x1_u = [=] {
+          CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = TinyMatrix<1>{2 * xj[cell_id][0] + 1}; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2x2_u = [=] {
+          CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+
+              uj[cell_id] = TinyMatrix<2>{2 * x[0] + 1, 1 - x[1],   //
+                                          2 * x[1], -x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3x3_u = [=] {
+          CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+
+              uj[cell_id] = TinyMatrix<3>{2 * x[0] + 1,    1 - x[1],        3,             //
+                                          2 * x[1],        -x[0],           x[0] - x[1],   //
+                                          3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_Vector3_u = [=] {
+          CellArray<double> uj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj_vector[cell_id][0] = 2 * x[0] + 1;
+              uj_vector[cell_id][1] = 1 - x[1] * x[2];
+              uj_vector[cell_id][2] = x[0] + x[2];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+        }();
+
+        std::shared_ptr p_Vector3_v = [=] {
+          CellArray<double> vj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj_vector[cell_id][0] = x[0] * x[1] + 1;
+              vj_vector[cell_id][1] = 2 * x[1];
+              vj_vector[cell_id][2] = x[2] * x[0];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+        }();
+
+        std::shared_ptr p_Vector2_w = [=] {
+          CellArray<double> wj_vector{mesh->connectivity(), 2};
+          parallel_for(
+            wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              wj_vector[cell_id][0] = x[0] + x[1] * 2;
+              wj_vector[cell_id][1] = x[0] * x[1];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+        }();
+
+        SECTION("sqrt Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt);
+          REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("abs Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs);
+          REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("sin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin);
+          REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("cos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos);
+          REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("tan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan);
+          REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("asin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin);
+          REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("acos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos);
+          REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("atan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan);
+          REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("sinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh);
+          REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("cosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh);
+          REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("tanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh);
+          REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("asinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh);
+          REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("acosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh);
+          REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("atanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh);
+          REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("exp Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp);
+          REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("log Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log);
+          REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("atan2 Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+          REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+        }
+
+        SECTION("atan2 Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("atan2 R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("min Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min);
+          REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
+
+        SECTION("min Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min);
+          REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("min R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min);
+          REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("min Vh -> R")
+        {
+          REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("max Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max);
+          REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
+
+        SECTION("max Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max);
+          REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("max Vh -> R")
+        {
+          REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("max R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max);
+          REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("pow Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow);
+          REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
+
+        SECTION("pow Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow);
+          REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("pow R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow);
+          REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("dot Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot);
+
+          {
+            auto p_UV = dot(p_Vector3_u, p_Vector3_v);
+            REQUIRE(p_UV.use_count() == 1);
+
+            auto UV        = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV);
+            auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v);
+
+            bool is_same = true;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              if (UV[cell_id] != direct_UV[cell_id]) {
+                is_same = false;
+                break;
+              }
+            }
+
+            REQUIRE(is_same);
+          }
+
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
+        }
+
+        SECTION("dot Vh*Rd -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot);
+          REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})),
+                              "error: incompatible operand types Vh(P0:R^1) and R^2");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
+                              "error: incompatible operand types Vh(P0:R^2) and R^3");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1");
+        }
+
+        SECTION("dot Rd*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot);
+          REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u),
+                              "error: incompatible operand types R^2 and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
+                              "error: incompatible operand types R^3 and Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)");
+        }
+
+        SECTION("sum_of_R* Vh -> R*")
+        {
+          REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues()));
+
+          REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+
+        SECTION("integral_of_R* Vh -> R*")
+        {
+          auto integrate_locally = [&](const auto& cell_values) {
+            const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj();
+            using DataType = decltype(double{} * cell_values[CellId{0}]);
+            CellValue<DataType> local_integral{mesh->connectivity()};
+            parallel_for(
+              local_integral.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; });
+            return local_integral;
+          };
+
+          REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues())));
+
+          REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+      }
+    }
+  }
+
+  SECTION("3D")
+  {
+    constexpr size_t Dimension = 3;
+
+    using Rd = TinyVector<Dimension>;
+
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh = named_mesh.mesh();
+
+        std::shared_ptr other_mesh =
+          std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+        CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+        CellValue<double> values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> positive_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 2 + std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        CellValue<double> bounded_values = [=] {
+          CellValue<double> build_values{mesh->connectivity()};
+          parallel_for(
+            build_values.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.9 * std::sin(l2Norm(xj[cell_id])); });
+          return build_values;
+        }();
+
+        std::shared_ptr p_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, values);
+        std::shared_ptr p_other_mesh_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, values);
+        std::shared_ptr p_positive_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, positive_values);
+        std::shared_ptr p_bounded_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, bounded_values);
+
+        std::shared_ptr p_R1_u = [=] {
+          CellValue<TinyVector<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R1_v = [=] {
+          CellValue<TinyVector<1>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R1_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+        constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+          if constexpr (Dimension == 1) {
+            return TinyVector<2>{x[0], 1 + x[0] * x[0]};
+          } else if constexpr (Dimension == 2) {
+            return TinyVector<2>{x[0], x[1]};
+          } else if constexpr (Dimension == 3) {
+            return TinyVector<2>{x[0], x[1] + x[2]};
+          }
+        };
+
+        std::shared_ptr p_R2_u = [=] {
+          CellValue<TinyVector<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              uj[cell_id]           = TinyVector<2>{2 * x[0] + 1, 1 - x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2_v = [=] {
+          CellValue<TinyVector<2>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+              vj[cell_id]           = TinyVector<2>{x[0] * x[1] + 1, 2 * x[1]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R2_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+        constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+          if constexpr (Dimension == 1) {
+            return TinyVector<3>{x[0], 1 + x[0] * x[0], 2 - x[0]};
+          } else if constexpr (Dimension == 2) {
+            return TinyVector<3>{x[0], x[1], x[0] + x[1]};
+          } else if constexpr (Dimension == 3) {
+            return TinyVector<3>{x[0], x[1], x[2]};
+          }
+        };
+
+        std::shared_ptr p_R3_u = [=] {
+          CellValue<TinyVector<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj[cell_id]           = TinyVector<3>{2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3_v = [=] {
+          CellValue<TinyVector<3>> vj{mesh->connectivity()};
+          parallel_for(
+            vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj[cell_id]           = TinyVector<3>{x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+        }();
+
+        std::shared_ptr p_other_mesh_R3_u =
+          std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+        std::shared_ptr p_R1x1_u = [=] {
+          CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(),
+            PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = TinyMatrix<1>{2 * xj[cell_id][0] + 1}; });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R2x2_u = [=] {
+          CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<2> x = to_2d(xj[cell_id]);
+
+              uj[cell_id] = TinyMatrix<2>{2 * x[0] + 1, 1 - x[1],   //
+                                          2 * x[1], -x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_R3x3_u = [=] {
+          CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+          parallel_for(
+            uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+
+              uj[cell_id] = TinyMatrix<3>{2 * x[0] + 1,    1 - x[1],        3,             //
+                                          2 * x[1],        -x[0],           x[0] - x[1],   //
+                                          3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+            });
+
+          return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+        }();
+
+        std::shared_ptr p_Vector3_u = [=] {
+          CellArray<double> uj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              uj_vector[cell_id][0] = 2 * x[0] + 1;
+              uj_vector[cell_id][1] = 1 - x[1] * x[2];
+              uj_vector[cell_id][2] = x[0] + x[2];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+        }();
+
+        std::shared_ptr p_Vector3_v = [=] {
+          CellArray<double> vj_vector{mesh->connectivity(), 3};
+          parallel_for(
+            vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              vj_vector[cell_id][0] = x[0] * x[1] + 1;
+              vj_vector[cell_id][1] = 2 * x[1];
+              vj_vector[cell_id][2] = x[2] * x[0];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+        }();
+
+        std::shared_ptr p_Vector2_w = [=] {
+          CellArray<double> wj_vector{mesh->connectivity(), 2};
+          parallel_for(
+            wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<3> x = to_3d(xj[cell_id]);
+              wj_vector[cell_id][0] = x[0] + x[1] * 2;
+              wj_vector[cell_id][1] = x[0] * x[1];
+            });
+
+          return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+        }();
+
+        SECTION("sqrt Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, sqrt);
+          REQUIRE_THROWS_WITH(sqrt(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("abs Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, abs);
+          REQUIRE_THROWS_WITH(abs(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("sin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sin);
+          REQUIRE_THROWS_WITH(sin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("cos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cos);
+          REQUIRE_THROWS_WITH(cos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("tan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tan);
+          REQUIRE_THROWS_WITH(tan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("asin Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, asin);
+          REQUIRE_THROWS_WITH(asin(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("acos Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, acos);
+          REQUIRE_THROWS_WITH(acos(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("atan Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atan);
+          REQUIRE_THROWS_WITH(atan(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("sinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, sinh);
+          REQUIRE_THROWS_WITH(sinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("cosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, cosh);
+          REQUIRE_THROWS_WITH(cosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("tanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, tanh);
+          REQUIRE_THROWS_WITH(tanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("asinh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, asinh);
+          REQUIRE_THROWS_WITH(asinh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("acosh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, acosh);
+          REQUIRE_THROWS_WITH(acosh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("atanh Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_bounded_u, atanh);
+          REQUIRE_THROWS_WITH(atanh(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("exp Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_u, exp);
+          REQUIRE_THROWS_WITH(exp(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("log Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH_TO_VH_REAL_FUNCTION_EVALUATION(p_positive_u, log);
+          REQUIRE_THROWS_WITH(log(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("atan2 Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+          REQUIRE_THROWS_WITH(atan2(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+        }
+
+        SECTION("atan2 Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 3.6, atan2);
+          REQUIRE_THROWS_WITH(atan2(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("atan2 R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.4, p_u, atan2);
+          REQUIRE_THROWS_WITH(atan2(2.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("min Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, min);
+          REQUIRE_THROWS_WITH(::min(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::min(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::min(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
+
+        SECTION("min Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, min);
+          REQUIRE_THROWS_WITH(min(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("min R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, min);
+          REQUIRE_THROWS_WITH(min(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("min Vh -> R")
+        {
+          REQUIRE(min(std::shared_ptr<const IDiscreteFunction>{p_u}) == min(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(min(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("max Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_u, p_bounded_u, max);
+          REQUIRE_THROWS_WITH(::max(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(::max(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(::max(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
+
+        SECTION("max Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_u, 1.2, max);
+          REQUIRE_THROWS_WITH(max(p_R1_u, 2.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("max Vh -> R")
+        {
+          REQUIRE(max(std::shared_ptr<const IDiscreteFunction>{p_u}) == max(p_u->cellValues()));
+          REQUIRE_THROWS_WITH(max(std::shared_ptr<const IDiscreteFunction>{p_R1_u}),
+                              "error: invalid operand type Vh(P0:R^1)");
+        }
+
+        SECTION("max R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(0.4, p_u, max);
+          REQUIRE_THROWS_WITH(max(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("pow Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_positive_u, p_bounded_u, pow);
+          REQUIRE_THROWS_WITH(pow(p_u, p_other_mesh_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(pow(p_u, p_R1_u), "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(pow(p_R1_u, p_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R)");
+        }
+
+        SECTION("pow Vh*R -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_positive_u, 3.3, pow);
+          REQUIRE_THROWS_WITH(pow(p_R1_u, 3.1), "error: incompatible operand types Vh(P0:R^1) and R");
+        }
+
+        SECTION("pow R*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION(2.1, p_u, pow);
+          REQUIRE_THROWS_WITH(pow(3.1, p_R1_u), "error: incompatible operand types R and Vh(P0:R^1)");
+        }
+
+        SECTION("dot Vh*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R1_u, p_R1_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R2_u, p_R2_v, dot);
+          CHECK_EMBEDDED_VH2_TO_VH_FUNCTION_EVALUATION(p_R3_u, p_R3_v, dot);
+
+          {
+            auto p_UV = dot(p_Vector3_u, p_Vector3_v);
+            REQUIRE(p_UV.use_count() == 1);
+
+            auto UV        = dynamic_cast<const DiscreteFunctionP0<Dimension, double>&>(*p_UV);
+            auto direct_UV = dot(*p_Vector3_u, *p_Vector3_v);
+
+            bool is_same = true;
+            for (CellId cell_id = 0; cell_id < mesh->numberOfCells(); ++cell_id) {
+              if (UV[cell_id] != direct_UV[cell_id]) {
+                is_same = false;
+                break;
+              }
+            }
+
+            REQUIRE(is_same);
+          }
+
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_other_mesh_R1_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, p_other_mesh_R2_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, p_other_mesh_R3_u), "error: operands are defined on different meshes");
+          REQUIRE_THROWS_WITH(dot(p_R1_u, p_R3_u), "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(dot(p_Vector3_u, p_Vector2_w), "error: operands have different dimension");
+        }
+
+        SECTION("dot Vh*Rd -> Vh")
+        {
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R1_u, (TinyVector<1>{3}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R2_u, (TinyVector<2>{-6, 2}), dot);
+          CHECK_EMBEDDED_VHxW_TO_VH_FUNCTION_EVALUATION(p_R3_u, (TinyVector<3>{-1, 5, 2}), dot);
+          REQUIRE_THROWS_WITH(dot(p_R1_u, (TinyVector<2>{-6, 2})),
+                              "error: incompatible operand types Vh(P0:R^1) and R^2");
+          REQUIRE_THROWS_WITH(dot(p_R2_u, (TinyVector<3>{-1, 5, 2})),
+                              "error: incompatible operand types Vh(P0:R^2) and R^3");
+          REQUIRE_THROWS_WITH(dot(p_R3_u, (TinyVector<1>{-1})), "error: incompatible operand types Vh(P0:R^3) and R^1");
+        }
+
+        SECTION("dot Rd*Vh -> Vh")
+        {
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<1>{3}), p_R1_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<2>{-6, 2}), p_R2_u, dot);
+          CHECK_EMBEDDED_WxVH_TO_VH_FUNCTION_EVALUATION((TinyVector<3>{-1, 5, 2}), p_R3_u, dot);
+          REQUIRE_THROWS_WITH(dot((TinyVector<2>{-6, 2}), p_R1_u),
+                              "error: incompatible operand types R^2 and Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<3>{-1, 5, 2}), p_R2_u),
+                              "error: incompatible operand types R^3 and Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(dot((TinyVector<1>{-1}), p_R3_u), "error: incompatible operand types R^1 and Vh(P0:R^3)");
+        }
+
+        SECTION("sum_of_R* Vh -> R*")
+        {
+          REQUIRE(sum_of<double>(p_u) == sum(p_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<1>>(p_R1_u) == sum(p_R1_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<2>>(p_R2_u) == sum(p_R2_u->cellValues()));
+          REQUIRE(sum_of<TinyVector<3>>(p_R3_u) == sum(p_R3_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<1>>(p_R1x1_u) == sum(p_R1x1_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<2>>(p_R2x2_u) == sum(p_R2x2_u->cellValues()));
+          REQUIRE(sum_of<TinyMatrix<3>>(p_R3x3_u) == sum(p_R3x3_u->cellValues()));
+
+          REQUIRE_THROWS_WITH(sum_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(sum_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+
+        SECTION("integral_of_R* Vh -> R*")
+        {
+          auto integrate_locally = [&](const auto& cell_values) {
+            const auto& Vj = MeshDataManager::instance().getMeshData(*mesh).Vj();
+            using DataType = decltype(double{} * cell_values[CellId{0}]);
+            CellValue<DataType> local_integral{mesh->connectivity()};
+            parallel_for(
+              local_integral.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { local_integral[cell_id] = Vj[cell_id] * cell_values[cell_id]; });
+            return local_integral;
+          };
+
+          REQUIRE(integral_of<double>(p_u) == sum(integrate_locally(p_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<1>>(p_R1_u) == sum(integrate_locally(p_R1_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<2>>(p_R2_u) == sum(integrate_locally(p_R2_u->cellValues())));
+          REQUIRE(integral_of<TinyVector<3>>(p_R3_u) == sum(integrate_locally(p_R3_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<1>>(p_R1x1_u) == sum(integrate_locally(p_R1x1_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<2>>(p_R2x2_u) == sum(integrate_locally(p_R2x2_u->cellValues())));
+          REQUIRE(integral_of<TinyMatrix<3>>(p_R3x3_u) == sum(integrate_locally(p_R3x3_u->cellValues())));
+
+          REQUIRE_THROWS_WITH(integral_of<TinyVector<1>>(p_u), "error: invalid operand type Vh(P0:R)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1_u), "error: invalid operand type Vh(P0:R^1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2_u), "error: invalid operand type Vh(P0:R^2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3_u), "error: invalid operand type Vh(P0:R^3)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R1x1_u), "error: invalid operand type Vh(P0:R^1x1)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R2x2_u), "error: invalid operand type Vh(P0:R^2x2)");
+          REQUIRE_THROWS_WITH(integral_of<double>(p_R3x3_u), "error: invalid operand type Vh(P0:R^3x3)");
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_EmbeddedIDiscreteFunctionOperators.cpp b/tests/test_EmbeddedIDiscreteFunctionOperators.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b955ba24b785a83c3be6f73360fa9ea0026cc56e
--- /dev/null
+++ b/tests/test_EmbeddedIDiscreteFunctionOperators.cpp
@@ -0,0 +1,2735 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+
+#include <language/utils/EmbeddedIDiscreteFunctionOperators.hpp>
+#include <scheme/DiscreteFunctionP0.hpp>
+#include <scheme/DiscreteFunctionP0Vector.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+#define CHECK_SCALAR_VH2_TO_VH(P_LHS, OPERATOR, P_RHS)                                  \
+  {                                                                                     \
+    using DiscreteFunctionType = const std::decay_t<decltype(*P_LHS OPERATOR * P_RHS)>; \
+                                                                                        \
+    std::shared_ptr p_fuv = P_LHS OPERATOR P_RHS;                                       \
+                                                                                        \
+    REQUIRE(p_fuv.use_count() > 0);                                                     \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv));                 \
+                                                                                        \
+    const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv);                \
+                                                                                        \
+    auto lhs_values = P_LHS->cellValues();                                              \
+    auto rhs_values = P_RHS->cellValues();                                              \
+    bool is_same    = true;                                                             \
+    for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {         \
+      if (fuv[cell_id] != (lhs_values[cell_id] OPERATOR rhs_values[cell_id])) {         \
+        is_same = false;                                                                \
+        break;                                                                          \
+      }                                                                                 \
+    }                                                                                   \
+                                                                                        \
+    REQUIRE(is_same);                                                                   \
+  }
+
+#define CHECK_SCALAR_VHxX_TO_VH(P_LHS, OPERATOR, RHS)                               \
+  {                                                                                 \
+    using DiscreteFunctionType = const std::decay_t<decltype(*P_LHS OPERATOR RHS)>; \
+                                                                                    \
+    std::shared_ptr p_fuv = P_LHS OPERATOR RHS;                                     \
+                                                                                    \
+    REQUIRE(p_fuv.use_count() > 0);                                                 \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv));             \
+                                                                                    \
+    const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv);            \
+                                                                                    \
+    auto lhs_values = P_LHS->cellValues();                                          \
+    bool is_same    = true;                                                         \
+    for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {     \
+      if (fuv[cell_id] != (lhs_values[cell_id] OPERATOR RHS)) {                     \
+        is_same = false;                                                            \
+        break;                                                                      \
+      }                                                                             \
+    }                                                                               \
+                                                                                    \
+    REQUIRE(is_same);                                                               \
+  }
+
+#define CHECK_SCALAR_XxVH_TO_VH(LHS, OPERATOR, P_RHS)                                \
+  {                                                                                  \
+    using DiscreteFunctionType = const std::decay_t<decltype(LHS OPERATOR * P_RHS)>; \
+                                                                                     \
+    std::shared_ptr p_fuv = LHS OPERATOR P_RHS;                                      \
+                                                                                     \
+    REQUIRE(p_fuv.use_count() > 0);                                                  \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv));              \
+                                                                                     \
+    const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv);             \
+                                                                                     \
+    auto rhs_values = P_RHS->cellValues();                                           \
+    bool is_same    = true;                                                          \
+    for (CellId cell_id = 0; cell_id < rhs_values.numberOfItems(); ++cell_id) {      \
+      if (fuv[cell_id] != (LHS OPERATOR rhs_values[cell_id])) {                      \
+        is_same = false;                                                             \
+        break;                                                                       \
+      }                                                                              \
+    }                                                                                \
+                                                                                     \
+    REQUIRE(is_same);                                                                \
+  }
+
+#define CHECK_VECTOR_VH2_TO_VH(P_LHS, OPERATOR, P_RHS)                                     \
+  {                                                                                        \
+    using DiscreteFunctionType = const std::decay_t<decltype(*P_LHS OPERATOR * P_RHS)>;    \
+                                                                                           \
+    std::shared_ptr p_fuv = P_LHS OPERATOR P_RHS;                                          \
+                                                                                           \
+    REQUIRE(p_fuv.use_count() > 0);                                                        \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv));                    \
+                                                                                           \
+    const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv);                   \
+                                                                                           \
+    auto lhs_arrays = P_LHS->cellArrays();                                                 \
+    auto rhs_arrays = P_RHS->cellArrays();                                                 \
+    bool is_same    = true;                                                                \
+    REQUIRE(rhs_arrays.sizeOfArrays() > 0);                                                \
+    REQUIRE(lhs_arrays.sizeOfArrays() == rhs_arrays.sizeOfArrays());                       \
+    REQUIRE(lhs_arrays.sizeOfArrays() == fuv.size());                                      \
+    for (CellId cell_id = 0; cell_id < lhs_arrays.numberOfItems(); ++cell_id) {            \
+      for (size_t i = 0; i < fuv.size(); ++i) {                                            \
+        if (fuv[cell_id][i] != (lhs_arrays[cell_id][i] OPERATOR rhs_arrays[cell_id][i])) { \
+          is_same = false;                                                                 \
+          break;                                                                           \
+        }                                                                                  \
+      }                                                                                    \
+    }                                                                                      \
+                                                                                           \
+    REQUIRE(is_same);                                                                      \
+  }
+
+#define CHECK_VECTOR_XxVH_TO_VH(LHS, OPERATOR, P_RHS)                                \
+  {                                                                                  \
+    using DiscreteFunctionType = const std::decay_t<decltype(LHS OPERATOR * P_RHS)>; \
+                                                                                     \
+    std::shared_ptr p_fuv = LHS OPERATOR P_RHS;                                      \
+                                                                                     \
+    REQUIRE(p_fuv.use_count() > 0);                                                  \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fuv));              \
+                                                                                     \
+    const auto& fuv = dynamic_cast<const DiscreteFunctionType&>(*p_fuv);             \
+                                                                                     \
+    auto rhs_arrays = P_RHS->cellArrays();                                           \
+    bool is_same    = true;                                                          \
+    REQUIRE(rhs_arrays.sizeOfArrays() > 0);                                          \
+    REQUIRE(fuv.size() == rhs_arrays.sizeOfArrays());                                \
+    for (CellId cell_id = 0; cell_id < rhs_arrays.numberOfItems(); ++cell_id) {      \
+      for (size_t i = 0; i < fuv.size(); ++i) {                                      \
+        if (fuv[cell_id][i] != (LHS OPERATOR rhs_arrays[cell_id][i])) {              \
+          is_same = false;                                                           \
+          break;                                                                     \
+        }                                                                            \
+      }                                                                              \
+    }                                                                                \
+                                                                                     \
+    REQUIRE(is_same);                                                                \
+  }
+
+#define CHECK_SCALAR_VH_TO_VH(OPERATOR, P_RHS)                                   \
+  {                                                                              \
+    using DiscreteFunctionType = const std::decay_t<decltype(OPERATOR * P_RHS)>; \
+                                                                                 \
+    std::shared_ptr p_fu = OPERATOR P_RHS;                                       \
+                                                                                 \
+    REQUIRE(p_fu.use_count() > 0);                                               \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fu));           \
+                                                                                 \
+    const auto& fu = dynamic_cast<const DiscreteFunctionType&>(*p_fu);           \
+                                                                                 \
+    auto rhs_values = P_RHS->cellValues();                                       \
+    bool is_same    = true;                                                      \
+    for (CellId cell_id = 0; cell_id < rhs_values.numberOfItems(); ++cell_id) {  \
+      if (fu[cell_id] != (OPERATOR rhs_values[cell_id])) {                       \
+        is_same = false;                                                         \
+        break;                                                                   \
+      }                                                                          \
+    }                                                                            \
+                                                                                 \
+    REQUIRE(is_same);                                                            \
+  }
+
+#define CHECK_VECTOR_VH_TO_VH(OPERATOR, P_RHS)                                   \
+  {                                                                              \
+    using DiscreteFunctionType = const std::decay_t<decltype(OPERATOR * P_RHS)>; \
+                                                                                 \
+    std::shared_ptr p_fu = OPERATOR P_RHS;                                       \
+                                                                                 \
+    REQUIRE(p_fu.use_count() > 0);                                               \
+    REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionType&>(*p_fu));           \
+                                                                                 \
+    const auto& fu = dynamic_cast<const DiscreteFunctionType&>(*p_fu);           \
+                                                                                 \
+    auto rhs_arrays = P_RHS->cellArrays();                                       \
+    REQUIRE(rhs_arrays.sizeOfArrays() > 0);                                      \
+    REQUIRE(rhs_arrays.sizeOfArrays() == fu.size());                             \
+    bool is_same = true;                                                         \
+    for (CellId cell_id = 0; cell_id < rhs_arrays.numberOfItems(); ++cell_id) {  \
+      for (size_t i = 0; i < rhs_arrays.sizeOfArrays(); ++i) {                   \
+        if (fu[cell_id][i] != (OPERATOR rhs_arrays[cell_id][i])) {               \
+          is_same = false;                                                       \
+          break;                                                                 \
+        }                                                                        \
+      }                                                                          \
+    }                                                                            \
+                                                                                 \
+    REQUIRE(is_same);                                                            \
+  }
+
+#ifdef __clang__
+#pragma clang optimize off
+#endif   // __clang__
+
+TEST_CASE("EmbeddedIDiscreteFunctionOperators", "[scheme]")
+{
+  SECTION("binary operators")
+  {
+    SECTION("1D")
+    {
+      constexpr size_t Dimension = 1;
+
+      using Rd = TinyVector<Dimension>;
+
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          std::shared_ptr other_mesh =
+            std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          CellValue<double> v_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+          std::shared_ptr p_other_mesh_R_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values);
+          std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1_v = [=] {
+            CellValue<TinyVector<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<2>{x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<2>{x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<2>{x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<2>{2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2_v = [=] {
+            CellValue<TinyVector<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                vj[cell_id]           = TinyVector<2>{x[0] * x[1] + 1, 2 * x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<3>{x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<3>{x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<3>{x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<3>{2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3_v = [=] {
+            CellValue<TinyVector<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj[cell_id]           = TinyVector<3>{x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = TinyMatrix<1>{2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1x1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues());
+
+          std::shared_ptr p_R1x1_v = [=] {
+            CellValue<TinyMatrix<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = TinyMatrix<1>{0.3 - xj[cell_id][0]}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<2>{2 * x[0] + 1, 1 - x[1],   //
+                                            2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2x2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues());
+
+          std::shared_ptr p_R2x2_v = [=] {
+            CellValue<TinyMatrix<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                vj[cell_id] = TinyMatrix<2>{x[0] + 0.3, 1 - x[1] - x[0],   //
+                                            2 * x[1] + x[0], x[1] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<3>{2 * x[0] + 1,    1 - x[1],        3,             //
+                                            2 * x[1],        -x[0],           x[0] - x[1],   //
+                                            3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3x3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues());
+
+          std::shared_ptr p_R3x3_v = [=] {
+            CellValue<TinyMatrix<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                vj[cell_id] = TinyMatrix<3>{0.2 * x[0] + 1,  2 + x[1],          3 - x[2],      //
+                                            2.3 * x[2],      x[1] - x[0],       x[2] - x[1],   //
+                                            2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          std::shared_ptr p_other_mesh_Vector3_u =
+            std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays());
+
+          std::shared_ptr p_Vector3_v = [=] {
+            CellArray<double> vj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj_vector[cell_id][0] = x[0] * x[1] + 1;
+                vj_vector[cell_id][1] = 2 * x[1];
+                vj_vector[cell_id][2] = x[2] * x[0];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+          }();
+
+          std::shared_ptr p_Vector2_w = [=] {
+            CellArray<double> wj_vector{mesh->connectivity(), 2};
+            parallel_for(
+              wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                wj_vector[cell_id][0] = x[0] + x[1] * 2;
+                wj_vector[cell_id][1] = x[0] * x[1];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+          }();
+
+          SECTION("sum")
+          {
+            SECTION("Vh + Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
+
+            SECTION("Vh + X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
+
+            SECTION("X + Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      +, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
+
+          SECTION("difference")
+          {
+            SECTION("Vh - Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
+
+            SECTION("Vh - X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
+
+            SECTION("X - Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      -, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
+
+          SECTION("product")
+          {
+            SECTION("Vh * Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v);
+
+              {
+                std::shared_ptr p_fuv = p_R_u * p_Vector3_v;
+
+                REQUIRE(p_fuv.use_count() > 0);
+                REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv));
+
+                const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv);
+
+                auto lhs_values = p_R_u->cellValues();
+                auto rhs_arrays = p_Vector3_v->cellArrays();
+                bool is_same    = true;
+                for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {
+                  for (size_t i = 0; i < fuv.size(); ++i) {
+                    if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) {
+                      is_same = false;
+                      break;
+                    }
+                  }
+                }
+
+                REQUIRE(is_same);
+              }
+
+              REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)");
+
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)");
+
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
+            }
+
+            SECTION("Vh * X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3");
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^1) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3x3");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^1x1) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
+              REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
+            }
+
+            SECTION("X * Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                                     4.7, 2.3, 7.1,   //
+                                                                     9.7, 3.2, 6.8}),
+                                                      *, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                                     4.7, 2.3, 7.1,   //
+                                                                     9.7, 3.2, 6.8}),
+                                                      *, p_R3x3_u);
+
+              CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u);
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0Vector:R)");
+            }
+          }
+
+          SECTION("ratio")
+          {
+            SECTION("Vh / Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v);
+
+              REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+
+              REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes");
+            }
+
+            SECTION("X / Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u);
+            }
+          }
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
+
+      using Rd = TinyVector<Dimension>;
+
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          std::shared_ptr other_mesh =
+            std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          CellValue<double> v_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+          std::shared_ptr p_other_mesh_R_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values);
+          std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1_v = [=] {
+            CellValue<TinyVector<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<2>{x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<2>{x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<2>{x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<2>{2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2_v = [=] {
+            CellValue<TinyVector<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                vj[cell_id]           = TinyVector<2>{x[0] * x[1] + 1, 2 * x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<3>{x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<3>{x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<3>{x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<3>{2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3_v = [=] {
+            CellValue<TinyVector<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj[cell_id]           = TinyVector<3>{x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = TinyMatrix<1>{2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1x1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues());
+
+          std::shared_ptr p_R1x1_v = [=] {
+            CellValue<TinyMatrix<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = TinyMatrix<1>{0.3 - xj[cell_id][0]}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<2>{2 * x[0] + 1, 1 - x[1],   //
+                                            2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2x2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues());
+
+          std::shared_ptr p_R2x2_v = [=] {
+            CellValue<TinyMatrix<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                vj[cell_id] = TinyMatrix<2>{x[0] + 0.3, 1 - x[1] - x[0],   //
+                                            2 * x[1] + x[0], x[1] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<3>{2 * x[0] + 1,    1 - x[1],        3,             //
+                                            2 * x[1],        -x[0],           x[0] - x[1],   //
+                                            3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3x3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues());
+
+          std::shared_ptr p_R3x3_v = [=] {
+            CellValue<TinyMatrix<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                vj[cell_id] = TinyMatrix<3>{0.2 * x[0] + 1,  2 + x[1],          3 - x[2],      //
+                                            2.3 * x[2],      x[1] - x[0],       x[2] - x[1],   //
+                                            2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          std::shared_ptr p_other_mesh_Vector3_u =
+            std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays());
+
+          std::shared_ptr p_Vector3_v = [=] {
+            CellArray<double> vj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj_vector[cell_id][0] = x[0] * x[1] + 1;
+                vj_vector[cell_id][1] = 2 * x[1];
+                vj_vector[cell_id][2] = x[2] * x[0];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+          }();
+
+          std::shared_ptr p_Vector2_w = [=] {
+            CellArray<double> wj_vector{mesh->connectivity(), 2};
+            parallel_for(
+              wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                wj_vector[cell_id][0] = x[0] + x[1] * 2;
+                wj_vector[cell_id][1] = x[0] * x[1];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+          }();
+
+          SECTION("sum")
+          {
+            SECTION("Vh + Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
+
+            SECTION("Vh + X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
+
+            SECTION("X + Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      +, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
+
+          SECTION("difference")
+          {
+            SECTION("Vh - Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
+
+            SECTION("Vh - X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
+
+            SECTION("X - Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      -, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
+
+          SECTION("product")
+          {
+            SECTION("Vh * Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v);
+
+              {
+                std::shared_ptr p_fuv = p_R_u * p_Vector3_v;
+
+                REQUIRE(p_fuv.use_count() > 0);
+                REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv));
+
+                const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv);
+
+                auto lhs_values = p_R_u->cellValues();
+                auto rhs_arrays = p_Vector3_v->cellArrays();
+                bool is_same    = true;
+                for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {
+                  for (size_t i = 0; i < fuv.size(); ++i) {
+                    if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) {
+                      is_same = false;
+                      break;
+                    }
+                  }
+                }
+
+                REQUIRE(is_same);
+              }
+
+              REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)");
+
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)");
+
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
+            }
+
+            SECTION("Vh * X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3");
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^1) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3x3");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^1x1) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
+              REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
+            }
+
+            SECTION("X * Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                                     4.7, 2.3, 7.1,   //
+                                                                     9.7, 3.2, 6.8}),
+                                                      *, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                                     4.7, 2.3, 7.1,   //
+                                                                     9.7, 3.2, 6.8}),
+                                                      *, p_R3x3_u);
+
+              CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u);
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0Vector:R)");
+            }
+          }
+
+          SECTION("ratio")
+          {
+            SECTION("Vh / Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v);
+
+              REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+
+              REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes");
+            }
+
+            SECTION("X / Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u);
+            }
+          }
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
+
+      using Rd = TinyVector<Dimension>;
+
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          std::shared_ptr other_mesh =
+            std::make_shared<Mesh<Connectivity<Dimension>>>(mesh->shared_connectivity(), mesh->xr());
+
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          CellValue<double> v_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.6 + std::sin(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+          std::shared_ptr p_other_mesh_R_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, double>>(other_mesh, u_R_values);
+          std::shared_ptr p_R_v = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, v_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1_v = [=] {
+            CellValue<TinyVector<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { vj[cell_id][0] = xj[cell_id][0] * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(other_mesh, p_R1_u->cellValues());
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<2>{x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<2>{x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<2>{x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<2>{2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2_v = [=] {
+            CellValue<TinyVector<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                vj[cell_id]           = TinyVector<2>{x[0] * x[1] + 1, 2 * x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(other_mesh, p_R2_u->cellValues());
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<3>{x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<3>{x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<3>{x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<3>{2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3_v = [=] {
+            CellValue<TinyVector<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj[cell_id]           = TinyVector<3>{x[0] * x[1] + 1, 2 * x[1], x[2] * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(other_mesh, p_R3_u->cellValues());
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = TinyMatrix<1>{2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R1x1_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(other_mesh, p_R1x1_u->cellValues());
+
+          std::shared_ptr p_R1x1_v = [=] {
+            CellValue<TinyMatrix<1>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { vj[cell_id] = TinyMatrix<1>{0.3 - xj[cell_id][0]}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<2>{2 * x[0] + 1, 1 - x[1],   //
+                                            2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R2x2_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(other_mesh, p_R2x2_u->cellValues());
+
+          std::shared_ptr p_R2x2_v = [=] {
+            CellValue<TinyMatrix<2>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                vj[cell_id] = TinyMatrix<2>{x[0] + 0.3, 1 - x[1] - x[0],   //
+                                            2 * x[1] + x[0], x[1] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<3>{2 * x[0] + 1,    1 - x[1],        3,             //
+                                            2 * x[1],        -x[0],           x[0] - x[1],   //
+                                            3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_other_mesh_R3x3_u =
+            std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(other_mesh, p_R3x3_u->cellValues());
+
+          std::shared_ptr p_R3x3_v = [=] {
+            CellValue<TinyMatrix<3>> vj{mesh->connectivity()};
+            parallel_for(
+              vj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                vj[cell_id] = TinyMatrix<3>{0.2 * x[0] + 1,  2 + x[1],          3 - x[2],      //
+                                            2.3 * x[2],      x[1] - x[0],       x[2] - x[1],   //
+                                            2 * x[2] + x[0], x[1] + 0.2 * x[2], x[2] - 2 * x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, vj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          std::shared_ptr p_other_mesh_Vector3_u =
+            std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(other_mesh, p_Vector3_u->cellArrays());
+
+          std::shared_ptr p_Vector3_v = [=] {
+            CellArray<double> vj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              vj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                vj_vector[cell_id][0] = x[0] * x[1] + 1;
+                vj_vector[cell_id][1] = 2 * x[1];
+                vj_vector[cell_id][2] = x[2] * x[0];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, vj_vector);
+          }();
+
+          std::shared_ptr p_Vector2_w = [=] {
+            CellArray<double> wj_vector{mesh->connectivity(), 2};
+            parallel_for(
+              wj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                wj_vector[cell_id][0] = x[0] + x[1] * 2;
+                wj_vector[cell_id][1] = x[0] * x[1];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, wj_vector);
+          }();
+
+          SECTION("sum")
+          {
+            SECTION("Vh + Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, +, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, +, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, +, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, +, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, +, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, +, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, +, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, +, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u + p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u + p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u + p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R_u + p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u + p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u + p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u + p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u + p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u + p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u + p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u + p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u + p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
+
+            SECTION("Vh + X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, +, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, +, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, +, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, +, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, +, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, +, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, +,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u + (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u + (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u + (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
+
+            SECTION("X + Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, +, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, +, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), +, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), +, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), +, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), +, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), +, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      +, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) + p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) + p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) + p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) + p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) + p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) + p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) + p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
+
+          SECTION("difference")
+          {
+            SECTION("Vh - Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, -, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1_u, -, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2_u, -, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3_u, -, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, -, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, -, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, -, p_R3x3_v);
+
+              CHECK_VECTOR_VH2_TO_VH(p_Vector3_u, -, p_Vector3_v);
+
+              REQUIRE_THROWS_WITH(p_R_u - p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u - p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u - p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u - p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_R_v, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_Vector2_w, "error: Vh(P0Vector:R) spaces have different sizes");
+
+              REQUIRE_THROWS_WITH(p_R_u - p_other_mesh_R_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1_u - p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2_u - p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3_u - p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u - p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u - p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u - p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_Vector3_u - p_other_mesh_Vector3_u,
+                                  "error: operands are defined on different meshes");
+            }
+
+            SECTION("Vh - X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, -, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1_u, -, (TinyVector<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2_u, -, (TinyVector<2>{1.2, 2.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3_u, -, (TinyVector<3>{3.2, 7.1, 5.2}));
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, -, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, -, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, -,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R) and R^1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<2>{1, 2}), "error: incompatible operand types Vh(P0:R) and R^2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<1>{2}), "error: incompatible operand types Vh(P0:R) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R_u - (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u - (double{1}), "error: incompatible operand types Vh(P0Vector:R) and R");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<1>{1}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^1");
+              REQUIRE_THROWS_WITH(p_Vector3_u - (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^2");
+            }
+
+            SECTION("X - Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, -, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, -, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<1>{1.3}), -, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<2>{1.2, 2.3}), -, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyVector<3>{3.2, 7.1, 5.2}), -, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), -, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), -, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}),
+                                      -, p_R3x3_u);
+
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_R_u, "error: incompatible operand types R^1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_R_u, "error: incompatible operand types R^2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyVector<3>{2, 3, 2}) - p_R_u,
+                                  "error: incompatible operand types R^3 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) - p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) - p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) - p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((double{1}) - p_Vector3_u, "error: incompatible operand types R and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<1>{1}) - p_Vector3_u,
+                                  "error: incompatible operand types R^1 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyVector<2>{1, 2}) - p_Vector3_u,
+                                  "error: incompatible operand types R^2 and Vh(P0Vector:R)");
+            }
+          }
+
+          SECTION("product")
+          {
+            SECTION("Vh * Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R1x1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R2x2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, *, p_R3x3_v);
+
+              CHECK_SCALAR_VH2_TO_VH(p_R1x1_u, *, p_R1_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R2x2_u, *, p_R2_v);
+              CHECK_SCALAR_VH2_TO_VH(p_R3x3_u, *, p_R3_v);
+
+              {
+                std::shared_ptr p_fuv = p_R_u * p_Vector3_v;
+
+                REQUIRE(p_fuv.use_count() > 0);
+                REQUIRE_NOTHROW(dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv));
+
+                const auto& fuv = dynamic_cast<const DiscreteFunctionP0Vector<Dimension, double>&>(*p_fuv);
+
+                auto lhs_values = p_R_u->cellValues();
+                auto rhs_arrays = p_Vector3_v->cellArrays();
+                bool is_same    = true;
+                for (CellId cell_id = 0; cell_id < lhs_values.numberOfItems(); ++cell_id) {
+                  for (size_t i = 0; i < fuv.size(); ++i) {
+                    if (fuv[cell_id][i] != (lhs_values[cell_id] * rhs_arrays[cell_id][i])) {
+                      is_same = false;
+                      break;
+                    }
+                  }
+                }
+
+                REQUIRE(is_same);
+              }
+
+              REQUIRE_THROWS_WITH(p_R1_u * p_R1_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R1_u * p_R2x2_v, "error: incompatible operand types Vh(P0:R^1) and Vh(P0:R^2x2)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2x2_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3x3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1x1_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_R2_v, "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_R3_v, "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_R1_v, "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH(p_R1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^1x1) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^2x2) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0:R^3x3) and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_u * p_Vector3_v,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0Vector:R)");
+
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R_u, "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R1x1_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R2x2_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH(p_Vector3_v * p_R3x3_u,
+                                  "error: incompatible operand types Vh(P0Vector:R) and Vh(P0:R^3x3)");
+
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R1x1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R2x2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_R3x3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R1x1_u * p_other_mesh_R1_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R2x2_u * p_other_mesh_R2_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R3x3_u * p_other_mesh_R3_u, "error: operands are defined on different meshes");
+              REQUIRE_THROWS_WITH(p_R_u * p_other_mesh_Vector3_u, "error: operands are defined on different meshes");
+            }
+
+            SECTION("Vh * X -> Vh")
+            {
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, bool{true});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, uint64_t{1});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, int64_t{2});
+              CHECK_SCALAR_VHxX_TO_VH(p_R_u, *, double{1.3});
+
+              CHECK_SCALAR_VHxX_TO_VH(p_R1x1_u, *, (TinyMatrix<1>{1.3}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R2x2_u, *, (TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}));
+              CHECK_SCALAR_VHxX_TO_VH(p_R3x3_u, *,
+                                      (TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                     4.7, 2.3, 7.1,   //
+                                                     9.7, 3.2, 6.8}));
+
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyVector<1>{1}), "error: incompatible operand types Vh(P0:R^1) and R^1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyVector<2>{1, 2}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyVector<3>{2, 3, 2}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3");
+              REQUIRE_THROWS_WITH(p_R1_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^1) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R2_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^2) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^3) and R^3x3");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<1>{2}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^1x1");
+              REQUIRE_THROWS_WITH(p_R1x1_u * (TinyMatrix<2>{2, 3, 1, 4}),
+                                  "error: incompatible operand types Vh(P0:R^1x1) and R^2x2");
+              REQUIRE_THROWS_WITH(p_R2x2_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0:R^2x2) and R^3x3");
+
+              REQUIRE_THROWS_WITH(p_Vector3_u * (TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}),
+                                  "error: incompatible operand types Vh(P0Vector:R) and R^3x3");
+              REQUIRE_THROWS_WITH(p_Vector3_u * (double{2}), "error: incompatible operand types Vh(P0Vector:R) and R");
+            }
+
+            SECTION("X * Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R1x1_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R2x2_u);
+
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, *, p_R3x3_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, *, p_R3x3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                                     4.7, 2.3, 7.1,   //
+                                                                     9.7, 3.2, 6.8}),
+                                                      *, p_R3_u);
+
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<1>{1.3}), *, p_R1x1_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<2>{1.2, 2.3, 4.2, 5.1}), *, p_R2x2_u);
+              CHECK_SCALAR_XxVH_TO_VH((TinyMatrix<3>{3.2, 7.1, 5.2,   //
+                                                                     4.7, 2.3, 7.1,   //
+                                                                     9.7, 3.2, 6.8}),
+                                                      *, p_R3x3_u);
+
+              CHECK_VECTOR_XxVH_TO_VH(bool{true}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(uint64_t{1}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(int64_t{2}, *, p_Vector3_u);
+              CHECK_VECTOR_XxVH_TO_VH(double{1.3}, *, p_Vector3_u);
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R_u, "error: incompatible operand types R^1x1 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R1_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_R2x2_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R3x3_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^3x3)");
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_R2x2_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0:R^2x2)");
+              REQUIRE_THROWS_WITH((TinyMatrix<2>{2, 3, 1, 4}) * p_R1x1_u,
+                                  "error: incompatible operand types R^2x2 and Vh(P0:R^1x1)");
+
+              REQUIRE_THROWS_WITH((TinyMatrix<3>{1, 3, 6, 4, 7, 2, 5, 9, 8}) * p_Vector3_u,
+                                  "error: incompatible operand types R^3x3 and Vh(P0Vector:R)");
+              REQUIRE_THROWS_WITH((TinyMatrix<1>{2}) * p_Vector3_u,
+                                  "error: incompatible operand types R^1x1 and Vh(P0Vector:R)");
+            }
+          }
+
+          SECTION("ratio")
+          {
+            SECTION("Vh / Vh -> Vh")
+            {
+              CHECK_SCALAR_VH2_TO_VH(p_R_u, /, p_R_v);
+
+              REQUIRE_THROWS_WITH(p_R_u / p_R1_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R2_u / p_R1_v, "error: incompatible operand types Vh(P0:R^2) and Vh(P0:R^1)");
+              REQUIRE_THROWS_WITH(p_R3_u / p_R1x1_v, "error: incompatible operand types Vh(P0:R^3) and Vh(P0:R^1x1)");
+              REQUIRE_THROWS_WITH(p_R_u / p_R2x2_v, "error: incompatible operand types Vh(P0:R) and Vh(P0:R^2x2)");
+
+              REQUIRE_THROWS_WITH(p_R_u / p_other_mesh_R_u, "error: operands are defined on different meshes");
+            }
+
+            SECTION("X / Vh -> Vh")
+            {
+              CHECK_SCALAR_XxVH_TO_VH(bool{true}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(uint64_t{1}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(int64_t{2}, /, p_R_u);
+              CHECK_SCALAR_XxVH_TO_VH(double{1.3}, /, p_R_u);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("unary operators")
+  {
+    SECTION("1D")
+    {
+      constexpr size_t Dimension = 1;
+
+      using Rd = TinyVector<Dimension>;
+
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<2>{x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<2>{x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<2>{x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<2>{2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<3>{x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<3>{x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<3>{x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<3>{2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = TinyMatrix<1>{2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<2>{2 * x[0] + 1, 1 - x[1],   //
+                                            2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<3>{2 * x[0] + 1,    1 - x[1],        3,             //
+                                            2 * x[1],        -x[0],           x[0] - x[1],   //
+                                            3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          SECTION("unary minus")
+          {
+            SECTION("- Vh -> Vh")
+            {
+              CHECK_SCALAR_VH_TO_VH(-, p_R_u);
+
+              CHECK_SCALAR_VH_TO_VH(-, p_R1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3_u);
+
+              CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u);
+
+              CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u);
+            }
+          }
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
+
+      using Rd = TinyVector<Dimension>;
+
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<2>{x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<2>{x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<2>{x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<2>{2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<3>{x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<3>{x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<3>{x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<3>{2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = TinyMatrix<1>{2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<2>{2 * x[0] + 1, 1 - x[1],   //
+                                            2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<3>{2 * x[0] + 1,    1 - x[1],        3,             //
+                                            2 * x[1],        -x[0],           x[0] - x[1],   //
+                                            3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          SECTION("unary minus")
+          {
+            SECTION("- Vh -> Vh")
+            {
+              CHECK_SCALAR_VH_TO_VH(-, p_R_u);
+
+              CHECK_SCALAR_VH_TO_VH(-, p_R1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3_u);
+
+              CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u);
+
+              CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u);
+            }
+          }
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
+
+      using Rd = TinyVector<Dimension>;
+
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh = named_mesh.mesh();
+
+          CellValue<const Rd> xj = MeshDataManager::instance().getMeshData(*mesh).xj();
+
+          CellValue<double> u_R_values = [=] {
+            CellValue<double> build_values{mesh->connectivity()};
+            parallel_for(
+              build_values.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { build_values[cell_id] = 0.2 + std::cos(l2Norm(xj[cell_id])); });
+            return build_values;
+          }();
+
+          std::shared_ptr p_R_u = std::make_shared<const DiscreteFunctionP0<Dimension, double>>(mesh, u_R_values);
+
+          std::shared_ptr p_R1_u = [=] {
+            CellValue<TinyVector<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) { uj[cell_id][0] = 2 * xj[cell_id][0] + 1; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<1>>>(mesh, uj);
+          }();
+
+          constexpr auto to_2d = [&](const TinyVector<Dimension>& x) -> TinyVector<2> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<2>{x[0], 1 + x[0] * x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<2>{x[0], x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<2>{x[0], x[1] + x[2]};
+            }
+          };
+
+          std::shared_ptr p_R2_u = [=] {
+            CellValue<TinyVector<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<2>{2 * x[0] + 1, 1 - x[1]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<2>>>(mesh, uj);
+          }();
+
+          constexpr auto to_3d = [&](const TinyVector<Dimension>& x) -> TinyVector<3> {
+            if constexpr (Dimension == 1) {
+              return TinyVector<3>{x[0], 1 + x[0] * x[0], 2 - x[0]};
+            } else if constexpr (Dimension == 2) {
+              return TinyVector<3>{x[0], x[1], x[0] + x[1]};
+            } else if constexpr (Dimension == 3) {
+              return TinyVector<3>{x[0], x[1], x[2]};
+            }
+          };
+
+          std::shared_ptr p_R3_u = [=] {
+            CellValue<TinyVector<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj[cell_id]           = TinyVector<3>{2 * x[0] + 1, 1 - x[1] * x[2], x[0] + x[2]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyVector<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R1x1_u = [=] {
+            CellValue<TinyMatrix<1>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(),
+              PUGS_LAMBDA(const CellId cell_id) { uj[cell_id] = TinyMatrix<1>{2 * xj[cell_id][0] + 1}; });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<1>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R2x2_u = [=] {
+            CellValue<TinyMatrix<2>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<2> x = to_2d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<2>{2 * x[0] + 1, 1 - x[1],   //
+                                            2 * x[1], -x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<2>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_R3x3_u = [=] {
+            CellValue<TinyMatrix<3>> uj{mesh->connectivity()};
+            parallel_for(
+              uj.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+
+                uj[cell_id] = TinyMatrix<3>{2 * x[0] + 1,    1 - x[1],        3,             //
+                                            2 * x[1],        -x[0],           x[0] - x[1],   //
+                                            3 * x[2] - x[1], x[1] - 2 * x[2], x[2] - x[0]};
+              });
+
+            return std::make_shared<const DiscreteFunctionP0<Dimension, TinyMatrix<3>>>(mesh, uj);
+          }();
+
+          std::shared_ptr p_Vector3_u = [=] {
+            CellArray<double> uj_vector{mesh->connectivity(), 3};
+            parallel_for(
+              uj_vector.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<3> x = to_3d(xj[cell_id]);
+                uj_vector[cell_id][0] = 2 * x[0] + 1;
+                uj_vector[cell_id][1] = 1 - x[1] * x[2];
+                uj_vector[cell_id][2] = x[0] + x[2];
+              });
+
+            return std::make_shared<const DiscreteFunctionP0Vector<Dimension, double>>(mesh, uj_vector);
+          }();
+
+          SECTION("unary minus")
+          {
+            SECTION("- Vh -> Vh")
+            {
+              CHECK_SCALAR_VH_TO_VH(-, p_R_u);
+
+              CHECK_SCALAR_VH_TO_VH(-, p_R1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3_u);
+
+              CHECK_SCALAR_VH_TO_VH(-, p_R1x1_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R2x2_u);
+              CHECK_SCALAR_VH_TO_VH(-, p_R3x3_u);
+
+              CHECK_VECTOR_VH_TO_VH(-, p_Vector3_u);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+#ifdef __clang__
+#pragma clang optimize on
+#endif   // __clang__
diff --git a/tests/test_EmbeddedIDiscreteFunctionUtils.cpp b/tests/test_EmbeddedIDiscreteFunctionUtils.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e7d0e8eea200cb115be60a2ba789999155cf5fab
--- /dev/null
+++ b/tests/test_EmbeddedIDiscreteFunctionUtils.cpp
@@ -0,0 +1,167 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/utils/EmbeddedIDiscreteFunctionUtils.hpp>
+#include <scheme/DiscreteFunctionP0.hpp>
+#include <scheme/DiscreteFunctionP0Vector.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("EmbeddedIDiscreteFunctionUtils", "[language]")
+{
+  using R1 = TinyVector<1, double>;
+  using R2 = TinyVector<2, double>;
+  using R3 = TinyVector<3, double>;
+
+  using R1x1 = TinyMatrix<1, 1, double>;
+  using R2x2 = TinyMatrix<2, 2, double>;
+  using R3x3 = TinyMatrix<3, 3, double>;
+
+  SECTION("operand type name")
+  {
+    SECTION("basic types")
+    {
+      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(double{1}) == "R");
+      REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(std::make_shared<double>(1)) == "R");
+    }
+
+    SECTION("discrete P0 function")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, double>{mesh_1d}) ==
+                  "Vh(P0:R)");
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1>{mesh_1d}) ==
+                  "Vh(P0:R^1)");
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2>{mesh_1d}) ==
+                  "Vh(P0:R^2)");
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3>{mesh_1d}) ==
+                  "Vh(P0:R^3)");
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R1x1>{mesh_1d}) ==
+                  "Vh(P0:R^1x1)");
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R2x2>{mesh_1d}) ==
+                  "Vh(P0:R^2x2)");
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0<1, R3x3>{mesh_1d}) ==
+                  "Vh(P0:R^3x3)");
+        }
+      }
+    }
+
+    SECTION("discrete P0Vector function")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(DiscreteFunctionP0Vector<1, double>{mesh_1d, 2}) ==
+                  "Vh(P0Vector:R)");
+        }
+      }
+    }
+  }
+
+  SECTION("check if is same discretization")
+  {
+    SECTION("from shared_ptr")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(std::make_shared<DiscreteFunctionP0<1, double>>(
+                                                                         mesh_1d),
+                                                                       std::make_shared<DiscreteFunctionP0<1, double>>(
+                                                                         mesh_1d)));
+
+          REQUIRE(not EmbeddedIDiscreteFunctionUtils::
+                    isSameDiscretization(std::make_shared<DiscreteFunctionP0<1, double>>(mesh_1d),
+                                         std::make_shared<DiscreteFunctionP0Vector<1, double>>(mesh_1d, 1)));
+        }
+      }
+    }
+
+    SECTION("from value")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, double>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, double>{mesh_1d}));
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R1>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R1>{mesh_1d}));
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R2>{mesh_1d}));
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R3>{mesh_1d}));
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R1x1>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R1x1>{mesh_1d}));
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2x2>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R2x2>{mesh_1d}));
+
+          REQUIRE(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3x3>{mesh_1d},
+                                                                       DiscreteFunctionP0<1, R3x3>{mesh_1d}));
+
+          REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, double>{mesh_1d},
+                                                                           DiscreteFunctionP0<1, R1>{mesh_1d}));
+
+          REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R2>{mesh_1d},
+                                                                           DiscreteFunctionP0<1, R2x2>{mesh_1d}));
+
+          REQUIRE(not EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1, R3x3>{mesh_1d},
+                                                                           DiscreteFunctionP0<1, R2x2>{mesh_1d}));
+        }
+      }
+    }
+
+    SECTION("invalid data type")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          REQUIRE_THROWS_WITH(EmbeddedIDiscreteFunctionUtils::isSameDiscretization(DiscreteFunctionP0<1,
+                                                                                                      int64_t>{mesh_1d},
+                                                                                   DiscreteFunctionP0<1, int64_t>{
+                                                                                     mesh_1d}),
+                              "unexpected error: invalid data type Vh(P0:Z)");
+        }
+      }
+    }
+  }
+
+#ifndef NDEBUG
+  SECTION("errors")
+  {
+    REQUIRE_THROWS_WITH(EmbeddedIDiscreteFunctionUtils::getOperandTypeName(std::shared_ptr<double>()),
+                        "dangling shared_ptr");
+  }
+
+#endif   // NDEBUG
+}
diff --git a/tests/test_FaceIntegrator.cpp b/tests/test_FaceIntegrator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..945d765a6d0a530c7c7babb5d8b5800d5af033d3
--- /dev/null
+++ b/tests/test_FaceIntegrator.cpp
@@ -0,0 +1,1495 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/ItemValue.hpp>
+#include <mesh/Mesh.hpp>
+#include <scheme/FaceIntegrator.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("FaceIntegrator", "[scheme]")
+{
+  SECTION("scalar")
+  {
+    SECTION("2D")
+    {
+      using R2 = TinyVector<2>;
+
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+
+      auto f = [](const R2& X) -> double {
+        const double x = X[0];
+        const double y = X[1];
+        return x * x + 2 * x * y + 3 * y * y + 2;
+      };
+
+      Array<const double> int_f_per_face = [=] {
+        Array<double> int_f(mesh->numberOfFaces());
+        auto face_to_node_matrix = mesh->connectivity().faceToNodeMatrix();
+
+        parallel_for(
+          mesh->numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) {
+            auto face_node_list = face_to_node_matrix[face_id];
+            auto xr             = mesh->xr();
+            double integral     = 0;
+            LineTransformation<2> T(xr[face_node_list[0]], xr[face_node_list[1]]);
+            auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+
+            for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+              const auto& xi = qf.point(i);
+              integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
+            }
+
+            int_f[face_id] = integral;
+          });
+
+        return int_f;
+      }();
+
+      SECTION("direct formula")
+      {
+        SECTION("all faces")
+        {
+          SECTION("FaceValue")
+          {
+            FaceValue<double> values(mesh->connectivity());
+            FaceIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += std::abs(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<double> values(mesh->numberOfFaces());
+
+            FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += std::abs(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<double> values(mesh->numberOfFaces());
+            FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += std::abs(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("face list")
+        {
+          SECTION("Array")
+          {
+            Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            Array<double> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += std::abs(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            SmallArray<double> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += std::abs(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+
+      SECTION("tensorial formula")
+      {
+        SECTION("all faces")
+        {
+          SECTION("FaceValue")
+          {
+            FaceValue<double> values(mesh->connectivity());
+            FaceIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussLobattoQuadratureDescriptor(2), *mesh,
+                                        values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += std::abs(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<double> values(mesh->numberOfFaces());
+
+            FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += std::abs(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<double> values(mesh->numberOfFaces());
+            FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += std::abs(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("face list")
+        {
+          SECTION("Array")
+          {
+            Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            Array<double> values = FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += std::abs(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            SmallArray<double> values =
+              FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += std::abs(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> double {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1;
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        Array<const double> int_f_per_face = [=] {
+          Array<double> int_f(mesh->numberOfFaces());
+          auto face_to_node_matrix = mesh->connectivity().faceToNodeMatrix();
+
+          parallel_for(
+            mesh->numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) {
+              auto face_node_list = face_to_node_matrix[face_id];
+              auto xr             = mesh->xr();
+              double integral     = 0;
+
+              switch (face_node_list.size()) {
+              case 3: {
+                TriangleTransformation<3> T(xr[face_node_list[0]], xr[face_node_list[1]], xr[face_node_list[2]]);
+                auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(2));
+                for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                  const auto& xi = qf.point(i);
+                  integral += qf.weight(i) * T.areaVariationNorm() * f(T(xi));
+                }
+                break;
+              }
+              case 4: {
+                SquareTransformation<3> T(xr[face_node_list[0]], xr[face_node_list[1]], xr[face_node_list[2]],
+                                          xr[face_node_list[3]]);
+                auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(2));
+                for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                  const auto& xi = qf.point(i);
+                  integral += qf.weight(i) * T.areaVariationNorm(xi) * f(T(xi));
+                }
+                break;
+              }
+              default: {
+                throw UnexpectedError("invalid face (node number must be 3 or 4)");
+              }
+              }
+              int_f[face_id] = integral;
+            });
+
+          return int_f;
+        }();
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            SECTION("all faces")
+            {
+              SECTION("FaceValue")
+              {
+                FaceValue<double> values(mesh->connectivity());
+                FaceIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += std::abs(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<double> values(mesh->numberOfFaces());
+
+                FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += std::abs(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<double> values(mesh->numberOfFaces());
+                FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += std::abs(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("face list")
+            {
+              SECTION("Array")
+              {
+                Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                Array<double> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += std::abs(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                SmallArray<double> values =
+                  FaceIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += std::abs(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+
+          SECTION("tensorial formula")
+          {
+            SECTION("all faces")
+            {
+              SECTION("FaceValue")
+              {
+                FaceValue<double> values(mesh->connectivity());
+                FaceIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += std::abs(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<double> values(mesh->numberOfFaces());
+
+                FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += std::abs(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<double> values(mesh->numberOfFaces());
+                FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += std::abs(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("face list")
+            {
+              SECTION("Array")
+              {
+                Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                Array<double> values =
+                  FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += std::abs(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                SmallArray<double> values =
+                  FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += std::abs(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("R^d")
+  {
+    SECTION("2D")
+    {
+      using R2 = TinyVector<2>;
+
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+
+      auto f = [](const R2& X) -> R2 {
+        const double x = X[0];
+        const double y = X[1];
+        return R2{x * x + 2 * x * y + 3 * y * y + 2, 3 * x - 2 * y};
+      };
+
+      Array<const R2> int_f_per_face = [=] {
+        Array<R2> int_f(mesh->numberOfFaces());
+        auto face_to_node_matrix = mesh->connectivity().faceToNodeMatrix();
+
+        parallel_for(
+          mesh->numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) {
+            auto face_node_list = face_to_node_matrix[face_id];
+            auto xr             = mesh->xr();
+            R2 integral         = zero;
+            LineTransformation<2> T(xr[face_node_list[0]], xr[face_node_list[1]]);
+            auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+
+            for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+              const auto& xi = qf.point(i);
+              integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
+            }
+
+            int_f[face_id] = integral;
+          });
+
+        return int_f;
+      }();
+
+      SECTION("direct formula")
+      {
+        SECTION("all faces")
+        {
+          SECTION("FaceValue")
+          {
+            FaceValue<R2> values(mesh->connectivity());
+            FaceIntegrator::integrateTo([=](const R2& x) { return f(x); }, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2> values(mesh->numberOfFaces());
+
+            FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2> values(mesh->numberOfFaces());
+            FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("face list")
+        {
+          SECTION("Array")
+          {
+            Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            Array<R2> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            SmallArray<R2> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+
+      SECTION("tensorial formula")
+      {
+        SECTION("all faces")
+        {
+          SECTION("FaceValue")
+          {
+            FaceValue<R2> values(mesh->connectivity());
+            FaceIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussLobattoQuadratureDescriptor(2), *mesh,
+                                        values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2> values(mesh->numberOfFaces());
+
+            FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2> values(mesh->numberOfFaces());
+            FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("face list")
+        {
+          SECTION("Array")
+          {
+            Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            Array<R2> values = FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            SmallArray<R2> values = FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      using R2 = TinyVector<2>;
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> R2 {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return R2{x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1, 3 * x - 2 * y + z};
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        Array<const R2> int_f_per_face = [=] {
+          Array<R2> int_f(mesh->numberOfFaces());
+          auto face_to_node_matrix = mesh->connectivity().faceToNodeMatrix();
+
+          parallel_for(
+            mesh->numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) {
+              auto face_node_list = face_to_node_matrix[face_id];
+              auto xr             = mesh->xr();
+              R2 integral         = zero;
+
+              switch (face_node_list.size()) {
+              case 3: {
+                TriangleTransformation<3> T(xr[face_node_list[0]], xr[face_node_list[1]], xr[face_node_list[2]]);
+                auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(2));
+                for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                  const auto& xi = qf.point(i);
+                  integral += qf.weight(i) * T.areaVariationNorm() * f(T(xi));
+                }
+                break;
+              }
+              case 4: {
+                SquareTransformation<3> T(xr[face_node_list[0]], xr[face_node_list[1]], xr[face_node_list[2]],
+                                          xr[face_node_list[3]]);
+                auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(2));
+                for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                  const auto& xi = qf.point(i);
+                  integral += qf.weight(i) * T.areaVariationNorm(xi) * f(T(xi));
+                }
+                break;
+              }
+              default: {
+                throw UnexpectedError("invalid face (node number must be 3 or 4)");
+              }
+              }
+              int_f[face_id] = integral;
+            });
+
+          return int_f;
+        }();
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            SECTION("all faces")
+            {
+              SECTION("FaceValue")
+              {
+                FaceValue<R2> values(mesh->connectivity());
+                FaceIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2> values(mesh->numberOfFaces());
+
+                FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2> values(mesh->numberOfFaces());
+                FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("face list")
+            {
+              SECTION("Array")
+              {
+                Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                Array<R2> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                SmallArray<R2> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+
+          SECTION("tensorial formula")
+          {
+            SECTION("all faces")
+            {
+              SECTION("FaceValue")
+              {
+                FaceValue<R2> values(mesh->connectivity());
+                FaceIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2> values(mesh->numberOfFaces());
+
+                FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2> values(mesh->numberOfFaces());
+                FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("face list")
+            {
+              SECTION("Array")
+              {
+                Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                Array<R2> values = FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                SmallArray<R2> values =
+                  FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("R^dxd")
+  {
+    using R2x2  = TinyMatrix<2>;
+    auto l2Norm = [](const R2x2& A) -> double {
+      return std::sqrt(A(0, 0) * A(0, 0) + A(1, 0) * A(1, 0) + A(0, 1) * A(0, 1) + A(1, 1) * A(1, 1));
+    };
+
+    SECTION("2D")
+    {
+      using R2 = TinyVector<2>;
+
+      const auto mesh = MeshDataBaseForTests::get().hybrid2DMesh();
+
+      auto f = [](const R2& X) -> R2x2 {
+        const double x = X[0];
+        const double y = X[1];
+        return R2x2{x * x + 2 * x * y + 3 * y * y + 2, 3 * x - 2 * y, 2 * x * x - 2 * y, 2 + x * y};
+      };
+
+      Array<const R2x2> int_f_per_face = [=] {
+        Array<R2x2> int_f(mesh->numberOfFaces());
+        auto face_to_node_matrix = mesh->connectivity().faceToNodeMatrix();
+
+        parallel_for(
+          mesh->numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) {
+            auto face_node_list = face_to_node_matrix[face_id];
+            auto xr             = mesh->xr();
+            R2x2 integral       = zero;
+            LineTransformation<2> T(xr[face_node_list[0]], xr[face_node_list[1]]);
+            auto qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+
+            for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+              const auto& xi = qf.point(i);
+              integral += qf.weight(i) * T.velocityNorm() * f(T(xi));
+            }
+
+            int_f[face_id] = integral;
+          });
+
+        return int_f;
+      }();
+
+      SECTION("direct formula")
+      {
+        SECTION("all faces")
+        {
+          SECTION("FaceValue")
+          {
+            FaceValue<R2x2> values(mesh->connectivity());
+            FaceIntegrator::integrateTo([=](const R2& x) { return f(x); }, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2x2> values(mesh->numberOfFaces());
+
+            FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2x2> values(mesh->numberOfFaces());
+            FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("face list")
+        {
+          SECTION("Array")
+          {
+            Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            Array<R2x2> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            SmallArray<R2x2> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+
+      SECTION("tensorial formula")
+      {
+        SECTION("all faces")
+        {
+          SECTION("FaceValue")
+          {
+            FaceValue<R2x2> values(mesh->connectivity());
+            FaceIntegrator::integrateTo([=](const R2 x) { return f(x); }, GaussLobattoQuadratureDescriptor(2), *mesh,
+                                        values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("Array")
+          {
+            Array<R2x2> values(mesh->numberOfFaces());
+
+            FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<R2x2> values(mesh->numberOfFaces());
+            FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(2), *mesh, values);
+
+            double error = 0;
+            for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+              error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+
+        SECTION("face list")
+        {
+          SECTION("Array")
+          {
+            Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            Array<R2x2> values = FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+
+          SECTION("SmallArray")
+          {
+            SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+            {
+              size_t k = 0;
+              for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                face_list[k] = face_id;
+              }
+
+              REQUIRE(k == face_list.size());
+            }
+
+            SmallArray<R2x2> values =
+              FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(2), *mesh, face_list);
+
+            double error = 0;
+            for (size_t i = 0; i < face_list.size(); ++i) {
+              error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+            }
+
+            REQUIRE(error == Catch::Approx(0).margin(1E-10));
+          }
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      using R3 = TinyVector<3>;
+
+      auto hybrid_mesh = MeshDataBaseForTests::get().hybrid3DMesh();
+
+      auto f = [](const R3& X) -> R2x2 {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return R2x2{x * x + 2 * x * y + 3 * y * y + 2 * z * z - z + 1, 3 * x - 2 * y + z, 2 * x - y * z, 3 * z - x * x};
+      };
+
+      std::vector<std::pair<std::string, decltype(hybrid_mesh)>> mesh_list;
+      mesh_list.push_back(std::make_pair("hybrid mesh", hybrid_mesh));
+      mesh_list.push_back(std::make_pair("diamond mesh", DualMeshManager::instance().getDiamondDualMesh(*hybrid_mesh)));
+
+      for (auto mesh_info : mesh_list) {
+        auto mesh_name = mesh_info.first;
+        auto mesh      = mesh_info.second;
+
+        Array<const R2x2> int_f_per_face = [=] {
+          Array<R2x2> int_f(mesh->numberOfFaces());
+          auto face_to_node_matrix = mesh->connectivity().faceToNodeMatrix();
+
+          parallel_for(
+            mesh->numberOfFaces(), PUGS_LAMBDA(const FaceId face_id) {
+              auto face_node_list = face_to_node_matrix[face_id];
+              auto xr             = mesh->xr();
+              R2x2 integral       = zero;
+
+              switch (face_node_list.size()) {
+              case 3: {
+                TriangleTransformation<3> T(xr[face_node_list[0]], xr[face_node_list[1]], xr[face_node_list[2]]);
+                auto qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(2));
+                for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                  const auto& xi = qf.point(i);
+                  integral += qf.weight(i) * T.areaVariationNorm() * f(T(xi));
+                }
+                break;
+              }
+              case 4: {
+                SquareTransformation<3> T(xr[face_node_list[0]], xr[face_node_list[1]], xr[face_node_list[2]],
+                                          xr[face_node_list[3]]);
+                auto qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(2));
+                for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+                  const auto& xi = qf.point(i);
+                  integral += qf.weight(i) * T.areaVariationNorm(xi) * f(T(xi));
+                }
+                break;
+              }
+              default: {
+                throw UnexpectedError("invalid face (node number must be 3 or 4)");
+              }
+              }
+              int_f[face_id] = integral;
+            });
+
+          return int_f;
+        }();
+
+        SECTION(mesh_name)
+        {
+          SECTION("direct formula")
+          {
+            SECTION("all faces")
+            {
+              SECTION("FaceValue")
+              {
+                FaceValue<R2x2> values(mesh->connectivity());
+                FaceIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussQuadratureDescriptor(4), *mesh,
+                                            values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2x2> values(mesh->numberOfFaces());
+
+                FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2x2> values(mesh->numberOfFaces());
+                FaceIntegrator::integrateTo(f, GaussQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("face list")
+            {
+              SECTION("Array")
+              {
+                Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                Array<R2x2> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                SmallArray<R2x2> values = FaceIntegrator::integrate(f, GaussQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+
+          SECTION("tensorial formula")
+          {
+            SECTION("all faces")
+            {
+              SECTION("FaceValue")
+              {
+                FaceValue<R2x2> values(mesh->connectivity());
+                FaceIntegrator::integrateTo([=](const R3 x) { return f(x); }, GaussLegendreQuadratureDescriptor(10),
+                                            *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("Array")
+              {
+                Array<R2x2> values(mesh->numberOfFaces());
+
+                FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<R2x2> values(mesh->numberOfFaces());
+                FaceIntegrator::integrateTo(f, GaussLobattoQuadratureDescriptor(4), *mesh, values);
+
+                double error = 0;
+                for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++face_id) {
+                  error += l2Norm(int_f_per_face[face_id] - values[face_id]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+
+            SECTION("face list")
+            {
+              SECTION("Array")
+              {
+                Array<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                Array<R2x2> values =
+                  FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+
+              SECTION("SmallArray")
+              {
+                SmallArray<FaceId> face_list{mesh->numberOfFaces() / 2 + mesh->numberOfFaces() % 2};
+
+                {
+                  size_t k = 0;
+                  for (FaceId face_id = 0; face_id < mesh->numberOfFaces(); ++(++face_id), ++k) {
+                    face_list[k] = face_id;
+                  }
+
+                  REQUIRE(k == face_list.size());
+                }
+
+                SmallArray<R2x2> values =
+                  FaceIntegrator::integrate(f, GaussLobattoQuadratureDescriptor(4), *mesh, face_list);
+
+                double error = 0;
+                for (size_t i = 0; i < face_list.size(); ++i) {
+                  error += l2Norm(int_f_per_face[face_list[i]] - values[i]);
+                }
+
+                REQUIRE(error == Catch::Approx(0).margin(1E-10));
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_FunctionArgumentConverter.cpp b/tests/test_FunctionArgumentConverter.cpp
index 7befebe52d93fc7ba0e17b940dba88acaecfb306..643fbba0b092d337743ea5aeaedb25eb86a5b445 100644
--- a/tests/test_FunctionArgumentConverter.cpp
+++ b/tests/test_FunctionArgumentConverter.cpp
@@ -172,6 +172,18 @@ TEST_CASE("FunctionArgumentConverter", "[language]")
             std::vector<TinyVector<2>>{TinyVector<2>{1, 3.2}, TinyVector<2>{-1, 0.2}});
     REQUIRE(std::get<std::vector<TinyVector<2>>>(execution_policy.currentContext()[2]) ==
             std::vector<TinyVector<2>>{TinyVector<2>{-3, 12.2}, TinyVector<2>{2, 1.2}});
+
+    std::shared_ptr symbol_table = std::make_shared<SymbolTable>();
+    AggregateDataVariant v_fid{std::vector<DataVariant>{uint64_t{3}, uint64_t{2}, uint64_t{7}}};
+
+    FunctionListArgumentConverter<FunctionSymbolId, FunctionSymbolId> converterFid{0, symbol_table};
+    converterFid.convert(execution_policy, v_fid);
+
+    auto&& fid_tuple = std::get<std::vector<FunctionSymbolId>>(execution_policy.currentContext()[0]);
+
+    REQUIRE(fid_tuple[0].id() == 3);
+    REQUIRE(fid_tuple[1].id() == 2);
+    REQUIRE(fid_tuple[2].id() == 7);
   }
 
   SECTION("FunctionArgumentToFunctionSymbolIdConverter")
@@ -184,4 +196,16 @@ TEST_CASE("FunctionArgumentConverter", "[language]")
 
     REQUIRE(std::get<FunctionSymbolId>(execution_policy.currentContext()[0]).id() == f_id);
   }
+
+  SECTION("FunctionArgumentToTupleFunctionSymbolIdConverter")
+  {
+    std::shared_ptr symbol_table = std::make_shared<SymbolTable>();
+
+    const uint64_t f_id = 3;
+    FunctionArgumentToTupleFunctionSymbolIdConverter converter0{0, symbol_table};
+    converter0.convert(execution_policy, f_id);
+    auto&& tuple = std::get<std::vector<FunctionSymbolId>>(execution_policy.currentContext()[0]);
+    REQUIRE(tuple.size() == 1);
+    REQUIRE(tuple[0].id() == f_id);
+  }
 }
diff --git a/tests/test_GaussLegendreQuadratureDescriptor.cpp b/tests/test_GaussLegendreQuadratureDescriptor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8c28ee825b734b484a4f4ce7c1da425365342454
--- /dev/null
+++ b/tests/test_GaussLegendreQuadratureDescriptor.cpp
@@ -0,0 +1,16 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("GaussLegendreQuadratureDescriptor", "[analysis]")
+{
+  GaussLegendreQuadratureDescriptor quadrature_descriptor(3);
+
+  REQUIRE(quadrature_descriptor.isTensorial() == true);
+  REQUIRE(quadrature_descriptor.type() == QuadratureType::GaussLegendre);
+  REQUIRE(quadrature_descriptor.degree() == 3);
+  REQUIRE(quadrature_descriptor.name() == ::name(QuadratureType::GaussLegendre) + "(3)");
+}
diff --git a/tests/test_GaussLobattoQuadratureDescriptor.cpp b/tests/test_GaussLobattoQuadratureDescriptor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe5430735bd2cf2596ba63fd1857ec931f8a1bb3
--- /dev/null
+++ b/tests/test_GaussLobattoQuadratureDescriptor.cpp
@@ -0,0 +1,16 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("GaussLobattoQuadratureDescriptor", "[analysis]")
+{
+  GaussLobattoQuadratureDescriptor quadrature_descriptor(3);
+
+  REQUIRE(quadrature_descriptor.isTensorial() == true);
+  REQUIRE(quadrature_descriptor.type() == QuadratureType::GaussLobatto);
+  REQUIRE(quadrature_descriptor.degree() == 3);
+  REQUIRE(quadrature_descriptor.name() == ::name(QuadratureType::GaussLobatto) + "(3)");
+}
diff --git a/tests/test_GaussQuadratureDescriptor.cpp b/tests/test_GaussQuadratureDescriptor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..033ac7b4a6ebc2d78f0777f254ddf2a2eff659bb
--- /dev/null
+++ b/tests/test_GaussQuadratureDescriptor.cpp
@@ -0,0 +1,16 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("GaussQuadratureDescriptor", "[analysis]")
+{
+  GaussQuadratureDescriptor quadrature_descriptor(3);
+
+  REQUIRE(quadrature_descriptor.isTensorial() == false);
+  REQUIRE(quadrature_descriptor.type() == QuadratureType::Gauss);
+  REQUIRE(quadrature_descriptor.degree() == 3);
+  REQUIRE(quadrature_descriptor.name() == ::name(QuadratureType::Gauss) + "(3)");
+}
diff --git a/tests/test_IntegrateCellArray.cpp b/tests/test_IntegrateCellArray.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b6b8db8c8ca7bc0aa895f8be6b7d5f8dbccd89ff
--- /dev/null
+++ b/tests/test_IntegrateCellArray.cpp
@@ -0,0 +1,626 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/ast/ASTBuilder.hpp>
+#include <language/ast/ASTModulesImporter.hpp>
+#include <language/ast/ASTNodeDataTypeBuilder.hpp>
+#include <language/ast/ASTNodeExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionEvaluationExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionExpressionBuilder.hpp>
+#include <language/ast/ASTNodeTypeCleaner.hpp>
+#include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/PugsFunctionAdapter.hpp>
+#include <language/utils/SymbolTable.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/Mesh.hpp>
+#include <scheme/CellIntegrator.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+
+#include <language/utils/IntegrateCellArray.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("IntegrateCellArray", "[language]")
+{
+  SECTION("integrate on all cells")
+  {
+    auto same_item_integral = [](auto f, auto g) -> bool {
+      using ItemIdType = typename decltype(f)::index_type;
+      for (ItemIdType item_id = 0; item_id < f.numberOfItems(); ++item_id) {
+        for (size_t i = 0; i < f.sizeOfArrays(); ++i) {
+          if (f[item_id][i] != g[item_id][i]) {
+            return false;
+          }
+        }
+      }
+
+      return true;
+    };
+
+    SECTION("1D")
+    {
+      constexpr size_t Dimension = 1;
+      auto quadrature_descriptor = GaussQuadratureDescriptor(3);
+
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          std::string_view data = R"(
+import math;
+import math;
+let f: R^1 -> R, x -> 2*x[0] + 2;
+let g: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+)";
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          std::vector<FunctionSymbolId> function_symbol_id_list;
+
+          {
+            auto [i_symbol, found] = symbol_table->find("f", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          {
+            auto [i_symbol, found] = symbol_table->find("g", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          CellArray<double> cell_integral_array{mesh_1d->connectivity(), 2};
+
+          {
+            CellValue<double> cell_f_integral{mesh_1d->connectivity()};
+            auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * x[0] + 2; };
+            CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_f_integral);
+
+            parallel_for(
+              mesh_1d->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][0] = cell_f_integral[cell_id]; });
+
+            CellValue<double> cell_g_integral{mesh_1d->connectivity()};
+            auto g = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) + 3; };
+            CellIntegrator::integrateTo(g, quadrature_descriptor, *mesh_1d, cell_g_integral);
+
+            parallel_for(
+              mesh_1d->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][1] = cell_g_integral[cell_id]; });
+          }
+
+          CellArray<double> integrate_array =
+            IntegrateCellArray<double(TinyVector<Dimension>)>::integrate(function_symbol_id_list, quadrature_descriptor,
+                                                                         *mesh_1d);
+
+          REQUIRE(same_item_integral(cell_integral_array, integrate_array));
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
+      auto quadrature_descriptor = GaussLobattoQuadratureDescriptor(3);
+
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
+
+          std::string_view data = R"(
+import math;
+let f: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
+let g: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
+)";
+
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          std::vector<FunctionSymbolId> function_symbol_id_list;
+
+          {
+            auto [i_symbol, found] = symbol_table->find("f", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          {
+            auto [i_symbol, found] = symbol_table->find("g", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          CellArray<double> cell_integral_array{mesh_2d->connectivity(), 2};
+
+          {
+            CellValue<double> cell_f_integral{mesh_2d->connectivity()};
+            auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * x[0] + 3 * x[1] + 2; };
+            CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_f_integral);
+
+            parallel_for(
+              mesh_2d->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][0] = cell_f_integral[cell_id]; });
+
+            CellValue<double> cell_g_integral{mesh_2d->connectivity()};
+            auto g = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) + 3; };
+            CellIntegrator::integrateTo(g, quadrature_descriptor, *mesh_2d, cell_g_integral);
+
+            parallel_for(
+              mesh_2d->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][1] = cell_g_integral[cell_id]; });
+          }
+
+          CellArray<double> integrate_array =
+            IntegrateCellArray<double(TinyVector<Dimension>)>::integrate(function_symbol_id_list, quadrature_descriptor,
+                                                                         *mesh_2d);
+
+          REQUIRE(same_item_integral(cell_integral_array, integrate_array));
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
+      auto quadrature_descriptor = GaussLegendreQuadratureDescriptor(3);
+
+      using NamedMesh = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+      std::vector<NamedMesh> mesh_list = [] {
+        std::vector<NamedMesh> extended_mesh_list;
+        std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+        for (size_t i = 0; i < mesh_array.size(); ++i) {
+          extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+        }
+        extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                 *MeshDataBaseForTests::get().hybrid3DMesh())));
+        return extended_mesh_list;
+      }();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
+
+          std::string_view data = R"(
+import math;
+let f: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+let g: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+)";
+
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          std::vector<FunctionSymbolId> function_symbol_id_list;
+
+          {
+            auto [i_symbol, found] = symbol_table->find("f", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          {
+            auto [i_symbol, found] = symbol_table->find("g", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          CellArray<double> cell_integral_array{mesh_3d->connectivity(), 2};
+
+          {
+            CellValue<double> cell_f_integral{mesh_3d->connectivity()};
+            auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * x[0] + 3 * x[1] + 2 * x[2] - 1; };
+            CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_f_integral);
+
+            parallel_for(
+              mesh_3d->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][0] = cell_f_integral[cell_id]; });
+
+            CellValue<double> cell_g_integral{mesh_3d->connectivity()};
+            auto g = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) * x[2] + 3; };
+            CellIntegrator::integrateTo(g, quadrature_descriptor, *mesh_3d, cell_g_integral);
+
+            parallel_for(
+              mesh_3d->numberOfCells(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][1] = cell_g_integral[cell_id]; });
+          }
+
+          CellArray<double> integrate_array =
+            IntegrateCellArray<double(TinyVector<Dimension>)>::integrate(function_symbol_id_list, quadrature_descriptor,
+                                                                         *mesh_3d);
+
+          REQUIRE(same_item_integral(cell_integral_array, integrate_array));
+        }
+      }
+    }
+  }
+
+  SECTION("integrate on cell list")
+  {
+    auto same_item_integral = [](auto f, auto g) -> bool {
+      using ItemIdType = typename decltype(f)::index_type;
+      for (ItemIdType item_id = 0; item_id < f.numberOfRows(); ++item_id) {
+        for (size_t i = 0; i < f.numberOfColumns(); ++i) {
+          if (f[item_id][i] != g[item_id][i]) {
+            return false;
+          }
+        }
+      }
+
+      return true;
+    };
+
+    SECTION("1D")
+    {
+      constexpr size_t Dimension = 1;
+      auto quadrature_descriptor = GaussLegendreQuadratureDescriptor(3);
+
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+          Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
+
+          {
+            size_t k = 0;
+            for (CellId cell_id = 0; cell_id < mesh_1d->numberOfCells(); ++(++cell_id), ++k) {
+              cell_list[k] = cell_id;
+            }
+
+            REQUIRE(k == cell_list.size());
+          }
+
+          std::string_view data = R"(
+import math;
+let f: R^1 -> R, x -> 2*x[0] + 2;
+let g: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+)";
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          std::vector<FunctionSymbolId> function_symbol_id_list;
+
+          {
+            auto [i_symbol, found] = symbol_table->find("f", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          {
+            auto [i_symbol, found] = symbol_table->find("g", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          Table<double> cell_integral_array{cell_list.size(), 2};
+
+          {
+            auto f                        = [](const TinyVector<Dimension>& x) -> double { return 2 * x[0] + 2; };
+            Array<double> cell_f_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+
+            parallel_for(
+              cell_integral_array.numberOfRows(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][0] = cell_f_integral[cell_id]; });
+
+            auto g                        = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) + 3; };
+            Array<double> cell_g_integral = CellIntegrator::integrate(g, quadrature_descriptor, *mesh_1d, cell_list);
+
+            parallel_for(
+              cell_integral_array.numberOfRows(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][1] = cell_g_integral[cell_id]; });
+          }
+
+          Table<const double> integrate_value =
+            IntegrateCellArray<double(TinyVector<Dimension>)>::integrate(function_symbol_id_list, quadrature_descriptor,
+                                                                         *mesh_1d, cell_list);
+
+          REQUIRE(same_item_integral(cell_integral_array, integrate_value));
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
+      auto quadrature_descriptor = GaussLegendreQuadratureDescriptor(3);
+
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
+
+          Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
+
+          {
+            size_t k = 0;
+            for (CellId cell_id = 0; cell_id < mesh_2d->numberOfCells(); ++(++cell_id), ++k) {
+              cell_list[k] = cell_id;
+            }
+
+            REQUIRE(k == cell_list.size());
+          }
+
+          std::string_view data = R"(
+import math;
+let f: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
+let g: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
+)";
+
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          std::vector<FunctionSymbolId> function_symbol_id_list;
+
+          {
+            auto [i_symbol, found] = symbol_table->find("f", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          {
+            auto [i_symbol, found] = symbol_table->find("g", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          Table<double> cell_integral_array{cell_list.size(), 2};
+
+          {
+            auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * x[0] + 3 * x[1] + 2; };
+            Array<double> cell_f_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+
+            parallel_for(
+              cell_integral_array.numberOfRows(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][0] = cell_f_integral[cell_id]; });
+
+            auto g = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) + 3; };
+            Array<double> cell_g_integral = CellIntegrator::integrate(g, quadrature_descriptor, *mesh_2d, cell_list);
+
+            parallel_for(
+              cell_integral_array.numberOfRows(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][1] = cell_g_integral[cell_id]; });
+          }
+
+          Table<const double> integrate_value =
+            IntegrateCellArray<double(TinyVector<Dimension>)>::integrate(function_symbol_id_list, quadrature_descriptor,
+                                                                         *mesh_2d, cell_list);
+
+          REQUIRE(same_item_integral(cell_integral_array, integrate_value));
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
+      auto quadrature_descriptor = GaussQuadratureDescriptor(3);
+
+      using NamedMesh = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+      std::vector<NamedMesh> mesh_list = [] {
+        std::vector<NamedMesh> extended_mesh_list;
+        std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+        for (size_t i = 0; i < mesh_array.size(); ++i) {
+          extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+        }
+        extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                 *MeshDataBaseForTests::get().hybrid3DMesh())));
+        return extended_mesh_list;
+      }();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
+
+          Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
+
+          {
+            size_t k = 0;
+            for (CellId cell_id = 0; cell_id < mesh_3d->numberOfCells(); ++(++cell_id), ++k) {
+              cell_list[k] = cell_id;
+            }
+
+            REQUIRE(k == cell_list.size());
+          }
+
+          std::string_view data = R"(
+import math;
+let f: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+let g: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+)";
+
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          std::vector<FunctionSymbolId> function_symbol_id_list;
+
+          {
+            auto [i_symbol, found] = symbol_table->find("f", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          {
+            auto [i_symbol, found] = symbol_table->find("g", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
+
+          Table<double> cell_integral_array{cell_list.size(), 2};
+
+          {
+            auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * x[0] + 3 * x[1] + 2 * x[2] - 1; };
+            Array<double> cell_f_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+
+            parallel_for(
+              cell_integral_array.numberOfRows(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][0] = cell_f_integral[cell_id]; });
+
+            auto g = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) * x[2] + 3; };
+            Array<double> cell_g_integral = CellIntegrator::integrate(g, quadrature_descriptor, *mesh_3d, cell_list);
+
+            parallel_for(
+              cell_integral_array.numberOfRows(),
+              PUGS_LAMBDA(const CellId cell_id) { cell_integral_array[cell_id][1] = cell_g_integral[cell_id]; });
+          }
+
+          Table<const double> integrate_value =
+            IntegrateCellArray<double(TinyVector<Dimension>)>::integrate(function_symbol_id_list, quadrature_descriptor,
+                                                                         *mesh_3d, cell_list);
+
+          REQUIRE(same_item_integral(cell_integral_array, integrate_value));
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_IntegrateCellValue.cpp b/tests/test_IntegrateCellValue.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..df35a215bea28b236cd2bcd7b243e2f759c4f40b
--- /dev/null
+++ b/tests/test_IntegrateCellValue.cpp
@@ -0,0 +1,451 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/ast/ASTBuilder.hpp>
+#include <language/ast/ASTModulesImporter.hpp>
+#include <language/ast/ASTNodeDataTypeBuilder.hpp>
+#include <language/ast/ASTNodeExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionEvaluationExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionExpressionBuilder.hpp>
+#include <language/ast/ASTNodeTypeCleaner.hpp>
+#include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/PugsFunctionAdapter.hpp>
+#include <language/utils/SymbolTable.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/Mesh.hpp>
+#include <scheme/CellIntegrator.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+
+#include <language/utils/IntegrateCellValue.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("IntegrateCellValue", "[language]")
+{
+  SECTION("integrate on all cells")
+  {
+    auto same_item_integral = [](auto f, auto g) -> bool {
+      using ItemIdType = typename decltype(f)::index_type;
+      for (ItemIdType item_id = 0; item_id < f.numberOfItems(); ++item_id) {
+        if (f[item_id] != g[item_id]) {
+          return false;
+        }
+      }
+
+      return true;
+    };
+
+    SECTION("1D")
+    {
+      constexpr size_t Dimension = 1;
+      auto quadrature_descriptor = GaussQuadratureDescriptor(3);
+
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          std::string_view data = R"(
+import math;
+let R2x2_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
+)";
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          using R2x2             = TinyMatrix<2>;
+          auto [i_symbol, found] = symbol_table->find("R2x2_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<R2x2> cell_integral{mesh_1d->connectivity()};
+          auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+            return R2x2{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+          };
+          CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+          CellValue<R2x2> integrate_value =
+            IntegrateCellValue<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_1d);
+
+          REQUIRE(same_item_integral(cell_integral, integrate_value));
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
+      auto quadrature_descriptor = GaussLobattoQuadratureDescriptor(3);
+
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
+
+          std::string_view data = R"(
+import math;
+let R3_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3);
+)";
+
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          using R3               = TinyVector<3>;
+          auto [i_symbol, found] = symbol_table->find("R3_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<R3> cell_integral{mesh_2d->connectivity()};
+          auto f = [](const TinyVector<Dimension>& x) -> R3 {
+            return R3{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+          };
+          CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+          CellValue<R3> integrate_value =
+            IntegrateCellValue<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                     *mesh_2d);
+
+          REQUIRE(same_item_integral(cell_integral, integrate_value));
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
+      auto quadrature_descriptor = GaussLegendreQuadratureDescriptor(3);
+
+      using NamedMesh = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+      std::vector<NamedMesh> mesh_list = [] {
+        std::vector<NamedMesh> extended_mesh_list;
+        std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+        for (size_t i = 0; i < mesh_array.size(); ++i) {
+          extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+        }
+        extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                 *MeshDataBaseForTests::get().hybrid3DMesh())));
+        return extended_mesh_list;
+      }();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
+
+          std::string_view data = R"(
+import math;
+let scalar_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+)";
+
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          auto [i_symbol, found] = symbol_table->find("scalar_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          CellValue<double> cell_integral{mesh_3d->connectivity()};
+          auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) * x[2] + 3; };
+          CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+          CellValue<double> integrate_value =
+            IntegrateCellValue<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_3d);
+
+          REQUIRE(same_item_integral(cell_integral, integrate_value));
+        }
+      }
+    }
+  }
+
+  SECTION("integrate on cell list")
+  {
+    auto same_item_integral = [](auto f, auto g) -> bool {
+      using ItemIdType = typename decltype(g)::index_type;
+      for (ItemIdType item_id = 0; item_id < f.size(); ++item_id) {
+        if (f[item_id] != g[item_id]) {
+          return false;
+        }
+      }
+
+      return true;
+    };
+
+    SECTION("1D")
+    {
+      constexpr size_t Dimension = 1;
+      auto quadrature_descriptor = GaussLegendreQuadratureDescriptor(3);
+
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+          Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
+
+          {
+            size_t k = 0;
+            for (CellId cell_id = 0; cell_id < mesh_1d->numberOfCells(); ++(++cell_id), ++k) {
+              cell_list[k] = cell_id;
+            }
+
+            REQUIRE(k == cell_list.size());
+          }
+
+          std::string_view data = R"(
+import math;
+let scalar_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+)";
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          auto [i_symbol, found] = symbol_table->find("scalar_1d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * std::exp(x[0]) + 3; };
+
+          Array<const double> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+          Array<const double> integrate_value =
+            IntegrateCellValue<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_1d, cell_list);
+
+          REQUIRE(same_item_integral(cell_integral, integrate_value));
+        }
+      }
+    }
+
+    SECTION("2D")
+    {
+      constexpr size_t Dimension = 2;
+      auto quadrature_descriptor = GaussLegendreQuadratureDescriptor(3);
+
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
+
+          Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
+
+          {
+            size_t k = 0;
+            for (CellId cell_id = 0; cell_id < mesh_2d->numberOfCells(); ++(++cell_id), ++k) {
+              cell_list[k] = cell_id;
+            }
+
+            REQUIRE(k == cell_list.size());
+          }
+
+          std::string_view data = R"(
+import math;
+let R3_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3);
+)";
+
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          using R3               = TinyVector<3>;
+          auto [i_symbol, found] = symbol_table->find("R3_2d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          auto f = [](const TinyVector<Dimension>& x) -> R3 {
+            return R3{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+          };
+
+          Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+          Array<const R3> integrate_value =
+            IntegrateCellValue<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                     *mesh_2d, cell_list);
+
+          REQUIRE(same_item_integral(cell_integral, integrate_value));
+        }
+      }
+    }
+
+    SECTION("3D")
+    {
+      constexpr size_t Dimension = 3;
+      auto quadrature_descriptor = GaussQuadratureDescriptor(3);
+
+      using NamedMesh = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+      std::vector<NamedMesh> mesh_list = [] {
+        std::vector<NamedMesh> extended_mesh_list;
+        std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+        for (size_t i = 0; i < mesh_array.size(); ++i) {
+          extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+        }
+        extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                 *MeshDataBaseForTests::get().hybrid3DMesh())));
+        return extended_mesh_list;
+      }();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
+
+          Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
+
+          {
+            size_t k = 0;
+            for (CellId cell_id = 0; cell_id < mesh_3d->numberOfCells(); ++(++cell_id), ++k) {
+              cell_list[k] = cell_id;
+            }
+
+            REQUIRE(k == cell_list.size());
+          }
+
+          std::string_view data = R"(
+import math;
+let R2x2_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
+)";
+
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          using R2x2             = TinyMatrix<2>;
+          auto [i_symbol, found] = symbol_table->find("R2x2_3d", position);
+          REQUIRE(found);
+          REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+          FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+          auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+            return R2x2{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]};
+          };
+
+          Array<const R2x2> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+
+          Array<R2x2> integrate_value =
+            IntegrateCellValue<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_3d, cell_list);
+
+          REQUIRE(same_item_integral(cell_integral, integrate_value));
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_IntegrateCellValue.hpp b/tests/test_IntegrateCellValue.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f9a9e32a9a0bb69e61626eab69334c3248ee2b74
--- /dev/null
+++ b/tests/test_IntegrateCellValue.hpp
@@ -0,0 +1 @@
+i_f_symboli_f_symboli_f_symbol
diff --git a/tests/test_IntegrateOnCells.cpp b/tests/test_IntegrateOnCells.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cf2d770265fd3dbf1812b14606aa7fadda972d05
--- /dev/null
+++ b/tests/test_IntegrateOnCells.cpp
@@ -0,0 +1,2118 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/ast/ASTBuilder.hpp>
+#include <language/ast/ASTModulesImporter.hpp>
+#include <language/ast/ASTNodeDataTypeBuilder.hpp>
+#include <language/ast/ASTNodeExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionEvaluationExpressionBuilder.hpp>
+#include <language/ast/ASTNodeFunctionExpressionBuilder.hpp>
+#include <language/ast/ASTNodeTypeCleaner.hpp>
+#include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/PugsFunctionAdapter.hpp>
+#include <language/utils/SymbolTable.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/Mesh.hpp>
+#include <scheme/CellIntegrator.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <language/utils/IntegrateOnCells.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("IntegrateOnCells", "[language]")
+{
+  SECTION("Gauss quadrature")
+  {
+    auto quadrature_descriptor = GaussQuadratureDescriptor(3);
+
+    SECTION("integrate on all cells")
+    {
+      auto same_item_integral = [](auto f, auto g) -> bool {
+        using ItemIdType = typename decltype(f)::index_type;
+        for (ItemIdType item_id = 0; item_id < f.numberOfItems(); ++item_id) {
+          if (f[item_id] != g[item_id]) {
+            return false;
+          }
+        }
+
+        return true;
+      };
+
+      SECTION("1D")
+      {
+        constexpr size_t Dimension = 1;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_1d = named_mesh.mesh();
+
+            std::string_view data = R"(
+import math;
+let scalar_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+let R3_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
+let R2x2_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
+)";
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 1d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<double> cell_integral{mesh_1d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * std::exp(x[0]) + 3; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+              Array<double> integrate_value(mesh_1d->numberOfCells());
+              IntegrateOnCells<double(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_1d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 1d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R3> cell_integral{mesh_1d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R3 { return R3{2 * exp(x[0]) + 3, x[0] - 2, 3}; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+              Array<R3> integrate_value(mesh_1d->numberOfCells());
+              IntegrateOnCells<R3(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_1d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 1d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R2x2> cell_integral{mesh_1d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+              Array<R2x2> integrate_value(mesh_1d->numberOfCells());
+              IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_1d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("2D")
+      {
+        constexpr size_t Dimension = 2;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_2d = named_mesh.mesh();
+
+            std::string_view data = R"(
+import math;
+let scalar_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
+let R3_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3);
+let R2x2_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 2d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<double> cell_integral{mesh_2d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) + 3; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+              Array<double> integrate_value(mesh_2d->numberOfCells());
+              IntegrateOnCells<double(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_2d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 2d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R3> cell_integral{mesh_2d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+              Array<R3> integrate_value(mesh_2d->numberOfCells());
+              IntegrateOnCells<R3(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_2d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 2d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R2x2> cell_integral{mesh_2d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+              Array<R2x2> integrate_value(mesh_2d->numberOfCells());
+              IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_2d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("3D")
+      {
+        constexpr size_t Dimension = 3;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+        std::vector<NamedMesh> mesh_list = [] {
+          std::vector<NamedMesh> extended_mesh_list;
+          std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+          for (size_t i = 0; i < mesh_array.size(); ++i) {
+            extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+          }
+          extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+          return extended_mesh_list;
+        }();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_3d = named_mesh.mesh();
+
+            std::string_view data = R"(
+import math;
+let scalar_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+let R3_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3);
+let R2x2_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 3d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<double> cell_integral{mesh_3d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) * x[2] + 3; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+              Array<double> integrate_value(mesh_3d->numberOfCells());
+              IntegrateOnCells<double(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_3d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 3d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R3> cell_integral{mesh_3d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+              Array<R3> integrate_value(mesh_3d->numberOfCells());
+              IntegrateOnCells<R3(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_3d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 3d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R2x2> cell_integral{mesh_3d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
+                            x[0] * x[1] * x[2]};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+              Array<R2x2> integrate_value(mesh_3d->numberOfCells());
+              IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_3d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+    }
+
+    SECTION("integrate on cell list")
+    {
+      auto same_item_integral = [](auto f, auto g) -> bool {
+        using ItemIdType = typename decltype(f)::index_type;
+        for (ItemIdType item_id = 0; item_id < f.size(); ++item_id) {
+          if (f[item_id] != g[item_id]) {
+            return false;
+          }
+        }
+
+        return true;
+      };
+
+      SECTION("1D")
+      {
+        constexpr size_t Dimension = 1;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_1d = named_mesh.mesh();
+            Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh_1d->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            std::string_view data = R"(
+import math;
+let scalar_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+let R3_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
+let R2x2_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
+)";
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 1d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * std::exp(x[0]) + 3; };
+
+              Array<const double> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+              Array<const double> integrate_value =
+                IntegrateOnCells<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_1d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 1d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R3 { return R3{2 * exp(x[0]) + 3, x[0] - 2, 3}; };
+
+              Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+              Array<const R3> integrate_value =
+                IntegrateOnCells<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_1d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 1d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              };
+
+              Array<const R2x2> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+              Array<const R2x2> integrate_value =
+                IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_1d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("2D")
+      {
+        constexpr size_t Dimension = 2;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_2d = named_mesh.mesh();
+
+            Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh_2d->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            std::string_view data = R"(
+import math;
+let scalar_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
+let R3_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3);
+let R2x2_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 2d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) + 3; };
+
+              Array<const double> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+              Array<const double> integrate_value =
+                IntegrateOnCells<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_2d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 2d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              };
+
+              Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+              Array<const R3> integrate_value =
+                IntegrateOnCells<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_2d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 2d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              };
+
+              Array<const R2x2> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+              Array<const R2x2> integrate_value =
+                IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_2d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("3D")
+      {
+        constexpr size_t Dimension = 3;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+        std::vector<NamedMesh> mesh_list = [] {
+          std::vector<NamedMesh> extended_mesh_list;
+          std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+          for (size_t i = 0; i < mesh_array.size(); ++i) {
+            extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+          }
+          extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+          return extended_mesh_list;
+        }();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_3d = named_mesh.mesh();
+
+            Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh_3d->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            std::string_view data = R"(
+import math;
+let scalar_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+let R3_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3);
+let R2x2_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 3d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) * x[2] + 3; };
+
+              Array<const double> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+              Array<const double> integrate_value =
+                IntegrateOnCells<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_3d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 3d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              };
+
+              Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+              Array<const R3> integrate_value =
+                IntegrateOnCells<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_3d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 3d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
+                            x[0] * x[1] * x[2]};
+              };
+
+              Array<const R2x2> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+              Array<const R2x2> integrate_value =
+                IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_3d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("Gauss-Legendre quadrature")
+  {
+    auto quadrature_descriptor = GaussLegendreQuadratureDescriptor(3);
+
+    SECTION("integrate on all cells")
+    {
+      auto same_item_integral = [](auto f, auto g) -> bool {
+        using ItemIdType = typename decltype(f)::index_type;
+        for (ItemIdType item_id = 0; item_id < f.numberOfItems(); ++item_id) {
+          if (f[item_id] != g[item_id]) {
+            return false;
+          }
+        }
+
+        return true;
+      };
+
+      SECTION("1D")
+      {
+        constexpr size_t Dimension = 1;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_1d = named_mesh.mesh();
+
+            std::string_view data = R"(
+import math;
+let scalar_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+let R3_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
+let R2x2_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
+)";
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 1d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<double> cell_integral{mesh_1d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * std::exp(x[0]) + 3; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+              Array<double> integrate_value(mesh_1d->numberOfCells());
+              IntegrateOnCells<double(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_1d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 1d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R3> cell_integral{mesh_1d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R3 { return R3{2 * exp(x[0]) + 3, x[0] - 2, 3}; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+              Array<R3> integrate_value(mesh_1d->numberOfCells());
+              IntegrateOnCells<R3(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_1d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 1d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R2x2> cell_integral{mesh_1d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+              Array<R2x2> integrate_value(mesh_1d->numberOfCells());
+              IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_1d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("2D")
+      {
+        constexpr size_t Dimension = 2;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_2d = named_mesh.mesh();
+
+            std::string_view data = R"(
+import math;
+let scalar_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
+let R3_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3);
+let R2x2_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 2d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<double> cell_integral{mesh_2d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) + 3; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+              Array<double> integrate_value(mesh_2d->numberOfCells());
+              IntegrateOnCells<double(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_2d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 2d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R3> cell_integral{mesh_2d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+              Array<R3> integrate_value(mesh_2d->numberOfCells());
+              IntegrateOnCells<R3(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_2d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 2d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R2x2> cell_integral{mesh_2d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+              Array<R2x2> integrate_value(mesh_2d->numberOfCells());
+              IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_2d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("3D")
+      {
+        constexpr size_t Dimension = 3;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+        std::vector<NamedMesh> mesh_list = [] {
+          std::vector<NamedMesh> extended_mesh_list;
+          std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+          for (size_t i = 0; i < mesh_array.size(); ++i) {
+            extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+          }
+          extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+          return extended_mesh_list;
+        }();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_3d = named_mesh.mesh();
+
+            std::string_view data = R"(
+import math;
+let scalar_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+let R3_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3);
+let R2x2_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 3d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<double> cell_integral{mesh_3d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) * x[2] + 3; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+              Array<double> integrate_value(mesh_3d->numberOfCells());
+              IntegrateOnCells<double(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_3d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 3d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R3> cell_integral{mesh_3d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+              Array<R3> integrate_value(mesh_3d->numberOfCells());
+              IntegrateOnCells<R3(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_3d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 3d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R2x2> cell_integral{mesh_3d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
+                            x[0] * x[1] * x[2]};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+              Array<R2x2> integrate_value(mesh_3d->numberOfCells());
+              IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_3d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+    }
+
+    SECTION("integrate on cell list")
+    {
+      auto same_item_integral = [](auto f, auto g) -> bool {
+        using ItemIdType = typename decltype(f)::index_type;
+        for (ItemIdType item_id = 0; item_id < f.size(); ++item_id) {
+          if (f[item_id] != g[item_id]) {
+            return false;
+          }
+        }
+
+        return true;
+      };
+
+      SECTION("1D")
+      {
+        constexpr size_t Dimension = 1;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_1d = named_mesh.mesh();
+            Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh_1d->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            std::string_view data = R"(
+import math;
+let scalar_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+let R3_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
+let R2x2_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
+)";
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 1d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * std::exp(x[0]) + 3; };
+
+              Array<const double> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+              Array<const double> integrate_value =
+                IntegrateOnCells<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_1d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 1d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R3 { return R3{2 * exp(x[0]) + 3, x[0] - 2, 3}; };
+
+              Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+              Array<const R3> integrate_value =
+                IntegrateOnCells<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_1d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 1d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              };
+
+              Array<const R2x2> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+              Array<const R2x2> integrate_value =
+                IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_1d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("2D")
+      {
+        constexpr size_t Dimension = 2;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_2d = named_mesh.mesh();
+
+            Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh_2d->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            std::string_view data = R"(
+import math;
+let scalar_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
+let R3_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3);
+let R2x2_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 2d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) + 3; };
+
+              Array<const double> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+              Array<const double> integrate_value =
+                IntegrateOnCells<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_2d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 2d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              };
+
+              Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+              Array<const R3> integrate_value =
+                IntegrateOnCells<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_2d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 2d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              };
+
+              Array<const R2x2> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+              Array<const R2x2> integrate_value =
+                IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_2d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("3D")
+      {
+        constexpr size_t Dimension = 3;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+        std::vector<NamedMesh> mesh_list = [] {
+          std::vector<NamedMesh> extended_mesh_list;
+          std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+          for (size_t i = 0; i < mesh_array.size(); ++i) {
+            extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+          }
+          extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+          return extended_mesh_list;
+        }();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_3d = named_mesh.mesh();
+
+            Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh_3d->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            std::string_view data = R"(
+import math;
+let scalar_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+let R3_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3);
+let R2x2_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 3d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) * x[2] + 3; };
+
+              Array<const double> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+              Array<const double> integrate_value =
+                IntegrateOnCells<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_3d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 3d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              };
+
+              Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+              Array<const R3> integrate_value =
+                IntegrateOnCells<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_3d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 3d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
+                            x[0] * x[1] * x[2]};
+              };
+
+              Array<const R2x2> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+              Array<const R2x2> integrate_value =
+                IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_3d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+    }
+  }
+
+  SECTION("Gauss-Lobatto quadrature")
+  {
+    auto quadrature_descriptor = GaussLobattoQuadratureDescriptor(3);
+
+    SECTION("integrate on all cells")
+    {
+      auto same_item_integral = [](auto f, auto g) -> bool {
+        using ItemIdType = typename decltype(f)::index_type;
+        for (ItemIdType item_id = 0; item_id < f.numberOfItems(); ++item_id) {
+          if (f[item_id] != g[item_id]) {
+            return false;
+          }
+        }
+
+        return true;
+      };
+
+      SECTION("1D")
+      {
+        constexpr size_t Dimension = 1;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_1d = named_mesh.mesh();
+
+            std::string_view data = R"(
+import math;
+let scalar_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+let R3_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
+let R2x2_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
+)";
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 1d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<double> cell_integral{mesh_1d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * std::exp(x[0]) + 3; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+              Array<double> integrate_value(mesh_1d->numberOfCells());
+              IntegrateOnCells<double(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_1d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 1d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R3> cell_integral{mesh_1d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R3 { return R3{2 * exp(x[0]) + 3, x[0] - 2, 3}; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+              Array<R3> integrate_value(mesh_1d->numberOfCells());
+              IntegrateOnCells<R3(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_1d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 1d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R2x2> cell_integral{mesh_1d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_1d, cell_integral);
+
+              Array<R2x2> integrate_value(mesh_1d->numberOfCells());
+              IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_1d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("2D")
+      {
+        constexpr size_t Dimension = 2;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_2d = named_mesh.mesh();
+
+            std::string_view data = R"(
+import math;
+let scalar_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
+let R3_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3);
+let R2x2_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 2d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<double> cell_integral{mesh_2d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) + 3; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+              Array<double> integrate_value(mesh_2d->numberOfCells());
+              IntegrateOnCells<double(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_2d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 2d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R3> cell_integral{mesh_2d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+              Array<R3> integrate_value(mesh_2d->numberOfCells());
+              IntegrateOnCells<R3(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_2d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 2d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R2x2> cell_integral{mesh_2d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_2d, cell_integral);
+
+              Array<R2x2> integrate_value(mesh_2d->numberOfCells());
+              IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_2d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("3D")
+      {
+        constexpr size_t Dimension = 3;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+        std::vector<NamedMesh> mesh_list = [] {
+          std::vector<NamedMesh> extended_mesh_list;
+          std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+          for (size_t i = 0; i < mesh_array.size(); ++i) {
+            extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+          }
+          extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+          return extended_mesh_list;
+        }();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_3d = named_mesh.mesh();
+
+            std::string_view data = R"(
+import math;
+let scalar_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+let R3_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3);
+let R2x2_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 3d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<double> cell_integral{mesh_3d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) * x[2] + 3; };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+              Array<double> integrate_value(mesh_3d->numberOfCells());
+              IntegrateOnCells<double(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_3d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 3d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R3> cell_integral{mesh_3d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+              Array<R3> integrate_value(mesh_3d->numberOfCells());
+              IntegrateOnCells<R3(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_3d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 3d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              CellValue<R2x2> cell_integral{mesh_3d->connectivity()};
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
+                            x[0] * x[1] * x[2]};
+              };
+              CellIntegrator::integrateTo(f, quadrature_descriptor, *mesh_3d, cell_integral);
+
+              Array<R2x2> integrate_value(mesh_3d->numberOfCells());
+              IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrateTo(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_3d, integrate_value);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+    }
+
+    SECTION("integrate on cell list")
+    {
+      auto same_item_integral = [](auto f, auto g) -> bool {
+        using ItemIdType = typename decltype(f)::index_type;
+        for (ItemIdType item_id = 0; item_id < f.size(); ++item_id) {
+          if (f[item_id] != g[item_id]) {
+            return false;
+          }
+        }
+
+        return true;
+      };
+
+      SECTION("1D")
+      {
+        constexpr size_t Dimension = 1;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_1d = named_mesh.mesh();
+            Array<CellId> cell_list{mesh_1d->numberOfCells() / 2 + mesh_1d->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh_1d->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            std::string_view data = R"(
+import math;
+let scalar_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+let R3_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
+let R2x2_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
+)";
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 1d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * std::exp(x[0]) + 3; };
+
+              Array<const double> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+              Array<const double> integrate_value =
+                IntegrateOnCells<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_1d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 1d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R3 { return R3{2 * exp(x[0]) + 3, x[0] - 2, 3}; };
+
+              Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+              Array<const R3> integrate_value =
+                IntegrateOnCells<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_1d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 1d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_1d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              };
+
+              Array<const R2x2> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_1d, cell_list);
+              Array<const R2x2> integrate_value =
+                IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_1d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("2D")
+      {
+        constexpr size_t Dimension = 2;
+
+        std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_2d = named_mesh.mesh();
+
+            Array<CellId> cell_list{mesh_2d->numberOfCells() / 2 + mesh_2d->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh_2d->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            std::string_view data = R"(
+import math;
+let scalar_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
+let R3_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3);
+let R2x2_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 2d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) + 3; };
+
+              Array<const double> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+              Array<const double> integrate_value =
+                IntegrateOnCells<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_2d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 2d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              };
+
+              Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+              Array<const R3> integrate_value =
+                IntegrateOnCells<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_2d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 2d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_2d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              };
+
+              Array<const R2x2> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_2d, cell_list);
+              Array<const R2x2> integrate_value =
+                IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_2d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+
+      SECTION("3D")
+      {
+        constexpr size_t Dimension = 3;
+        using NamedMesh            = MeshDataBaseForTests::NamedMesh<Dimension>;
+
+        std::vector<NamedMesh> mesh_list = [] {
+          std::vector<NamedMesh> extended_mesh_list;
+          std::array mesh_array = MeshDataBaseForTests::get().all3DMeshes();
+          for (size_t i = 0; i < mesh_array.size(); ++i) {
+            extended_mesh_list.push_back(MeshDataBaseForTests::get().all3DMeshes()[i]);
+          }
+          extended_mesh_list.push_back(NamedMesh("diamond dual", DualMeshManager::instance().getDiamondDualMesh(
+                                                                   *MeshDataBaseForTests::get().hybrid3DMesh())));
+          return extended_mesh_list;
+        }();
+
+        for (auto named_mesh : mesh_list) {
+          SECTION(named_mesh.name())
+          {
+            auto mesh_3d = named_mesh.mesh();
+
+            Array<CellId> cell_list{mesh_3d->numberOfCells() / 2 + mesh_3d->numberOfCells() % 2};
+
+            {
+              size_t k = 0;
+              for (CellId cell_id = 0; cell_id < mesh_3d->numberOfCells(); ++(++cell_id), ++k) {
+                cell_list[k] = cell_id;
+              }
+
+              REQUIRE(k == cell_list.size());
+            }
+
+            std::string_view data = R"(
+import math;
+let scalar_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+let R3_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3);
+let R2x2_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
+)";
+
+            TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+            auto ast = ASTBuilder::build(input);
+
+            ASTModulesImporter{*ast};
+            ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+            ASTSymbolTableBuilder{*ast};
+            ASTNodeDataTypeBuilder{*ast};
+
+            ASTNodeTypeCleaner<language::var_declaration>{*ast};
+            ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+            ASTNodeExpressionBuilder{*ast};
+
+            std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+            TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+            position.byte = data.size();   // ensure that variables are declared at this point
+
+            SECTION("scalar 3d")
+            {
+              auto [i_symbol, found] = symbol_table->find("scalar_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> double { return 2 * exp(x[0]) * sin(x[1]) * x[2] + 3; };
+
+              Array<const double> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+              Array<const double> integrate_value =
+                IntegrateOnCells<double(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                           *mesh_3d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("vector 3d")
+            {
+              using R3               = TinyVector<3>;
+              auto [i_symbol, found] = symbol_table->find("R3_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R3 {
+                return R3{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              };
+
+              Array<const R3> cell_integral = CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+              Array<const R3> integrate_value =
+                IntegrateOnCells<R3(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                       *mesh_3d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+
+            SECTION("matrix 3d")
+            {
+              using R2x2             = TinyMatrix<2>;
+              auto [i_symbol, found] = symbol_table->find("R2x2_3d", position);
+              REQUIRE(found);
+              REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+              FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+              auto f = [](const TinyVector<Dimension>& x) -> R2x2 {
+                return R2x2{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
+                            x[0] * x[1] * x[2]};
+              };
+
+              Array<const R2x2> cell_integral =
+                CellIntegrator::integrate(f, quadrature_descriptor, *mesh_3d, cell_list);
+              Array<const R2x2> integrate_value =
+                IntegrateOnCells<R2x2(TinyVector<Dimension>)>::integrate(function_symbol_id, quadrature_descriptor,
+                                                                         *mesh_3d, cell_list);
+
+              REQUIRE(same_item_integral(cell_integral, integrate_value));
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/tests/test_InterpolateItemArray.cpp b/tests/test_InterpolateItemArray.cpp
index 5e0bbd962bd9c24165956b9e17a223f80a187055..7fd968be9aaeed6cd7774353df69b2153eb68d51 100644
--- a/tests/test_InterpolateItemArray.cpp
+++ b/tests/test_InterpolateItemArray.cpp
@@ -46,195 +46,219 @@ TEST_CASE("InterpolateItemArray", "[language]")
     {
       constexpr size_t Dimension = 1;
 
-      const auto& mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      std::string_view data = R"(
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_1d: R^1 -> R, x -> 2*x[0] + 2;
 let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      CellArray<double> cell_array{mesh_1d->connectivity(), 2};
-      parallel_for(
-        cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_array[cell_id][0]         = 2 * x[0] + 2;
-          cell_array[cell_id][1]         = 2 * exp(x[0]) + 3;
-        });
+          CellArray<double> cell_array{mesh_1d->connectivity(), 2};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = 2 * x[0] + 2;
+              cell_array[cell_id][1]         = 2 * exp(x[0]) + 3;
+            });
 
-      CellArray<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
+          CellArray<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
 
-      REQUIRE(same_cell_array(cell_array, interpolate_array));
+          REQUIRE(same_cell_array(cell_array, interpolate_array));
+        }
+      }
     }
 
     SECTION("2D")
     {
       constexpr size_t Dimension = 2;
 
-      const auto& mesh_2d = MeshDataBaseForTests::get().cartesianMesh2D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      std::string_view data = R"(
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_2d: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
 let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      CellArray<double> cell_array{mesh_2d->connectivity(), 2};
-      parallel_for(
-        cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_array[cell_id][0]         = 2 * x[0] + 3 * x[1] + 2;
-          cell_array[cell_id][1]         = 2 * exp(x[0]) * sin(x[1]) + 3;
-        });
+          CellArray<double> cell_array{mesh_2d->connectivity(), 2};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = 2 * x[0] + 3 * x[1] + 2;
+              cell_array[cell_id][1]         = 2 * exp(x[0]) * sin(x[1]) + 3;
+            });
 
-      CellArray<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
+          CellArray<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
 
-      REQUIRE(same_cell_array(cell_array, interpolate_array));
+          REQUIRE(same_cell_array(cell_array, interpolate_array));
+        }
+      }
     }
 
     SECTION("3D")
     {
       constexpr size_t Dimension = 3;
 
-      const auto& mesh_3d = MeshDataBaseForTests::get().cartesianMesh3D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-      std::string_view data = R"(
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_3d: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
 let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      CellArray<double> cell_array{mesh_3d->connectivity(), 2};
-      parallel_for(
-        cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-          const TinyVector<Dimension>& x = xj[cell_id];
-          cell_array[cell_id][0]         = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
-          cell_array[cell_id][1]         = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
-        });
+          CellArray<double> cell_array{mesh_3d->connectivity(), 2};
+          parallel_for(
+            cell_array.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+              const TinyVector<Dimension>& x = xj[cell_id];
+              cell_array[cell_id][0]         = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+              cell_array[cell_id][1]         = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+            });
 
-      CellArray<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
+          CellArray<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj);
 
-      REQUIRE(same_cell_array(cell_array, interpolate_array));
+          REQUIRE(same_cell_array(cell_array, interpolate_array));
+        }
+      }
     }
   }
 
@@ -255,213 +279,237 @@ let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
     {
       constexpr size_t Dimension = 1;
 
-      const auto& mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      Array<const CellId> cell_id_list = [&] {
-        Array<CellId> cell_ids{mesh_1d->numberOfCells() / 2};
-        for (size_t i_cell = 0; i_cell < cell_ids.size(); ++i_cell) {
-          cell_ids[i_cell] = static_cast<CellId>(2 * i_cell);
-        }
-        return cell_ids;
-      }();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
 
-      std::string_view data = R"(
-  import math;
-  let scalar_affine_1d: R^1 -> R, x -> 2*x[0] + 2;
-  let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
-  )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
 
-      auto ast = ASTBuilder::build(input);
+          Array<const CellId> cell_id_list = [&] {
+            Array<CellId> cell_ids{mesh_1d->numberOfCells() / 2};
+            for (size_t i_cell = 0; i_cell < cell_ids.size(); ++i_cell) {
+              cell_ids[i_cell] = static_cast<CellId>(2 * i_cell);
+            }
+            return cell_ids;
+          }();
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          std::string_view data = R"(
+import math;
+let scalar_affine_1d: R^1 -> R, x -> 2*x[0] + 2;
+let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
+)";
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          auto ast = ASTBuilder::build(input);
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      Table<double> cell_array{cell_id_list.size(), 2};
-      parallel_for(
-        cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
-          const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-          cell_array[i][0]               = 2 * x[0] + 2;
-          cell_array[i][1]               = 2 * exp(x[0]) + 3;
-        });
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      Table<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      REQUIRE(same_cell_value(cell_array, interpolate_array));
+          Table<double> cell_array{cell_id_list.size(), 2};
+          parallel_for(
+            cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
+              const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+              cell_array[i][0]               = 2 * x[0] + 2;
+              cell_array[i][1]               = 2 * exp(x[0]) + 3;
+            });
+
+          Table<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
+
+          REQUIRE(same_cell_value(cell_array, interpolate_array));
+        }
+      }
     }
 
     SECTION("2D")
     {
       constexpr size_t Dimension = 2;
 
-      const auto& mesh_2d = MeshDataBaseForTests::get().cartesianMesh2D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      Array<CellId> cell_id_list{mesh_2d->numberOfCells() / 2};
-      for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
-        cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
-      }
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
 
-      std::string_view data = R"(
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+
+          Array<CellId> cell_id_list{mesh_2d->numberOfCells() / 2};
+          for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
+            cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
+          }
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_2d: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
 let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      Table<double> cell_array{cell_id_list.size(), 2};
-      parallel_for(
-        cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
-          const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-          cell_array[i][0]               = 2 * x[0] + 3 * x[1] + 2;
-          cell_array[i][1]               = 2 * exp(x[0]) * sin(x[1]) + 3;
-        });
+          Table<double> cell_array{cell_id_list.size(), 2};
+          parallel_for(
+            cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
+              const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+              cell_array[i][0]               = 2 * x[0] + 3 * x[1] + 2;
+              cell_array[i][1]               = 2 * exp(x[0]) * sin(x[1]) + 3;
+            });
 
-      Table<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
+          Table<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
 
-      REQUIRE(same_cell_value(cell_array, interpolate_array));
+          REQUIRE(same_cell_value(cell_array, interpolate_array));
+        }
+      }
     }
 
     SECTION("3D")
     {
       constexpr size_t Dimension = 3;
 
-      const auto& mesh_3d = MeshDataBaseForTests::get().cartesianMesh3D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      Array<CellId> cell_id_list{mesh_3d->numberOfCells() / 2};
-      for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
-        cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
-      }
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+
+          Array<CellId> cell_id_list{mesh_3d->numberOfCells() / 2};
+          for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
+            cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
+          }
 
-      std::string_view data = R"(
+          std::string_view data = R"(
 import math;
 let scalar_affine_3d: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
 let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      auto ast = ASTBuilder::build(input);
+          auto ast = ASTBuilder::build(input);
 
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-      std::vector<FunctionSymbolId> function_symbol_id_list;
+          std::vector<FunctionSymbolId> function_symbol_id_list;
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        function_symbol_id_list.push_back(
-          FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
-      }
+            function_symbol_id_list.push_back(
+              FunctionSymbolId(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table));
+          }
 
-      Table<double> cell_array{cell_id_list.size(), 2};
-      parallel_for(
-        cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
-          const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-          cell_array[i][0]               = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
-          cell_array[i][1]               = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
-        });
+          Table<double> cell_array{cell_id_list.size(), 2};
+          parallel_for(
+            cell_id_list.size(), PUGS_LAMBDA(const size_t i) {
+              const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+              cell_array[i][0]               = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+              cell_array[i][1]               = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+            });
 
-      Table<const double> interpolate_array =
-        InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
+          Table<const double> interpolate_array =
+            InterpolateItemArray<double(TinyVector<Dimension>)>::interpolate(function_symbol_id_list, xj, cell_id_list);
 
-      REQUIRE(same_cell_value(cell_array, interpolate_array));
+          REQUIRE(same_cell_value(cell_array, interpolate_array));
+        }
+      }
     }
   }
 }
diff --git a/tests/test_InterpolateItemValue.cpp b/tests/test_InterpolateItemValue.cpp
index fa786632c10660c9197842196440ae383a489edd..1d7032c660ee001d652339abc0d2daee8a825fff 100644
--- a/tests/test_InterpolateItemValue.cpp
+++ b/tests/test_InterpolateItemValue.cpp
@@ -28,7 +28,7 @@ TEST_CASE("InterpolateItemValue", "[language]")
 {
   SECTION("interpolate on all items")
   {
-    auto same_cell_value = [](auto f, auto g) -> bool {
+    auto same_item_value = [](auto f, auto g) -> bool {
       using ItemIdType = typename decltype(f)::index_type;
       for (ItemIdType item_id = 0; item_id < f.numberOfItems(); ++item_id) {
         if (f[item_id] != g[item_id]) {
@@ -43,10 +43,16 @@ TEST_CASE("InterpolateItemValue", "[language]")
     {
       constexpr size_t Dimension = 1;
 
-      const auto& mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      std::string_view data = R"(
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_1d: R^1 -> R, x -> 2*x[0] + 2;
 let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
@@ -55,149 +61,152 @@ let R3_non_linear_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
 let R2x2_affine_1d: R^1 -> R^2x2, x -> (2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2);
 let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+          auto ast = ASTBuilder::build(input);
 
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<double> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * x[0] + 2;
-          });
-
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-      SECTION("scalar_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-        CellValue<double> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * exp(x[0]) + 3;
-          });
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+          SECTION("scalar_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      SECTION("R3_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            CellValue<double> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * x[0] + 2;
+              });
 
-        CellValue<TinyVector<3>> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyVector<3>{2 * x[0] + 2, 3 * x[0], 2};
-          });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
 
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+          SECTION("scalar_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      SECTION("R3_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            CellValue<double> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * exp(x[0]) + 3;
+              });
 
-        CellValue<TinyVector<3>> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyVector<3>{2 * exp(x[0]) + 3, x[0] - 2, 3};
-          });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
 
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+          SECTION("R3_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      SECTION("R2x2_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            CellValue<TinyVector<3>> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyVector<3>{2 * x[0] + 2, 3 * x[0], 2};
+              });
 
-        CellValue<TinyMatrix<2>> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyMatrix<2>{2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2};
-          });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
 
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+          SECTION("R3_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-      SECTION("R2x2_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            CellValue<TinyVector<3>> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyVector<3>{2 * exp(x[0]) + 3, x[0] - 2, 3};
+              });
 
-        CellValue<TinyMatrix<2>> cell_value{mesh_1d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
-          });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
 
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          SECTION("R2x2_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyMatrix<2>{2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2};
+              });
+
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_1d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] =
+                  TinyMatrix<2>{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              });
+
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
 
@@ -205,10 +214,16 @@ let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x
     {
       constexpr size_t Dimension = 2;
 
-      const auto& mesh_2d = MeshDataBaseForTests::get().cartesianMesh2D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
 
-      std::string_view data = R"(
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_2d: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
 let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
@@ -217,143 +232,146 @@ let R3_non_linear_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3)
 let R2x2_affine_2d: R^2 -> R^2x2, x -> (2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2);
 let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<double> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * x[0] + 3 * x[1] + 2;
-          });
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<double> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * exp(x[0]) * sin(x[1]) + 3;
-          });
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyVector<3>> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[1]};
-          });
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyVector<3>> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
-          });
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyMatrix<2>> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2};
-          });
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyMatrix<2>> cell_value{mesh_2d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
-          });
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          SECTION("scalar_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<double> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * x[0] + 3 * x[1] + 2;
+              });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<double> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * exp(x[0]) * sin(x[1]) + 3;
+              });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyVector<3>> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[1]};
+              });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyVector<3>> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2};
+              });
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_2d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] =
+                  TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              });
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
 
@@ -361,10 +379,16 @@ let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*
     {
       constexpr size_t Dimension = 3;
 
-      const auto& mesh_3d = MeshDataBaseForTests::get().cartesianMesh3D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
 
-      std::string_view data = R"(
+          std::string_view data = R"(
 import math;
 let scalar_affine_3d: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
 let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
@@ -373,152 +397,154 @@ let R3_non_linear_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[
 let R2x2_affine_3d: R^3 -> R^2x2, x -> (2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2], 2 * x[0] + x[1] + x[2], 2);
 let R2x2_non_linear_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<double> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
-          });
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<double> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id]            = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
-          });
-        CellValue<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyVector<3>> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1] + 2 * x[2], 2 * x[2]};
-          });
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyVector<3>> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
-          });
-        CellValue<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyMatrix<2>> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] =
-              TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2], 2 * x[0] + x[1] + x[2], 2};
-          });
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        CellValue<TinyMatrix<2>> cell_value{mesh_3d->connectivity()};
-        parallel_for(
-          cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
-            const TinyVector<Dimension>& x = xj[cell_id];
-            cell_value[cell_id] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]),
-                                                3, x[0] * x[1] * x[2]};
-          });
-        CellValue<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          SECTION("scalar_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<double> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+              });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<double> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+              });
+            CellValue<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyVector<3>> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1] + 2 * x[2], 2 * x[2]};
+              });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyVector<3>> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              });
+            CellValue<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2],
+                                                    2 * x[0] + x[1] + x[2], 2};
+              });
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            CellValue<TinyMatrix<2>> cell_value{mesh_3d->connectivity()};
+            parallel_for(
+              cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+                const TinyVector<Dimension>& x = xj[cell_id];
+                cell_value[cell_id]            = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]),
+                                                    sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]};
+              });
+            CellValue<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
   }
 
-  SECTION("interpolate on items list")
+  SECTION("interpolate on item list")
   {
-    auto same_cell_value = [](auto interpolated, auto reference) -> bool {
+    auto same_item_value = [](auto interpolated, auto reference) -> bool {
       for (size_t i = 0; i < interpolated.size(); ++i) {
         if (interpolated[i] != reference[i]) {
           return false;
@@ -532,18 +558,24 @@ let R2x2_non_linear_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(
     {
       constexpr size_t Dimension = 1;
 
-      const auto& mesh_1d = MeshDataBaseForTests::get().cartesianMesh1D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      Array<const CellId> cell_id_list = [&] {
-        Array<CellId> cell_ids{mesh_1d->numberOfCells() / 2};
-        for (size_t i_cell = 0; i_cell < cell_ids.size(); ++i_cell) {
-          cell_ids[i_cell] = static_cast<CellId>(2 * i_cell);
-        }
-        return cell_ids;
-      }();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_1d).xj();
+
+          Array<const CellId> cell_id_list = [&] {
+            Array<CellId> cell_ids{mesh_1d->numberOfCells() / 2};
+            for (size_t i_cell = 0; i_cell < cell_ids.size(); ++i_cell) {
+              cell_ids[i_cell] = static_cast<CellId>(2 * i_cell);
+            }
+            return cell_ids;
+          }();
 
-      std::string_view data = R"(
+          std::string_view data = R"(
 import math;
 let scalar_affine_1d: R^1 -> R, x -> 2*x[0] + 2;
 let scalar_non_linear_1d: R^1 -> R, x -> 2 * exp(x[0]) + 3;
@@ -552,149 +584,155 @@ let R3_non_linear_1d: R^1 -> R^3, x -> (2 * exp(x[0]) + 3, x[0] - 2, 3);
 let R2x2_affine_1d: R^1 -> R^2x2, x -> (2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2);
 let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
 
-      SECTION("scalar_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+          auto ast = ASTBuilder::build(input);
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
 
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * x[0] + 2;
-          });
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
 
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * exp(x[0]) + 3;
-          });
-
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyVector<3>{2 * x[0] + 2, 3 * x[0], 2};
-          });
-
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyVector<3>{2 * exp(x[0]) + 3, x[0] - 2, 3};
-          });
-
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
 
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyMatrix<2>{2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2};
-          });
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
 
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+          SECTION("scalar_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
 
-      SECTION("R2x2_non_linear_1d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * x[0] + 2;
+              });
 
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
 
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
-          });
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
 
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * exp(x[0]) + 3;
+              });
+
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyVector<3>{2 * x[0] + 2, 3 * x[0], 2};
+              });
+
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyVector<3>{2 * exp(x[0]) + 3, x[0] - 2, 3};
+              });
+
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
 
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyMatrix<2>{2 * x[0] + 3 + 2, 3 * x[0], 2 * x[0], 2};
+              });
+
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_1d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_1d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[0]) + 3, sin(x[0] - 2 * x[0]), 3, x[0] * x[0]};
+              });
+
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
 
@@ -702,15 +740,21 @@ let R2x2_non_linear_1d: R^1 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[0]) + 3, sin(x
     {
       constexpr size_t Dimension = 2;
 
-      const auto& mesh_2d = MeshDataBaseForTests::get().cartesianMesh2D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      Array<CellId> cell_id_list{mesh_2d->numberOfCells() / 2};
-      for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
-        cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
-      }
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
 
-      std::string_view data = R"(
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_2d).xj();
+
+          Array<CellId> cell_id_list{mesh_2d->numberOfCells() / 2};
+          for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
+            cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
+          }
+
+          std::string_view data = R"(
 import math;
 let scalar_affine_2d: R^2 -> R, x -> 2*x[0] + 3*x[1] + 2;
 let scalar_non_linear_2d: R^2 -> R, x -> 2*exp(x[0])*sin(x[1])+3;
@@ -719,144 +763,150 @@ let R3_non_linear_2d: R^2 -> R^3, x -> (2*exp(x[0])*sin(x[1])+3, x[0]-2*x[1], 3)
 let R2x2_affine_2d: R^2 -> R^2x2, x -> (2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2);
 let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*x[1]), 3, x[0]*x[1]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * x[0] + 3 * x[1] + 2;
-          });
-
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * exp(x[0]) * sin(x[1]) + 3;
-          });
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[1]};
-          });
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
-          });
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2};
-          });
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_non_linear_2d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
-          });
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          SECTION("scalar_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * x[0] + 3 * x[1] + 2;
+              });
+
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * exp(x[0]) * sin(x[1]) + 3;
+              });
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[1]};
+              });
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + 3, x[0] - 2 * x[1], 3};
+              });
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1], 2 * x[0] + x[1], 2};
+              });
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_2d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_2d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3, sin(x[0] - 2 * x[1]), 3, x[0] * x[1]};
+              });
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
 
@@ -864,15 +914,21 @@ let R2x2_non_linear_2d: R^2 -> R^2x2, x -> (2*exp(x[0])*sin(x[1])+3, sin(x[0]-2*
     {
       constexpr size_t Dimension = 3;
 
-      const auto& mesh_3d = MeshDataBaseForTests::get().cartesianMesh3D();
-      auto xj             = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      Array<CellId> cell_id_list{mesh_3d->numberOfCells() / 2};
-      for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
-        cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
-      }
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
+
+          auto xj = MeshDataManager::instance().getMeshData(*mesh_3d).xj();
+
+          Array<CellId> cell_id_list{mesh_3d->numberOfCells() / 2};
+          for (size_t i_cell = 0; i_cell < cell_id_list.size(); ++i_cell) {
+            cell_id_list[i_cell] = static_cast<CellId>(2 * i_cell);
+          }
 
-      std::string_view data = R"(
+          std::string_view data = R"(
 import math;
 let scalar_affine_3d: R^3 -> R, x -> 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
 let scalar_non_linear_3d: R^3 -> R, x -> 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
@@ -881,145 +937,151 @@ let R3_non_linear_3d: R^3 -> R^3, x -> (2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[
 let R2x2_affine_3d: R^3 -> R^2x2, x -> (2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2], 2 * x[0] + x[1] + x[2], 2);
 let R2x2_non_linear_3d: R^3 -> R^2x2, x -> (2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3, x[0] * x[1] * x[2]);
 )";
-      TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
-
-      auto ast = ASTBuilder::build(input);
-
-      ASTModulesImporter{*ast};
-      ASTNodeTypeCleaner<language::import_instruction>{*ast};
-
-      ASTSymbolTableBuilder{*ast};
-      ASTNodeDataTypeBuilder{*ast};
-
-      ASTNodeTypeCleaner<language::var_declaration>{*ast};
-      ASTNodeTypeCleaner<language::fct_declaration>{*ast};
-      ASTNodeExpressionBuilder{*ast};
-
-      std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
-
-      TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
-      position.byte = data.size();   // ensure that variables are declared at this point
-
-      SECTION("scalar_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
-          });
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("scalar_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<double> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i]                  = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
-          });
-        Array<const double> interpolate_value =
-          InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1] + 2 * x[2], 2 * x[2]};
-          });
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R3_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyVector<3>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
-          });
-        Array<const TinyVector<3>> interpolate_value =
-          InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_affine_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_affine_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] =
-              TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2], 2 * x[0] + x[1] + x[2], 2};
-          });
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
-      }
-
-      SECTION("R2x2_non_linear_3d")
-      {
-        auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
-        REQUIRE(found);
-        REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
-
-        FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
-
-        Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
-        parallel_for(
-          cell_value.size(), PUGS_LAMBDA(const size_t i) {
-            const TinyVector<Dimension>& x = xj[cell_id_list[i]];
-            cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
-                                          x[0] * x[1] * x[2]};
-          });
-        Array<const TinyMatrix<2>> interpolate_value =
-          InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
-
-        REQUIRE(same_cell_value(cell_value, interpolate_value));
+          TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+
+          auto ast = ASTBuilder::build(input);
+
+          ASTModulesImporter{*ast};
+          ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+          ASTSymbolTableBuilder{*ast};
+          ASTNodeDataTypeBuilder{*ast};
+
+          ASTNodeTypeCleaner<language::var_declaration>{*ast};
+          ASTNodeTypeCleaner<language::fct_declaration>{*ast};
+          ASTNodeExpressionBuilder{*ast};
+
+          std::shared_ptr<SymbolTable> symbol_table = ast->m_symbol_table;
+
+          TAO_PEGTL_NAMESPACE::position position{TAO_PEGTL_NAMESPACE::internal::iterator{"fixture"}, "fixture"};
+          position.byte = data.size();   // ensure that variables are declared at this point
+
+          SECTION("scalar_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * x[0] + 3 * x[1] + 2 * x[2] - 1;
+              });
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("scalar_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("scalar_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<double> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i]                  = 2 * exp(x[0]) * sin(x[1]) * x[2] + 3;
+              });
+            Array<const double> interpolate_value =
+              InterpolateItemValue<double(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj, cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyVector<3>{2 * x[0] + 3 * x[1] + 2, 3 * x[0] + x[1] + 2 * x[2], 2 * x[2]};
+              });
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R3_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R3_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyVector<3>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyVector<3>{2 * exp(x[0]) * sin(x[1]) + x[2] + 3, x[0] * x[2] - 2 * x[1], 3};
+              });
+            Array<const TinyVector<3>> interpolate_value =
+              InterpolateItemValue<TinyVector<3>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_affine_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_affine_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * x[0] + 3 * x[1] + 2 * x[2] + 1, 3 * x[0] + x[1] + 2 * x[2],
+                                              2 * x[0] + x[1] + x[2], 2};
+              });
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+
+          SECTION("R2x2_non_linear_3d")
+          {
+            auto [i_symbol, found] = symbol_table->find("R2x2_non_linear_3d", position);
+            REQUIRE(found);
+            REQUIRE(i_symbol->attributes().dataType() == ASTNodeDataType::function_t);
+
+            FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
+
+            Array<TinyMatrix<2>> cell_value{cell_id_list.size()};
+            parallel_for(
+              cell_value.size(), PUGS_LAMBDA(const size_t i) {
+                const TinyVector<Dimension>& x = xj[cell_id_list[i]];
+                cell_value[i] = TinyMatrix<2>{2 * exp(x[0]) * sin(x[1]) + 3 * cos(x[2]), sin(x[0] - 2 * x[1] * x[2]), 3,
+                                              x[0] * x[1] * x[2]};
+              });
+            Array<const TinyMatrix<2>> interpolate_value =
+              InterpolateItemValue<TinyMatrix<2>(TinyVector<Dimension>)>::interpolate(function_symbol_id, xj,
+                                                                                      cell_id_list);
+
+            REQUIRE(same_item_value(cell_value, interpolate_value));
+          }
+        }
       }
     }
   }
diff --git a/tests/test_ItemArray.cpp b/tests/test_ItemArray.cpp
index 17337200786737dcc7b98c75eb18021cfd62a82d..9b3b95fded76400eb5953d4a3d70f50481dadde8 100644
--- a/tests/test_ItemArray.cpp
+++ b/tests/test_ItemArray.cpp
@@ -28,118 +28,150 @@ TEST_CASE("ItemArray", "[mesh]")
 
   SECTION("1D")
   {
-    const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-    const Connectivity<1>& connectivity  = mesh_1d.connectivity();
-
-    REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(CellArray<int>{connectivity, 3});
-
-    REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(CellArray<int>{connectivity, 3}.isBuilt());
-
-    NodeArray<int> node_value{connectivity, 3};
-    EdgeArray<int> edge_value{connectivity, 3};
-    FaceArray<int> face_value{connectivity, 3};
-    CellArray<int> cell_value{connectivity, 3};
-
-    REQUIRE(edge_value.numberOfItems() == node_value.numberOfItems());
-    REQUIRE(face_value.numberOfItems() == node_value.numberOfItems());
-    REQUIRE(cell_value.numberOfItems() + 1 == node_value.numberOfItems());
-
-    REQUIRE(node_value.sizeOfArrays() == 3);
-    REQUIRE(edge_value.sizeOfArrays() == 3);
-    REQUIRE(face_value.sizeOfArrays() == 3);
-    REQUIRE(cell_value.sizeOfArrays() == 3);
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_1d = named_mesh.mesh();
+
+        const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+        REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(CellArray<int>{connectivity, 3});
+
+        REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(CellArray<int>{connectivity, 3}.isBuilt());
+
+        NodeArray<int> node_value{connectivity, 3};
+        EdgeArray<int> edge_value{connectivity, 3};
+        FaceArray<int> face_value{connectivity, 3};
+        CellArray<int> cell_value{connectivity, 3};
+
+        REQUIRE(edge_value.numberOfItems() == node_value.numberOfItems());
+        REQUIRE(face_value.numberOfItems() == node_value.numberOfItems());
+        REQUIRE(cell_value.numberOfItems() + 1 == node_value.numberOfItems());
+
+        REQUIRE(node_value.sizeOfArrays() == 3);
+        REQUIRE(edge_value.sizeOfArrays() == 3);
+        REQUIRE(face_value.sizeOfArrays() == 3);
+        REQUIRE(cell_value.sizeOfArrays() == 3);
+      }
+    }
   }
 
   SECTION("2D")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
-
-    REQUIRE_NOTHROW(NodeArray<int>{connectivity, 2});
-    REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 2});
-    REQUIRE_NOTHROW(FaceArray<int>{connectivity, 2});
-    REQUIRE_NOTHROW(CellArray<int>{connectivity, 2});
-
-    REQUIRE(NodeArray<int>{connectivity, 2}.isBuilt());
-    REQUIRE(EdgeArray<int>{connectivity, 2}.isBuilt());
-    REQUIRE(FaceArray<int>{connectivity, 2}.isBuilt());
-    REQUIRE(CellArray<int>{connectivity, 2}.isBuilt());
-
-    NodeArray<int> node_value{connectivity, 2};
-    EdgeArray<int> edge_value{connectivity, 2};
-    FaceArray<int> face_value{connectivity, 2};
-    CellArray<int> cell_value{connectivity, 2};
-
-    REQUIRE(edge_value.numberOfItems() == face_value.numberOfItems());
-
-    REQUIRE(node_value.sizeOfArrays() == 2);
-    REQUIRE(edge_value.sizeOfArrays() == 2);
-    REQUIRE(face_value.sizeOfArrays() == 2);
-    REQUIRE(cell_value.sizeOfArrays() == 2);
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_2d = named_mesh.mesh();
+
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+        REQUIRE_NOTHROW(NodeArray<int>{connectivity, 2});
+        REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 2});
+        REQUIRE_NOTHROW(FaceArray<int>{connectivity, 2});
+        REQUIRE_NOTHROW(CellArray<int>{connectivity, 2});
+
+        REQUIRE(NodeArray<int>{connectivity, 2}.isBuilt());
+        REQUIRE(EdgeArray<int>{connectivity, 2}.isBuilt());
+        REQUIRE(FaceArray<int>{connectivity, 2}.isBuilt());
+        REQUIRE(CellArray<int>{connectivity, 2}.isBuilt());
+
+        NodeArray<int> node_value{connectivity, 2};
+        EdgeArray<int> edge_value{connectivity, 2};
+        FaceArray<int> face_value{connectivity, 2};
+        CellArray<int> cell_value{connectivity, 2};
+
+        REQUIRE(edge_value.numberOfItems() == face_value.numberOfItems());
+
+        REQUIRE(node_value.sizeOfArrays() == 2);
+        REQUIRE(edge_value.sizeOfArrays() == 2);
+        REQUIRE(face_value.sizeOfArrays() == 2);
+        REQUIRE(cell_value.sizeOfArrays() == 2);
+      }
+    }
   }
 
   SECTION("3D")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-    REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3});
-    REQUIRE_NOTHROW(CellArray<int>{connectivity, 3});
-
-    REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt());
-    REQUIRE(CellArray<int>{connectivity, 3}.isBuilt());
-
-    NodeArray<int> node_value{connectivity, 3};
-    EdgeArray<int> edge_value{connectivity, 3};
-    FaceArray<int> face_value{connectivity, 3};
-    CellArray<int> cell_value{connectivity, 3};
-
-    REQUIRE(node_value.sizeOfArrays() == 3);
-    REQUIRE(edge_value.sizeOfArrays() == 3);
-    REQUIRE(face_value.sizeOfArrays() == 3);
-    REQUIRE(cell_value.sizeOfArrays() == 3);
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
+
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+        REQUIRE_NOTHROW(NodeArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(EdgeArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(FaceArray<int>{connectivity, 3});
+        REQUIRE_NOTHROW(CellArray<int>{connectivity, 3});
+
+        REQUIRE(NodeArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(EdgeArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(FaceArray<int>{connectivity, 3}.isBuilt());
+        REQUIRE(CellArray<int>{connectivity, 3}.isBuilt());
+
+        NodeArray<int> node_value{connectivity, 3};
+        EdgeArray<int> edge_value{connectivity, 3};
+        FaceArray<int> face_value{connectivity, 3};
+        CellArray<int> cell_value{connectivity, 3};
+
+        REQUIRE(node_value.sizeOfArrays() == 3);
+        REQUIRE(edge_value.sizeOfArrays() == 3);
+        REQUIRE(face_value.sizeOfArrays() == 3);
+        REQUIRE(cell_value.sizeOfArrays() == 3);
+      }
+    }
   }
 
   SECTION("set values from array")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-    CellArray<size_t> cell_array{connectivity, 3};
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
 
-    Table<size_t> table{cell_array.numberOfItems(), cell_array.sizeOfArrays()};
-    {
-      size_t k = 0;
-      for (size_t i = 0; i < table.numberOfRows(); ++i) {
-        for (size_t j = 0; j < table.numberOfColumns(); ++j) {
-          table(i, j) = k++;
-        }
-      }
-    }
-    cell_array = table;
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-    auto is_same = [](const CellArray<size_t>& cell_array, const Table<size_t>& table) {
-      bool is_same = true;
-      for (CellId cell_id = 0; cell_id < cell_array.numberOfItems(); ++cell_id) {
-        Array sub_array = cell_array[cell_id];
-        for (size_t i = 0; i < sub_array.size(); ++i) {
-          is_same &= (sub_array[i] == table(cell_id, i));
+        CellArray<size_t> cell_array{connectivity, 3};
+
+        Table<size_t> table{cell_array.numberOfItems(), cell_array.sizeOfArrays()};
+        {
+          size_t k = 0;
+          for (size_t i = 0; i < table.numberOfRows(); ++i) {
+            for (size_t j = 0; j < table.numberOfColumns(); ++j) {
+              table(i, j) = k++;
+            }
+          }
         }
+        cell_array = table;
+
+        auto is_same = [](const CellArray<size_t>& cell_array, const Table<size_t>& table) {
+          bool is_same = true;
+          for (CellId cell_id = 0; cell_id < cell_array.numberOfItems(); ++cell_id) {
+            Array sub_array = cell_array[cell_id];
+            for (size_t i = 0; i < sub_array.size(); ++i) {
+              is_same &= (sub_array[i] == table(cell_id, i));
+            }
+          }
+          return is_same;
+        };
+
+        REQUIRE(is_same(cell_array, table));
       }
-      return is_same;
-    };
-
-    REQUIRE(is_same(cell_array, table));
+    }
   }
 
   SECTION("copy")
@@ -155,43 +187,88 @@ TEST_CASE("ItemArray", "[mesh]")
       return is_same;
     };
 
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
 
-    CellArray<int> cell_array{connectivity, 4};
-    cell_array.fill(parallel::rank());
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-    CellArray<const int> cell_array_const_view{cell_array};
-    REQUIRE(cell_array.numberOfItems() == cell_array_const_view.numberOfItems());
-    REQUIRE(cell_array.sizeOfArrays() == cell_array_const_view.sizeOfArrays());
-    REQUIRE(is_same(cell_array_const_view, static_cast<std::int64_t>(parallel::rank())));
+        CellArray<int> cell_array{connectivity, 4};
+        cell_array.fill(parallel::rank());
 
-    CellArray<const int> const_cell_array;
-    const_cell_array = copy(cell_array);
+        CellArray<const int> cell_array_const_view{cell_array};
+        REQUIRE(cell_array.numberOfItems() == cell_array_const_view.numberOfItems());
+        REQUIRE(cell_array.sizeOfArrays() == cell_array_const_view.sizeOfArrays());
+        REQUIRE(is_same(cell_array_const_view, static_cast<std::int64_t>(parallel::rank())));
 
-    CellArray<int> duplicated_cell_array{connectivity, cell_array.sizeOfArrays()};
-    copy_to(const_cell_array, duplicated_cell_array);
+        CellArray<const int> const_cell_array;
+        const_cell_array = copy(cell_array);
 
-    cell_array.fill(0);
+        CellArray<int> duplicated_cell_array{connectivity, cell_array.sizeOfArrays()};
+        copy_to(const_cell_array, duplicated_cell_array);
 
-    REQUIRE(is_same(cell_array, 0));
-    REQUIRE(is_same(cell_array_const_view, 0));
-    REQUIRE(is_same(const_cell_array, static_cast<std::int64_t>(parallel::rank())));
-    REQUIRE(is_same(duplicated_cell_array, static_cast<std::int64_t>(parallel::rank())));
+        cell_array.fill(0);
+
+        REQUIRE(is_same(cell_array, 0));
+        REQUIRE(is_same(cell_array_const_view, 0));
+        REQUIRE(is_same(const_cell_array, static_cast<std::int64_t>(parallel::rank())));
+        REQUIRE(is_same(duplicated_cell_array, static_cast<std::int64_t>(parallel::rank())));
+      }
+    }
   }
 
   SECTION("WeakItemArray")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_2d = named_mesh.mesh();
+
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+        WeakFaceArray<int> weak_face_array{connectivity, 5};
+        for (FaceId face_id = 0; face_id < mesh_2d->numberOfFaces(); ++face_id) {
+          for (size_t i = 0; i < weak_face_array.sizeOfArrays(); ++i) {
+            weak_face_array[face_id][i] = 2 * face_id + 3 * i;
+          }
+        }
+
+        FaceArray<const int> face_array{weak_face_array};
+
+        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
 
-    WeakFaceArray<int> weak_face_array{connectivity, 5};
+        FaceArray<int> copied_face_array = copy(weak_face_array);
+        REQUIRE(copied_face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+        REQUIRE(weak_face_array.sizeOfArrays() == copied_face_array.sizeOfArrays());
 
-    weak_face_array.fill(parallel::rank());
+        weak_face_array.fill(0);
 
-    FaceArray<const int> face_array{weak_face_array};
+        {
+          bool is_same = true;
+          for (FaceId face_id = 0; face_id < mesh_2d->numberOfFaces(); ++face_id) {
+            for (size_t i = 0; i < face_array.sizeOfArrays(); ++i) {
+              is_same &= (face_array[face_id][i] == 0);
+            }
+          }
+          REQUIRE(is_same);
+        }
 
-    REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+        {
+          bool is_same = true;
+          for (FaceId face_id = 0; face_id < mesh_2d->numberOfFaces(); ++face_id) {
+            for (size_t i = 0; i < copied_face_array.sizeOfArrays(); ++i) {
+              is_same &= (copied_face_array[face_id][i] == static_cast<int>(2 * face_id + 3 * i));
+            }
+          }
+          REQUIRE(is_same);
+        }
+      }
+    }
   }
 
 #ifndef NDEBUG
@@ -214,35 +291,51 @@ TEST_CASE("ItemArray", "[mesh]")
 
     SECTION("checking for bounds violation")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
+
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      CellArray<int> cell_array{connectivity, 1};
-      CellId invalid_cell_id = connectivity.numberOfCells();
-      REQUIRE_THROWS_AS(cell_array[invalid_cell_id], AssertError);
+          CellArray<int> cell_array{connectivity, 1};
+          CellId invalid_cell_id = connectivity.numberOfCells();
+          REQUIRE_THROWS_AS(cell_array[invalid_cell_id], AssertError);
 
-      FaceArray<int> face_array{connectivity, 2};
-      FaceId invalid_face_id = connectivity.numberOfFaces();
-      REQUIRE_THROWS_AS(face_array[invalid_face_id], AssertError);
+          FaceArray<int> face_array{connectivity, 2};
+          FaceId invalid_face_id = connectivity.numberOfFaces();
+          REQUIRE_THROWS_AS(face_array[invalid_face_id], AssertError);
 
-      EdgeArray<int> edge_array{connectivity, 1};
-      EdgeId invalid_edge_id = connectivity.numberOfEdges();
-      REQUIRE_THROWS_AS(edge_array[invalid_edge_id], AssertError);
+          EdgeArray<int> edge_array{connectivity, 1};
+          EdgeId invalid_edge_id = connectivity.numberOfEdges();
+          REQUIRE_THROWS_AS(edge_array[invalid_edge_id], AssertError);
 
-      NodeArray<int> node_array{connectivity, 0};
-      NodeId invalid_node_id = connectivity.numberOfNodes();
-      REQUIRE_THROWS_AS(node_array[invalid_node_id], AssertError);
+          NodeArray<int> node_array{connectivity, 0};
+          NodeId invalid_node_id = connectivity.numberOfNodes();
+          REQUIRE_THROWS_AS(node_array[invalid_node_id], AssertError);
+        }
+      }
     }
 
     SECTION("set values from invalid array size")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-      CellArray<size_t> cell_array{connectivity, 2};
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      Table<size_t> values{3, connectivity.numberOfCells() + 3};
-      REQUIRE_THROWS_AS(cell_array = values, AssertError);
+          CellArray<size_t> cell_array{connectivity, 2};
+
+          Table<size_t> values{3, connectivity.numberOfCells() + 3};
+          REQUIRE_THROWS_AS(cell_array = values, AssertError);
+        }
+      }
     }
   }
 #endif   // NDEBUG
diff --git a/tests/test_ItemArrayUtils.cpp b/tests/test_ItemArrayUtils.cpp
index 6eb6894d41aa3700fdc86e6c0d7c9d83acda328e..af91ecb70897073a4069f20457190b6e20318e72 100644
--- a/tests/test_ItemArrayUtils.cpp
+++ b/tests/test_ItemArrayUtils.cpp
@@ -19,750 +19,774 @@ TEST_CASE("ItemArrayUtils", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
-
-      SECTION("node")
-      {
-        WeakNodeArray<size_t> weak_node_array{connectivity, 4};
-        auto node_number = connectivity.nodeNumber();
-
-        for (NodeId i_node = 0; i_node < mesh_1d.numberOfNodes(); ++i_node) {
-          Array array         = weak_node_array[i_node];
-          const size_t number = node_number[i_node];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-        NodeArray<const size_t> node_array{weak_node_array};
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
 
-        REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
-        {   // before synchronization
-          auto node_owner    = connectivity.nodeOwner();
-          auto node_is_owned = connectivity.nodeIsOwned();
+          SECTION("node")
+          {
+            WeakNodeArray<size_t> weak_node_array{connectivity, 4};
+            auto node_number = connectivity.nodeNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (NodeId i_node = 0; i_node < mesh_1d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            if (node_is_owned[i_node]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
+            for (NodeId i_node = 0; i_node < mesh_1d->numberOfNodes(); ++i_node) {
+              Array array         = weak_node_array[i_node];
+              const size_t number = node_number[i_node];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_node_array);
-
-        {   // after synchronization
-          auto node_owner = connectivity.nodeOwner();
+            NodeArray<const size_t> node_array{weak_node_array};
+
+            REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto node_owner    = connectivity.nodeOwner();
+              auto node_is_owned = connectivity.nodeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (NodeId i_node = 0; i_node < mesh_1d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                if (node_is_owned[i_node]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (NodeId i_node = 0; i_node < mesh_1d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
-
-          REQUIRE(is_synchronized);
-        }
-      }
 
-      SECTION("edge")
-      {
-        WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
-        auto edge_number = connectivity.edgeNumber();
+            synchronize(weak_node_array);
 
-        for (EdgeId i_edge = 0; i_edge < mesh_1d.numberOfEdges(); ++i_edge) {
-          Array array         = weak_edge_array[i_edge];
-          const size_t number = edge_number[i_edge];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+            {   // after synchronization
+              auto node_owner = connectivity.nodeOwner();
 
-        EdgeArray<const size_t> edge_array{weak_edge_array};
+              bool is_synchronized = true;
+              for (NodeId i_node = 0; i_node < mesh_1d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto edge_owner    = connectivity.edgeOwner();
-          auto edge_is_owned = connectivity.edgeIsOwned();
+          SECTION("edge")
+          {
+            WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
+            auto edge_number = connectivity.edgeNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_1d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            if (edge_is_owned[i_edge]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
+            for (EdgeId i_edge = 0; i_edge < mesh_1d->numberOfEdges(); ++i_edge) {
+              Array array         = weak_edge_array[i_edge];
+              const size_t number = edge_number[i_edge];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_edge_array);
 
-        {   // after synchronization
-          auto edge_owner = connectivity.edgeOwner();
+            EdgeArray<const size_t> edge_array{weak_edge_array};
+
+            REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto edge_owner    = connectivity.edgeOwner();
+              auto edge_is_owned = connectivity.edgeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_1d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                if (edge_is_owned[i_edge]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_1d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_synchronized);
-        }
-      }
+            synchronize(weak_edge_array);
 
-      SECTION("face")
-      {
-        WeakFaceArray<size_t> weak_face_array{connectivity, 4};
-        auto face_number = connectivity.faceNumber();
+            {   // after synchronization
+              auto edge_owner = connectivity.edgeOwner();
 
-        for (FaceId i_face = 0; i_face < mesh_1d.numberOfFaces(); ++i_face) {
-          Array array         = weak_face_array[i_face];
-          const size_t number = face_number[i_face];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        FaceArray<const size_t> face_array{weak_face_array};
+              bool is_synchronized = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_1d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto face_owner    = connectivity.faceOwner();
-          auto face_is_owned = connectivity.faceIsOwned();
+          SECTION("face")
+          {
+            WeakFaceArray<size_t> weak_face_array{connectivity, 4};
+            auto face_number = connectivity.faceNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (FaceId i_face = 0; i_face < mesh_1d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            if (face_is_owned[i_face]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
+            for (FaceId i_face = 0; i_face < mesh_1d->numberOfFaces(); ++i_face) {
+              Array array         = weak_face_array[i_face];
+              const size_t number = face_number[i_face];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
 
-        synchronize(weak_face_array);
-
-        {   // after synchronization
-          auto face_owner = connectivity.faceOwner();
+            FaceArray<const size_t> face_array{weak_face_array};
+
+            REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto face_owner    = connectivity.faceOwner();
+              auto face_is_owned = connectivity.faceIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (FaceId i_face = 0; i_face < mesh_1d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                if (face_is_owned[i_face]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (FaceId i_face = 0; i_face < mesh_1d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_synchronized);
-        }
-      }
+            synchronize(weak_face_array);
 
-      SECTION("cell")
-      {
-        WeakCellArray<size_t> weak_cell_array{connectivity, 4};
-        auto cell_number = connectivity.cellNumber();
+            {   // after synchronization
+              auto face_owner = connectivity.faceOwner();
 
-        for (CellId i_cell = 0; i_cell < mesh_1d.numberOfCells(); ++i_cell) {
-          Array array         = weak_cell_array[i_cell];
-          const size_t number = cell_number[i_cell];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        CellArray<const size_t> cell_array{weak_cell_array};
+              bool is_synchronized = true;
+              for (FaceId i_face = 0; i_face < mesh_1d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto cell_owner    = connectivity.cellOwner();
-          auto cell_is_owned = connectivity.cellIsOwned();
+          SECTION("cell")
+          {
+            WeakCellArray<size_t> weak_cell_array{connectivity, 4};
+            auto cell_number = connectivity.cellNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (CellId i_cell = 0; i_cell < mesh_1d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            if (cell_is_owned[i_cell]) {
+            for (CellId i_cell = 0; i_cell < mesh_1d->numberOfCells(); ++i_cell) {
+              Array array         = weak_cell_array[i_cell];
+              const size_t number = cell_number[i_cell];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+            }
+
+            CellArray<const size_t> cell_array{weak_cell_array};
+
+            REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto cell_owner    = connectivity.cellOwner();
+              auto cell_is_owned = connectivity.cellIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (CellId i_cell = 0; i_cell < mesh_1d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                if (cell_is_owned[i_cell]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
               }
+
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
+            synchronize(weak_cell_array);
 
-        synchronize(weak_cell_array);
+            {   // after synchronization
+              auto cell_owner = connectivity.cellOwner();
 
-        {   // after synchronization
-          auto cell_owner = connectivity.cellOwner();
+              bool is_synchronized = true;
+              for (CellId i_cell = 0; i_cell < mesh_1d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-          bool is_synchronized = true;
-          for (CellId i_cell = 0; i_cell < mesh_1d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_synchronized);
             }
           }
-
-          REQUIRE(is_synchronized);
         }
       }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
-
-      SECTION("node")
-      {
-        WeakNodeArray<size_t> weak_node_array{connectivity, 3};
-        auto node_number = connectivity.nodeNumber();
-
-        for (NodeId i_node = 0; i_node < mesh_2d.numberOfNodes(); ++i_node) {
-          Array array         = weak_node_array[i_node];
-          const size_t number = node_number[i_node];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        NodeArray<const size_t> node_array{weak_node_array};
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
 
-        REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-        {   // before synchronization
-          auto node_owner    = connectivity.nodeOwner();
-          auto node_is_owned = connectivity.nodeIsOwned();
+          SECTION("node")
+          {
+            WeakNodeArray<size_t> weak_node_array{connectivity, 3};
+            auto node_number = connectivity.nodeNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (NodeId i_node = 0; i_node < mesh_2d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            if (node_is_owned[i_node]) {
+            for (NodeId i_node = 0; i_node < mesh_2d->numberOfNodes(); ++i_node) {
+              Array array         = weak_node_array[i_node];
+              const size_t number = node_number[i_node];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_node_array);
 
-        {   // after synchronization
-          auto node_owner = connectivity.nodeOwner();
+            NodeArray<const size_t> node_array{weak_node_array};
+
+            REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto node_owner    = connectivity.nodeOwner();
+              auto node_is_owned = connectivity.nodeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (NodeId i_node = 0; i_node < mesh_2d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                if (node_is_owned[i_node]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (NodeId i_node = 0; i_node < mesh_2d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_synchronized);
-        }
-      }
+            synchronize(weak_node_array);
 
-      SECTION("edge")
-      {
-        WeakEdgeArray<size_t> weak_edge_array{connectivity, 3};
-        auto edge_number = connectivity.edgeNumber();
+            {   // after synchronization
+              auto node_owner = connectivity.nodeOwner();
 
-        for (EdgeId i_edge = 0; i_edge < mesh_2d.numberOfEdges(); ++i_edge) {
-          Array array         = weak_edge_array[i_edge];
-          const size_t number = edge_number[i_edge];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        EdgeArray<const size_t> edge_array{weak_edge_array};
+              bool is_synchronized = true;
+              for (NodeId i_node = 0; i_node < mesh_2d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto edge_owner    = connectivity.edgeOwner();
-          auto edge_is_owned = connectivity.edgeIsOwned();
+          SECTION("edge")
+          {
+            WeakEdgeArray<size_t> weak_edge_array{connectivity, 3};
+            auto edge_number = connectivity.edgeNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_2d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            if (edge_is_owned[i_edge]) {
+            for (EdgeId i_edge = 0; i_edge < mesh_2d->numberOfEdges(); ++i_edge) {
+              Array array         = weak_edge_array[i_edge];
+              const size_t number = edge_number[i_edge];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_edge_array);
 
-        {   // after synchronization
-          auto edge_owner = connectivity.edgeOwner();
+            EdgeArray<const size_t> edge_array{weak_edge_array};
+
+            REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto edge_owner    = connectivity.edgeOwner();
+              auto edge_is_owned = connectivity.edgeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_2d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                if (edge_is_owned[i_edge]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_2d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
-
-          REQUIRE(is_synchronized);
-        }
-      }
 
-      SECTION("face")
-      {
-        WeakFaceArray<size_t> weak_face_array{connectivity, 3};
-        auto face_number = connectivity.faceNumber();
+            synchronize(weak_edge_array);
 
-        for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-          Array array         = weak_face_array[i_face];
-          const size_t number = face_number[i_face];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+            {   // after synchronization
+              auto edge_owner = connectivity.edgeOwner();
 
-        FaceArray<const size_t> face_array{weak_face_array};
+              bool is_synchronized = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_2d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto face_owner    = connectivity.faceOwner();
-          auto face_is_owned = connectivity.faceIsOwned();
+          SECTION("face")
+          {
+            WeakFaceArray<size_t> weak_face_array{connectivity, 3};
+            auto face_number = connectivity.faceNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            if (face_is_owned[i_face]) {
+            for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+              Array array         = weak_face_array[i_face];
+              const size_t number = face_number[i_face];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_face_array);
 
-        {   // after synchronization
-          auto face_owner = connectivity.faceOwner();
+            FaceArray<const size_t> face_array{weak_face_array};
+
+            REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto face_owner    = connectivity.faceOwner();
+              auto face_is_owned = connectivity.faceIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                if (face_is_owned[i_face]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_synchronized);
-        }
-      }
+            synchronize(weak_face_array);
 
-      SECTION("cell")
-      {
-        WeakCellArray<size_t> weak_cell_array{connectivity, 3};
-        auto cell_number = connectivity.cellNumber();
+            {   // after synchronization
+              auto face_owner = connectivity.faceOwner();
 
-        for (CellId i_cell = 0; i_cell < mesh_2d.numberOfCells(); ++i_cell) {
-          Array array         = weak_cell_array[i_cell];
-          const size_t number = cell_number[i_cell];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        CellArray<const size_t> cell_array{weak_cell_array};
+              bool is_synchronized = true;
+              for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto cell_owner    = connectivity.cellOwner();
-          auto cell_is_owned = connectivity.cellIsOwned();
+          SECTION("cell")
+          {
+            WeakCellArray<size_t> weak_cell_array{connectivity, 3};
+            auto cell_number = connectivity.cellNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (CellId i_cell = 0; i_cell < mesh_2d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            if (cell_is_owned[i_cell]) {
+            for (CellId i_cell = 0; i_cell < mesh_2d->numberOfCells(); ++i_cell) {
+              Array array         = weak_cell_array[i_cell];
+              const size_t number = cell_number[i_cell];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+            }
+
+            CellArray<const size_t> cell_array{weak_cell_array};
+
+            REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto cell_owner    = connectivity.cellOwner();
+              auto cell_is_owned = connectivity.cellIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (CellId i_cell = 0; i_cell < mesh_2d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                if (cell_is_owned[i_cell]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
               }
+
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
+            synchronize(weak_cell_array);
 
-        synchronize(weak_cell_array);
+            {   // after synchronization
+              auto cell_owner = connectivity.cellOwner();
 
-        {   // after synchronization
-          auto cell_owner = connectivity.cellOwner();
+              bool is_synchronized = true;
+              for (CellId i_cell = 0; i_cell < mesh_2d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-          bool is_synchronized = true;
-          for (CellId i_cell = 0; i_cell < mesh_2d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_synchronized);
             }
           }
-
-          REQUIRE(is_synchronized);
         }
       }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-      SECTION("node")
-      {
-        WeakNodeArray<size_t> weak_node_array{connectivity, 4};
-        auto node_number = connectivity.nodeNumber();
-
-        for (NodeId i_node = 0; i_node < mesh_3d.numberOfNodes(); ++i_node) {
-          Array array         = weak_node_array[i_node];
-          const size_t number = node_number[i_node];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        NodeArray<const size_t> node_array{weak_node_array};
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-        REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-        {   // before synchronization
-          auto node_owner    = connectivity.nodeOwner();
-          auto node_is_owned = connectivity.nodeIsOwned();
+          SECTION("node")
+          {
+            WeakNodeArray<size_t> weak_node_array{connectivity, 4};
+            auto node_number = connectivity.nodeNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (NodeId i_node = 0; i_node < mesh_3d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            if (node_is_owned[i_node]) {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
+            for (NodeId i_node = 0; i_node < mesh_3d->numberOfNodes(); ++i_node) {
+              Array array         = weak_node_array[i_node];
+              const size_t number = node_number[i_node];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
-
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
 
-        synchronize(weak_node_array);
-
-        {   // after synchronization
-          auto node_owner = connectivity.nodeOwner();
+            NodeArray<const size_t> node_array{weak_node_array};
+
+            REQUIRE(node_array.connectivity_ptr() == weak_node_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto node_owner    = connectivity.nodeOwner();
+              auto node_is_owned = connectivity.nodeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (NodeId i_node = 0; i_node < mesh_3d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                if (node_is_owned[i_node]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (NodeId i_node = 0; i_node < mesh_3d.numberOfNodes(); ++i_node) {
-            Array array         = node_array[i_node];
-            const size_t number = node_number[i_node];
-            const size_t owner  = node_owner[i_node];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_synchronized);
-        }
-      }
-
-      SECTION("edge")
-      {
-        WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
-        auto edge_number = connectivity.edgeNumber();
+            synchronize(weak_node_array);
 
-        for (EdgeId i_edge = 0; i_edge < mesh_3d.numberOfEdges(); ++i_edge) {
-          Array array         = weak_edge_array[i_edge];
-          const size_t number = edge_number[i_edge];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+            {   // after synchronization
+              auto node_owner = connectivity.nodeOwner();
 
-        EdgeArray<const size_t> edge_array{weak_edge_array};
+              bool is_synchronized = true;
+              for (NodeId i_node = 0; i_node < mesh_3d->numberOfNodes(); ++i_node) {
+                Array array         = node_array[i_node];
+                const size_t number = node_number[i_node];
+                const size_t owner  = node_owner[i_node];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto edge_owner    = connectivity.edgeOwner();
-          auto edge_is_owned = connectivity.edgeIsOwned();
+          SECTION("edge")
+          {
+            WeakEdgeArray<size_t> weak_edge_array{connectivity, 4};
+            auto edge_number = connectivity.edgeNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_3d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            if (edge_is_owned[i_edge]) {
+            for (EdgeId i_edge = 0; i_edge < mesh_3d->numberOfEdges(); ++i_edge) {
+              Array array         = weak_edge_array[i_edge];
+              const size_t number = edge_number[i_edge];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_edge_array);
-
-        {   // after synchronization
-          auto edge_owner = connectivity.edgeOwner();
+            EdgeArray<const size_t> edge_array{weak_edge_array};
+
+            REQUIRE(edge_array.connectivity_ptr() == weak_edge_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto edge_owner    = connectivity.edgeOwner();
+              auto edge_is_owned = connectivity.edgeIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_3d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                if (edge_is_owned[i_edge]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (EdgeId i_edge = 0; i_edge < mesh_3d.numberOfEdges(); ++i_edge) {
-            Array array         = edge_array[i_edge];
-            const size_t number = edge_number[i_edge];
-            const size_t owner  = edge_owner[i_edge];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_synchronized);
-        }
-      }
-
-      SECTION("face")
-      {
-        WeakFaceArray<size_t> weak_face_array{connectivity, 4};
-        auto face_number = connectivity.faceNumber();
+            synchronize(weak_edge_array);
 
-        for (FaceId i_face = 0; i_face < mesh_3d.numberOfFaces(); ++i_face) {
-          Array array         = weak_face_array[i_face];
-          const size_t number = face_number[i_face];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
+            {   // after synchronization
+              auto edge_owner = connectivity.edgeOwner();
 
-        FaceArray<const size_t> face_array{weak_face_array};
+              bool is_synchronized = true;
+              for (EdgeId i_edge = 0; i_edge < mesh_3d->numberOfEdges(); ++i_edge) {
+                Array array         = edge_array[i_edge];
+                const size_t number = edge_number[i_edge];
+                const size_t owner  = edge_owner[i_edge];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto face_owner    = connectivity.faceOwner();
-          auto face_is_owned = connectivity.faceIsOwned();
+          SECTION("face")
+          {
+            WeakFaceArray<size_t> weak_face_array{connectivity, 4};
+            auto face_number = connectivity.faceNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (FaceId i_face = 0; i_face < mesh_3d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            if (face_is_owned[i_face]) {
+            for (FaceId i_face = 0; i_face < mesh_3d->numberOfFaces(); ++i_face) {
+              Array array         = weak_face_array[i_face];
+              const size_t number = face_number[i_face];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
-              }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
             }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
-
-        synchronize(weak_face_array);
-
-        {   // after synchronization
-          auto face_owner = connectivity.faceOwner();
+            FaceArray<const size_t> face_array{weak_face_array};
+
+            REQUIRE(face_array.connectivity_ptr() == weak_face_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto face_owner    = connectivity.faceOwner();
+              auto face_is_owned = connectivity.faceIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (FaceId i_face = 0; i_face < mesh_3d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                if (face_is_owned[i_face]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
+              }
 
-          bool is_synchronized = true;
-          for (FaceId i_face = 0; i_face < mesh_3d.numberOfFaces(); ++i_face) {
-            Array array         = face_array[i_face];
-            const size_t number = face_number[i_face];
-            const size_t owner  = face_owner[i_face];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_synchronized);
-        }
-      }
+            synchronize(weak_face_array);
 
-      SECTION("cell")
-      {
-        WeakCellArray<size_t> weak_cell_array{connectivity, 4};
-        auto cell_number = connectivity.cellNumber();
+            {   // after synchronization
+              auto face_owner = connectivity.faceOwner();
 
-        for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-          Array array         = weak_cell_array[i_cell];
-          const size_t number = cell_number[i_cell];
-          for (size_t i = 0; i < array.size(); ++i) {
-            array[i] = number + (parallel::rank() + 1) * i;
-          }
-        }
-
-        CellArray<const size_t> cell_array{weak_cell_array};
+              bool is_synchronized = true;
+              for (FaceId i_face = 0; i_face < mesh_3d->numberOfFaces(); ++i_face) {
+                Array array         = face_array[i_face];
+                const size_t number = face_number[i_face];
+                const size_t owner  = face_owner[i_face];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-        REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+              REQUIRE(is_synchronized);
+            }
+          }
 
-        {   // before synchronization
-          auto cell_owner    = connectivity.cellOwner();
-          auto cell_is_owned = connectivity.cellIsOwned();
+          SECTION("cell")
+          {
+            WeakCellArray<size_t> weak_cell_array{connectivity, 4};
+            auto cell_number = connectivity.cellNumber();
 
-          bool is_synchronized = (parallel::size() > 1);
-          bool is_valid        = true;
-          for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            if (cell_is_owned[i_cell]) {
+            for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+              Array array         = weak_cell_array[i_cell];
+              const size_t number = cell_number[i_cell];
               for (size_t i = 0; i < array.size(); ++i) {
-                is_valid &= (array[i] == number + (owner + 1) * i);
+                array[i] = number + (parallel::rank() + 1) * i;
               }
-            } else {
-              for (size_t i = 0; i < array.size(); ++i) {
-                is_synchronized &= (array[i] == number + (owner + 1) * i);
+            }
+
+            CellArray<const size_t> cell_array{weak_cell_array};
+
+            REQUIRE(cell_array.connectivity_ptr() == weak_cell_array.connectivity_ptr());
+
+            {   // before synchronization
+              auto cell_owner    = connectivity.cellOwner();
+              auto cell_is_owned = connectivity.cellIsOwned();
+
+              bool is_synchronized = (parallel::size() > 1);
+              bool is_valid        = true;
+              for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                if (cell_is_owned[i_cell]) {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_valid &= (array[i] == number + (owner + 1) * i);
+                  }
+                } else {
+                  for (size_t i = 0; i < array.size(); ++i) {
+                    is_synchronized &= (array[i] == number + (owner + 1) * i);
+                  }
+                }
               }
+
+              REQUIRE(is_valid);
+              REQUIRE(not is_synchronized);
             }
-          }
 
-          REQUIRE(is_valid);
-          REQUIRE(not is_synchronized);
-        }
+            synchronize(weak_cell_array);
 
-        synchronize(weak_cell_array);
+            {   // after synchronization
+              auto cell_owner = connectivity.cellOwner();
 
-        {   // after synchronization
-          auto cell_owner = connectivity.cellOwner();
+              bool is_synchronized = true;
+              for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+                Array array         = cell_array[i_cell];
+                const size_t number = cell_number[i_cell];
+                const size_t owner  = cell_owner[i_cell];
+                for (size_t i = 0; i < array.size(); ++i) {
+                  is_synchronized &= (array[i] == number + (owner + 1) * i);
+                }
+              }
 
-          bool is_synchronized = true;
-          for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-            Array array         = cell_array[i_cell];
-            const size_t number = cell_number[i_cell];
-            const size_t owner  = cell_owner[i_cell];
-            for (size_t i = 0; i < array.size(); ++i) {
-              is_synchronized &= (array[i] == number + (owner + 1) * i);
+              REQUIRE(is_synchronized);
             }
           }
-
-          REQUIRE(is_synchronized);
         }
       }
     }
diff --git a/tests/test_ItemId.cpp b/tests/test_ItemId.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cad3328b06c75d5e57a468b5d7f7efe8a9b36215
--- /dev/null
+++ b/tests/test_ItemId.cpp
@@ -0,0 +1,143 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <mesh/ItemId.hpp>
+
+// Instantiate to ensure full coverage is performed
+template class ItemIdT<ItemType::node>;
+template class ItemIdT<ItemType::edge>;
+template class ItemIdT<ItemType::face>;
+template class ItemIdT<ItemType::cell>;
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("ItemId", "[mesh]")
+{
+  SECTION("NodeId")
+  {
+    NodeId node_id0(13);
+    REQUIRE(node_id0 == 13);
+
+    NodeId node_id1 = 0;
+    REQUIRE(node_id1 == 0);
+
+    REQUIRE(++node_id1 == 1);
+
+    REQUIRE(node_id1++ == 1);
+    REQUIRE(node_id1 == 2);
+
+    NodeId node_id2{node_id1};
+    REQUIRE(node_id2 == 2);
+
+    node_id2 = 17;
+    REQUIRE(node_id2 == 17);
+
+    node_id2 = node_id1;
+    REQUIRE(node_id2 == 2);
+
+    NodeId node_id3;
+    {
+      NodeId tmp_node_id{12};
+      NodeId node_id4{std::move(tmp_node_id)};
+      REQUIRE(node_id4 == 12);
+      node_id3 = std::move(node_id4);
+    }
+    REQUIRE(node_id3 == 12);
+  }
+
+  SECTION("EdgeId")
+  {
+    EdgeId edge_id0(13);
+    REQUIRE(edge_id0 == 13);
+
+    EdgeId edge_id1 = 0;
+    REQUIRE(edge_id1 == 0);
+
+    REQUIRE(++edge_id1 == 1);
+
+    REQUIRE(edge_id1++ == 1);
+    REQUIRE(edge_id1 == 2);
+
+    EdgeId edge_id2{edge_id1};
+    REQUIRE(edge_id2 == 2);
+
+    edge_id2 = 17;
+    REQUIRE(edge_id2 == 17);
+
+    edge_id2 = edge_id1;
+    REQUIRE(edge_id2 == 2);
+
+    EdgeId edge_id3;
+    {
+      EdgeId tmp_edge_id{12};
+      EdgeId edge_id4{std::move(tmp_edge_id)};
+      REQUIRE(edge_id4 == 12);
+      edge_id3 = std::move(edge_id4);
+    }
+    REQUIRE(edge_id3 == 12);
+  }
+
+  SECTION("FaceId")
+  {
+    FaceId face_id0(13);
+    REQUIRE(face_id0 == 13);
+
+    FaceId face_id1 = 0;
+    REQUIRE(face_id1 == 0);
+
+    REQUIRE(++face_id1 == 1);
+
+    REQUIRE(face_id1++ == 1);
+    REQUIRE(face_id1 == 2);
+
+    FaceId face_id2{face_id1};
+    REQUIRE(face_id2 == 2);
+
+    face_id2 = 17;
+    REQUIRE(face_id2 == 17);
+
+    face_id2 = face_id1;
+    REQUIRE(face_id2 == 2);
+
+    FaceId face_id3;
+    {
+      FaceId tmp_face_id{12};
+      FaceId face_id4{std::move(tmp_face_id)};
+      REQUIRE(face_id4 == 12);
+      face_id3 = std::move(face_id4);
+    }
+    REQUIRE(face_id3 == 12);
+  }
+
+  SECTION("CellId")
+  {
+    CellId cell_id0(13);
+    REQUIRE(cell_id0 == 13);
+
+    CellId cell_id1 = 0;
+    REQUIRE(cell_id1 == 0);
+
+    REQUIRE(++cell_id1 == 1);
+
+    REQUIRE(cell_id1++ == 1);
+    REQUIRE(cell_id1 == 2);
+
+    CellId cell_id2{cell_id1};
+    REQUIRE(cell_id2 == 2);
+
+    cell_id2 = 17;
+    REQUIRE(cell_id2 == 17);
+
+    cell_id2 = cell_id1;
+    REQUIRE(cell_id2 == 2);
+
+    CellId cell_id3;
+    {
+      CellId tmp_cell_id{12};
+      CellId cell_id4{std::move(tmp_cell_id)};
+      REQUIRE(cell_id4 == 12);
+      cell_id3 = std::move(cell_id4);
+    }
+    REQUIRE(cell_id3 == 12);
+  }
+}
diff --git a/tests/test_ItemValue.cpp b/tests/test_ItemValue.cpp
index 1c4ab49faf757cc0b2e871f9bf4e0ba33061ece7..5ec3fdb892eb5474b80381a250d1f2dc487574de 100644
--- a/tests/test_ItemValue.cpp
+++ b/tests/test_ItemValue.cpp
@@ -26,119 +26,196 @@ TEST_CASE("ItemValue", "[mesh]")
 
   SECTION("1D")
   {
-    const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-    const Connectivity<1>& connectivity  = mesh_1d.connectivity();
-
-    REQUIRE_NOTHROW(NodeValue<int>{connectivity});
-    REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
-    REQUIRE_NOTHROW(FaceValue<int>{connectivity});
-    REQUIRE_NOTHROW(CellValue<int>{connectivity});
-
-    REQUIRE(NodeValue<int>{connectivity}.isBuilt());
-    REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
-    REQUIRE(FaceValue<int>{connectivity}.isBuilt());
-    REQUIRE(CellValue<int>{connectivity}.isBuilt());
-
-    NodeValue<int> node_value{connectivity};
-    EdgeValue<int> edge_value{connectivity};
-    FaceValue<int> face_value{connectivity};
-    CellValue<int> cell_value{connectivity};
-
-    REQUIRE(edge_value.numberOfItems() == node_value.numberOfItems());
-    REQUIRE(face_value.numberOfItems() == node_value.numberOfItems());
-    REQUIRE(cell_value.numberOfItems() + 1 == node_value.numberOfItems());
+    std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_1d = named_mesh.mesh();
+
+        const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+        REQUIRE_NOTHROW(NodeValue<int>{connectivity});
+        REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
+        REQUIRE_NOTHROW(FaceValue<int>{connectivity});
+        REQUIRE_NOTHROW(CellValue<int>{connectivity});
+
+        REQUIRE(NodeValue<int>{connectivity}.isBuilt());
+        REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
+        REQUIRE(FaceValue<int>{connectivity}.isBuilt());
+        REQUIRE(CellValue<int>{connectivity}.isBuilt());
+
+        NodeValue<int> node_value{connectivity};
+        EdgeValue<int> edge_value{connectivity};
+        FaceValue<int> face_value{connectivity};
+        CellValue<int> cell_value{connectivity};
+
+        REQUIRE(edge_value.numberOfItems() == node_value.numberOfItems());
+        REQUIRE(face_value.numberOfItems() == node_value.numberOfItems());
+        REQUIRE(cell_value.numberOfItems() + 1 == node_value.numberOfItems());
+      }
+    }
   }
 
   SECTION("2D")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_2d = named_mesh.mesh();
 
-    REQUIRE_NOTHROW(NodeValue<int>{connectivity});
-    REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
-    REQUIRE_NOTHROW(FaceValue<int>{connectivity});
-    REQUIRE_NOTHROW(CellValue<int>{connectivity});
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-    REQUIRE(NodeValue<int>{connectivity}.isBuilt());
-    REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
-    REQUIRE(FaceValue<int>{connectivity}.isBuilt());
-    REQUIRE(CellValue<int>{connectivity}.isBuilt());
+        REQUIRE_NOTHROW(NodeValue<int>{connectivity});
+        REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
+        REQUIRE_NOTHROW(FaceValue<int>{connectivity});
+        REQUIRE_NOTHROW(CellValue<int>{connectivity});
 
-    EdgeValue<int> edge_value{connectivity};
-    FaceValue<int> face_value{connectivity};
+        REQUIRE(NodeValue<int>{connectivity}.isBuilt());
+        REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
+        REQUIRE(FaceValue<int>{connectivity}.isBuilt());
+        REQUIRE(CellValue<int>{connectivity}.isBuilt());
 
-    REQUIRE(edge_value.numberOfItems() == face_value.numberOfItems());
+        EdgeValue<int> edge_value{connectivity};
+        FaceValue<int> face_value{connectivity};
+
+        REQUIRE(edge_value.numberOfItems() == face_value.numberOfItems());
+      }
+    }
   }
 
   SECTION("3D")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-    REQUIRE_NOTHROW(NodeValue<int>{connectivity});
-    REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
-    REQUIRE_NOTHROW(FaceValue<int>{connectivity});
-    REQUIRE_NOTHROW(CellValue<int>{connectivity});
-
-    REQUIRE(NodeValue<int>{connectivity}.isBuilt());
-    REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
-    REQUIRE(FaceValue<int>{connectivity}.isBuilt());
-    REQUIRE(CellValue<int>{connectivity}.isBuilt());
-  }
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-  SECTION("set values from array")
-  {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
 
-    CellValue<size_t> cell_value{connectivity};
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-    Array<size_t> values{cell_value.numberOfItems()};
-    for (size_t i = 0; i < values.size(); ++i) {
-      values[i] = i;
-    }
+        REQUIRE_NOTHROW(NodeValue<int>{connectivity});
+        REQUIRE_NOTHROW(EdgeValue<int>{connectivity});
+        REQUIRE_NOTHROW(FaceValue<int>{connectivity});
+        REQUIRE_NOTHROW(CellValue<int>{connectivity});
 
-    cell_value = values;
-
-    for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-      REQUIRE(cell_value[i_cell] == i_cell);
+        REQUIRE(NodeValue<int>{connectivity}.isBuilt());
+        REQUIRE(EdgeValue<int>{connectivity}.isBuilt());
+        REQUIRE(FaceValue<int>{connectivity}.isBuilt());
+        REQUIRE(CellValue<int>{connectivity}.isBuilt());
+      }
     }
   }
 
-  SECTION("copy")
+  SECTION("set values from array")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
 
-    CellValue<int> cell_value{connectivity};
-    cell_value.fill(parallel::rank());
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-    CellValue<const int> const_cell_value;
-    const_cell_value = copy(cell_value);
+        CellValue<size_t> cell_value{connectivity};
 
-    cell_value.fill(0);
+        Array<size_t> values{cell_value.numberOfItems()};
+        for (size_t i = 0; i < values.size(); ++i) {
+          values[i] = i;
+        }
 
-    for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-      REQUIRE(cell_value[i_cell] == 0);
+        cell_value = values;
+
+        {
+          bool is_same = true;
+          for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+            is_same &= (cell_value[i_cell] == i_cell);
+          }
+          REQUIRE(is_same);
+        }
+      }
     }
+  }
 
-    for (CellId i_cell = 0; i_cell < mesh_3d.numberOfCells(); ++i_cell) {
-      REQUIRE(const_cell_value[i_cell] == static_cast<std::int64_t>(parallel::rank()));
+  SECTION("copy")
+  {
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
+
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+        CellValue<int> cell_value{connectivity};
+        cell_value.fill(parallel::rank());
+
+        CellValue<const int> const_cell_value;
+        const_cell_value = copy(cell_value);
+
+        cell_value.fill(0);
+
+        {
+          bool is_same = true;
+          for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+            is_same &= (cell_value[i_cell] == 0);
+          }
+          REQUIRE(is_same);
+        }
+
+        {
+          bool is_same = true;
+          for (CellId i_cell = 0; i_cell < mesh_3d->numberOfCells(); ++i_cell) {
+            is_same &= (const_cell_value[i_cell] == static_cast<std::int64_t>(parallel::rank()));
+          }
+          REQUIRE(is_same);
+        }
+      }
     }
   }
 
   SECTION("WeakItemValue")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_2d = named_mesh.mesh();
+
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+        WeakFaceValue<int> weak_face_value{connectivity};
+        for (FaceId face_id = 0; face_id < mesh_2d->numberOfFaces(); ++face_id) {
+          weak_face_value[face_id] = 2 * face_id + 1;
+        }
 
-    WeakFaceValue<int> weak_face_value{connectivity};
+        FaceValue<const int> face_value{weak_face_value};
 
-    weak_face_value.fill(parallel::rank());
+        REQUIRE(face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
 
-    FaceValue<const int> face_value{weak_face_value};
+        FaceValue<int> copied_face_value = copy(weak_face_value);
+        REQUIRE(copied_face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
 
-    REQUIRE(face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
+        weak_face_value.fill(0);
+        for (FaceId face_id = 0; face_id < mesh_2d->numberOfFaces(); ++face_id) {
+          REQUIRE(face_value[face_id] == static_cast<int>(0));
+        }
+
+        {
+          bool is_same = true;
+          for (FaceId face_id = 0; face_id < mesh_2d->numberOfFaces(); ++face_id) {
+            is_same &= (copied_face_value[face_id] == static_cast<int>(2 * face_id + 1));
+          }
+          REQUIRE(is_same);
+        }
+      }
+    }
   }
 
 #ifndef NDEBUG
@@ -161,48 +238,80 @@ TEST_CASE("ItemValue", "[mesh]")
 
     SECTION("checking for bounds violation")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-      CellValue<int> cell_value{connectivity};
-      CellId invalid_cell_id = connectivity.numberOfCells();
-      REQUIRE_THROWS_AS(cell_value[invalid_cell_id], AssertError);
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      FaceValue<int> face_value{connectivity};
-      FaceId invalid_face_id = connectivity.numberOfFaces();
-      REQUIRE_THROWS_AS(face_value[invalid_face_id], AssertError);
+          CellValue<int> cell_value{connectivity};
+          CellId invalid_cell_id = connectivity.numberOfCells();
+          REQUIRE_THROWS_AS(cell_value[invalid_cell_id], AssertError);
 
-      EdgeValue<int> edge_value{connectivity};
-      EdgeId invalid_edge_id = connectivity.numberOfEdges();
-      REQUIRE_THROWS_AS(edge_value[invalid_edge_id], AssertError);
+          FaceValue<int> face_value{connectivity};
+          FaceId invalid_face_id = connectivity.numberOfFaces();
+          REQUIRE_THROWS_AS(face_value[invalid_face_id], AssertError);
 
-      NodeValue<int> node_value{connectivity};
-      NodeId invalid_node_id = connectivity.numberOfNodes();
-      REQUIRE_THROWS_AS(node_value[invalid_node_id], AssertError);
+          EdgeValue<int> edge_value{connectivity};
+          EdgeId invalid_edge_id = connectivity.numberOfEdges();
+          REQUIRE_THROWS_AS(edge_value[invalid_edge_id], AssertError);
+
+          NodeValue<int> node_value{connectivity};
+          NodeId invalid_node_id = connectivity.numberOfNodes();
+          REQUIRE_THROWS_AS(node_value[invalid_node_id], AssertError);
+        }
+      }
     }
 
     SECTION("set values from invalid array size")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-      CellValue<size_t> cell_value{connectivity};
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      Array<size_t> values{3 + cell_value.numberOfItems()};
-      REQUIRE_THROWS_AS(cell_value = values, AssertError);
+          CellValue<size_t> cell_value{connectivity};
+
+          Array<size_t> values{3 + cell_value.numberOfItems()};
+          REQUIRE_THROWS_AS(cell_value = values, AssertError);
+        }
+      }
     }
 
     SECTION("invalid copy_to")
     {
-      const Mesh<Connectivity<2>>& mesh_2d   = *MeshDataBaseForTests::get().cartesianMesh2D();
-      const Connectivity<2>& connectivity_2d = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
+
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
+
+          const Connectivity<2>& connectivity_2d = mesh_2d->connectivity();
+
+          std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
+
+          for (auto named_mesh : mesh_list) {
+            SECTION(named_mesh.name())
+            {
+              auto mesh_3d = named_mesh.mesh();
 
-      const Mesh<Connectivity<3>>& mesh_3d   = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity_3d = mesh_3d.connectivity();
+              const Connectivity<3>& connectivity_3d = mesh_3d->connectivity();
 
-      CellValue<int> cell_2d_value{connectivity_2d};
-      CellValue<int> cell_3d_value{connectivity_3d};
-      REQUIRE_THROWS_AS(copy_to(cell_2d_value, cell_3d_value), AssertError);
+              CellValue<int> cell_2d_value{connectivity_2d};
+              CellValue<int> cell_3d_value{connectivity_3d};
+              REQUIRE_THROWS_AS(copy_to(cell_2d_value, cell_3d_value), AssertError);
+            }
+          }
+        }
+      }
     }
   }
 #endif   // NDEBUG
diff --git a/tests/test_ItemValueUtils.cpp b/tests/test_ItemValueUtils.cpp
index b77dfecd58fe2aa2ee2addcc483f631f3e3ec62f..bee143e751de72affa1134998d8561a997bc7f12 100644
--- a/tests/test_ItemValueUtils.cpp
+++ b/tests/test_ItemValueUtils.cpp
@@ -17,38 +17,46 @@ TEST_CASE("ItemValueUtils", "[mesh]")
 {
   SECTION("Synchronize")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-    WeakFaceValue<int> weak_face_value{connectivity};
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_2d = named_mesh.mesh();
 
-    weak_face_value.fill(parallel::rank());
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-    FaceValue<const int> face_value{weak_face_value};
+        WeakFaceValue<int> weak_face_value{connectivity};
 
-    REQUIRE(face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
+        weak_face_value.fill(parallel::rank());
 
-    {   // before synchronization
-      auto face_owner    = connectivity.faceOwner();
-      auto face_is_owned = connectivity.faceIsOwned();
+        FaceValue<const int> face_value{weak_face_value};
 
-      for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-        if (face_is_owned[i_face]) {
-          REQUIRE(face_owner[i_face] == face_value[i_face]);
-        } else {
-          REQUIRE(face_owner[i_face] != face_value[i_face]);
+        REQUIRE(face_value.connectivity_ptr() == weak_face_value.connectivity_ptr());
+
+        {   // before synchronization
+          auto face_owner    = connectivity.faceOwner();
+          auto face_is_owned = connectivity.faceIsOwned();
+
+          for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+            if (face_is_owned[i_face]) {
+              REQUIRE(face_owner[i_face] == face_value[i_face]);
+            } else {
+              REQUIRE(face_owner[i_face] != face_value[i_face]);
+            }
+          }
         }
-      }
-    }
 
-    synchronize(weak_face_value);
+        synchronize(weak_face_value);
 
-    {   // after synchronization
-      auto face_owner    = connectivity.faceOwner();
-      auto face_is_owned = connectivity.faceIsOwned();
+        {   // after synchronization
+          auto face_owner    = connectivity.faceOwner();
+          auto face_is_owned = connectivity.faceIsOwned();
 
-      for (FaceId i_face = 0; i_face < mesh_2d.numberOfFaces(); ++i_face) {
-        REQUIRE(face_owner[i_face] == face_value[i_face]);
+          for (FaceId i_face = 0; i_face < mesh_2d->numberOfFaces(); ++i_face) {
+            REQUIRE(face_owner[i_face] == face_value[i_face]);
+          }
+        }
       }
     }
   }
@@ -57,59 +65,83 @@ TEST_CASE("ItemValueUtils", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      CellValue<int> cell_value{connectivity};
-      cell_value.fill(-1);
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_1d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = 10 + parallel::rank();
-          }
-        });
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+          CellValue<int> cell_value{connectivity};
+          cell_value.fill(-1);
+
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_1d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = 10 + parallel::rank();
+              }
+            });
 
-      REQUIRE(min(cell_value) == 10);
+          REQUIRE(min(cell_value) == 10);
+        }
+      }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      CellValue<int> cell_value{connectivity};
-      cell_value.fill(-1);
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_2d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = 10 + parallel::rank();
-          }
-        });
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-      REQUIRE(min(cell_value) == 10);
+          CellValue<int> cell_value{connectivity};
+          cell_value.fill(-1);
+
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_2d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = 10 + parallel::rank();
+              }
+            });
+
+          REQUIRE(min(cell_value) == 10);
+        }
+      }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      CellValue<int> cell_value{connectivity};
-      cell_value.fill(-1);
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_3d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = 10 + parallel::rank();
-          }
-        });
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+          CellValue<int> cell_value{connectivity};
+          cell_value.fill(-1);
+
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_3d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = 10 + parallel::rank();
+              }
+            });
 
-      REQUIRE(min(cell_value) == 10);
+          REQUIRE(min(cell_value) == 10);
+        }
+      }
     }
   }
 
@@ -117,59 +149,83 @@ TEST_CASE("ItemValueUtils", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      CellValue<size_t> cell_value{connectivity};
-      cell_value.fill(std::numeric_limits<size_t>::max());
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_1d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = parallel::rank() + 1;
-          }
-        });
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
-      REQUIRE(max(cell_value) == parallel::size());
+          CellValue<size_t> cell_value{connectivity};
+          cell_value.fill(std::numeric_limits<size_t>::max());
+
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_1d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = parallel::rank() + 1;
+              }
+            });
+
+          REQUIRE(max(cell_value) == parallel::size());
+        }
+      }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      CellValue<size_t> cell_value{connectivity};
-      cell_value.fill(std::numeric_limits<size_t>::max());
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_2d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = parallel::rank() + 1;
-          }
-        });
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+          CellValue<size_t> cell_value{connectivity};
+          cell_value.fill(std::numeric_limits<size_t>::max());
+
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_2d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = parallel::rank() + 1;
+              }
+            });
 
-      REQUIRE(max(cell_value) == parallel::size());
+          REQUIRE(max(cell_value) == parallel::size());
+        }
+      }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      CellValue<size_t> cell_value{connectivity};
-      cell_value.fill(std::numeric_limits<size_t>::max());
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
-      parallel_for(
-        mesh_3d.numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
-          if (cell_is_owned[cell_id]) {
-            cell_value[cell_id] = parallel::rank() + 1;
-          }
-        });
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      REQUIRE(max(cell_value) == parallel::size());
+          CellValue<size_t> cell_value{connectivity};
+          cell_value.fill(std::numeric_limits<size_t>::max());
+
+          auto cell_is_owned = connectivity.cellIsOwned();
+          parallel_for(
+            mesh_3d->numberOfCells(), PUGS_LAMBDA(CellId cell_id) {
+              if (cell_is_owned[cell_id]) {
+                cell_value[cell_id] = parallel::rank() + 1;
+              }
+            });
+
+          REQUIRE(max(cell_value) == parallel::size());
+        }
+      }
     }
   }
 
@@ -177,65 +233,89 @@ TEST_CASE("ItemValueUtils", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      CellValue<size_t> cell_value{connectivity};
-      cell_value.fill(5);
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
 
-      auto cell_is_owned = connectivity.cellIsOwned();
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
-      const size_t global_number_of_cells = [&] {
-        size_t number_of_cells = 0;
-        for (CellId cell_id = 0; cell_id < cell_is_owned.numberOfItems(); ++cell_id) {
-          number_of_cells += cell_is_owned[cell_id];
-        }
-        return parallel::allReduceSum(number_of_cells);
-      }();
+          CellValue<size_t> cell_value{connectivity};
+          cell_value.fill(5);
+
+          auto cell_is_owned = connectivity.cellIsOwned();
 
-      REQUIRE(sum(cell_value) == 5 * global_number_of_cells);
+          const size_t global_number_of_cells = [&] {
+            size_t number_of_cells = 0;
+            for (CellId cell_id = 0; cell_id < cell_is_owned.numberOfItems(); ++cell_id) {
+              number_of_cells += cell_is_owned[cell_id];
+            }
+            return parallel::allReduceSum(number_of_cells);
+          }();
+
+          REQUIRE(sum(cell_value) == 5 * global_number_of_cells);
+        }
+      }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      FaceValue<size_t> face_value{connectivity};
-      face_value.fill(2);
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
 
-      auto face_is_owned = connectivity.faceIsOwned();
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-      const size_t global_number_of_faces = [&] {
-        size_t number_of_faces = 0;
-        for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) {
-          number_of_faces += face_is_owned[face_id];
-        }
-        return parallel::allReduceSum(number_of_faces);
-      }();
+          FaceValue<size_t> face_value{connectivity};
+          face_value.fill(2);
 
-      REQUIRE(sum(face_value) == 2 * global_number_of_faces);
+          auto face_is_owned = connectivity.faceIsOwned();
+
+          const size_t global_number_of_faces = [&] {
+            size_t number_of_faces = 0;
+            for (FaceId face_id = 0; face_id < face_is_owned.numberOfItems(); ++face_id) {
+              number_of_faces += face_is_owned[face_id];
+            }
+            return parallel::allReduceSum(number_of_faces);
+          }();
+
+          REQUIRE(sum(face_value) == 2 * global_number_of_faces);
+        }
+      }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      NodeValue<size_t> node_value{connectivity};
-      node_value.fill(3);
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-      auto node_is_owned = connectivity.nodeIsOwned();
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      const size_t global_number_of_nodes = [&] {
-        size_t number_of_nodes = 0;
-        for (NodeId node_id = 0; node_id < node_is_owned.numberOfItems(); ++node_id) {
-          number_of_nodes += node_is_owned[node_id];
-        }
-        return parallel::allReduceSum(number_of_nodes);
-      }();
+          NodeValue<size_t> node_value{connectivity};
+          node_value.fill(3);
+
+          auto node_is_owned = connectivity.nodeIsOwned();
 
-      REQUIRE(sum(node_value) == 3 * global_number_of_nodes);
+          const size_t global_number_of_nodes = [&] {
+            size_t number_of_nodes = 0;
+            for (NodeId node_id = 0; node_id < node_is_owned.numberOfItems(); ++node_id) {
+              number_of_nodes += node_is_owned[node_id];
+            }
+            return parallel::allReduceSum(number_of_nodes);
+          }();
+
+          REQUIRE(sum(node_value) == 3 * global_number_of_nodes);
+        }
+      }
     }
   }
 }
diff --git a/tests/test_LineTransformation.cpp b/tests/test_LineTransformation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..82559ac50cb17e77ff6916c5d066685649c69986
--- /dev/null
+++ b/tests/test_LineTransformation.cpp
@@ -0,0 +1,120 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/LineTransformation.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("LineTransformation", "[geometry]")
+{
+  SECTION("1D")
+  {
+    using R1 = TinyVector<1>;
+
+    const R1 a{0.3};
+    const R1 b{2.7};
+
+    const LineTransformation<1> t(a, b);
+
+    REQUIRE(t(R1{-1})[0] == Catch::Approx(0.3));
+    REQUIRE(t(R1{0})[0] == Catch::Approx(1.5));
+    REQUIRE(t(R1{1})[0] == Catch::Approx(2.7));
+
+    REQUIRE(t.jacobianDeterminant() == Catch::Approx(1.2));
+
+    auto p = [](const R1& X) {
+      const double x = X[0];
+      return 2 * x * x + 3 * x + 2;
+    };
+
+    QuadratureFormula<1> qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+
+    double sum = 0;
+    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+      sum += qf.weight(i) * t.jacobianDeterminant() * p(t(qf.point(i)));
+    }
+
+    auto P = [](const R1& X) {
+      const double x = X[0];
+      return 2. / 3 * x * x * x + 3. / 2 * x * x + 2 * x;
+    };
+
+    REQUIRE(sum == Catch::Approx(P(b) - P(a)));
+  }
+
+  SECTION("2D")
+  {
+    using R1 = TinyVector<1>;
+    using R2 = TinyVector<2>;
+
+    const R2 a{0.3, 1.2};
+    const R2 b{2.7, 0.7};
+
+    const LineTransformation<2> t(a, b);
+
+    REQUIRE(t(R1{-1})[0] == Catch::Approx(0.3));
+    REQUIRE(t(R1{-1})[1] == Catch::Approx(1.2));
+    REQUIRE(t(R1{0})[0] == Catch::Approx(1.5));
+    REQUIRE(t(R1{0})[1] == Catch::Approx(0.95));
+    REQUIRE(t(R1{1})[0] == Catch::Approx(2.7));
+    REQUIRE(t(R1{1})[1] == Catch::Approx(0.7));
+
+    REQUIRE(t.velocityNorm() == Catch::Approx(l2Norm(0.5 * (b - a))));
+
+    auto p = [](const R2& X) {
+      const double x = X[0];
+      const double y = X[1];
+      return 2 * x * x + 3 * x - 3 * y * y + y + 2;
+    };
+
+    QuadratureFormula<1> qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+
+    double sum = 0;
+    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+      sum += qf.weight(i) * t.velocityNorm() * p(t(qf.point(i)));
+    }
+
+    REQUIRE(sum == Catch::Approx(24.8585155630822));
+  }
+
+  SECTION("3D")
+  {
+    using R1 = TinyVector<1>;
+    using R3 = TinyVector<3>;
+
+    const R3 a{0.3, 1.2, -1};
+    const R3 b{2.7, 0.7, 0.3};
+
+    const LineTransformation<3> t(a, b);
+
+    REQUIRE(t(R1{-1})[0] == Catch::Approx(0.3));
+    REQUIRE(t(R1{-1})[1] == Catch::Approx(1.2));
+    REQUIRE(t(R1{-1})[2] == Catch::Approx(-1.));
+    REQUIRE(t(R1{0})[0] == Catch::Approx(1.5));
+    REQUIRE(t(R1{0})[1] == Catch::Approx(0.95));
+    REQUIRE(t(R1{0})[2] == Catch::Approx(-0.35));
+    REQUIRE(t(R1{1})[0] == Catch::Approx(2.7));
+    REQUIRE(t(R1{1})[1] == Catch::Approx(0.7));
+    REQUIRE(t(R1{1})[2] == Catch::Approx(0.3));
+
+    REQUIRE(t.velocityNorm() == Catch::Approx(l2Norm(0.5 * (b - a))));
+
+    auto p = [](const R3& X) {
+      const double x = X[0];
+      const double y = X[1];
+      const double z = X[2];
+      return 2 * x * x + 3 * x - 3 * y * y + y + 2 * z * z - 0.5 * z + 2;
+    };
+
+    QuadratureFormula<1> qf = QuadratureManager::instance().getLineFormula(GaussQuadratureDescriptor(2));
+
+    double sum = 0;
+    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+      sum += qf.weight(i) * t.velocityNorm() * p(t(qf.point(i)));
+    }
+
+    REQUIRE(sum == Catch::Approx(30.08440406681767));
+  }
+}
diff --git a/tests/test_MathModule.cpp b/tests/test_MathModule.cpp
index 7c2a9bc5438367b7f3cf70d14d3b8acc58a29ae7..99801b5f1d6602cbc40b0308e20ea1c1d9009b24 100644
--- a/tests/test_MathModule.cpp
+++ b/tests/test_MathModule.cpp
@@ -4,8 +4,6 @@
 #include <language/modules/MathModule.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
 
-#include <set>
-
 // clazy:excludeall=non-pod-global-static
 
 TEST_CASE("MathModule", "[language]")
@@ -15,7 +13,7 @@ TEST_CASE("MathModule", "[language]")
   MathModule math_module;
   const auto& name_builtin_function = math_module.getNameBuiltinFunctionMap();
 
-  REQUIRE(name_builtin_function.size() == 25);
+  REQUIRE(name_builtin_function.size() == 29);
 
   SECTION("double -> double")
   {
@@ -323,14 +321,71 @@ TEST_CASE("MathModule", "[language]")
       auto result = std::pow(arg0, arg1);
       REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result));
     }
+
+    SECTION("min")
+    {
+      auto i_function = name_builtin_function.find("min:R*R");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg0_variant, arg1_variant});
+
+      auto result = std::min(arg0, arg1);
+      REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result));
+    }
+
+    SECTION("max")
+    {
+      auto i_function = name_builtin_function.find("max:R*R");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg0_variant, arg1_variant});
+
+      auto result = std::max(arg0, arg1);
+      REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result));
+    }
+  }
+
+  SECTION("(uint64_t, uint64_t) -> uint64_t")
+  {
+    int64_t arg0 = 3;
+    int64_t arg1 = -2;
+
+    DataVariant arg0_variant = arg0;
+    DataVariant arg1_variant = arg1;
+
+    SECTION("min")
+    {
+      auto i_function = name_builtin_function.find("min:Z*Z");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg0_variant, arg1_variant});
+
+      auto result = std::min(arg0, arg1);
+      REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result));
+    }
+
+    SECTION("max")
+    {
+      auto i_function = name_builtin_function.find("max:Z*Z");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg0_variant, arg1_variant});
+
+      auto result = std::max(arg0, arg1);
+      REQUIRE(std::get<decltype(result)>(result_variant) == Catch::Approx(result));
+    }
   }
 
   SECTION("(R^d, R^d) -> double")
   {
     SECTION("dot:R^1*R^1")
     {
-      TinyVector<1> arg0 = 3;
-      TinyVector<1> arg1 = 2;
+      TinyVector<1> arg0{3};
+      TinyVector<1> arg1{2};
 
       DataVariant arg0_variant = arg0;
       DataVariant arg1_variant = arg1;
diff --git a/tests/test_MedianDualConnectivityBuilder.cpp b/tests/test_MedianDualConnectivityBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a95e6ad22f8a9b2605dec5c28e7c7704d40aed6f
--- /dev/null
+++ b/tests/test_MedianDualConnectivityBuilder.cpp
@@ -0,0 +1,142 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+
+template <ItemType item_type, typename ConnectivityType>
+inline auto
+get_item_ref_ids(const ConnectivityType& connectivity)
+{
+  std::map<std::string, size_t> ref_id_set;
+  for (size_t i = 0; i < connectivity.template numberOfRefItemList<item_type>(); ++i) {
+    const auto& ref_id_list = connectivity.template refItemList<item_type>(i);
+    std::ostringstream os;
+    os << ref_id_list.refId();
+    ItemValue<size_t, item_type> item_tag{connectivity};
+    item_tag.fill(0);
+    for (size_t i_item = 0; i_item < ref_id_list.list().size(); ++i_item) {
+      item_tag[ref_id_list.list()[i_item]] = 1;
+    }
+
+    ref_id_set[os.str()] = sum(item_tag);
+  }
+  return ref_id_set;
+}
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("MedianDualConnectivityBuilder", "[mesh]")
+{
+  SECTION("2D")
+  {
+    constexpr static size_t Dimension = 2;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh();
+    const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+    REQUIRE(primal_connectivity.numberOfNodes() == 53);
+    REQUIRE(primal_connectivity.numberOfFaces() == 110);
+    REQUIRE(primal_connectivity.numberOfCells() == 58);
+
+    std::shared_ptr p_median_dual_connectivity =
+      DualConnectivityManager::instance().getMedianDualConnectivity(primal_connectivity);
+    const ConnectivityType& dual_connectivity = *p_median_dual_connectivity;
+
+    REQUIRE(dual_connectivity.numberOfNodes() == 192);
+    REQUIRE(dual_connectivity.numberOfFaces() == 244);
+    REQUIRE(dual_connectivity.numberOfCells() == 53);
+
+    SECTION("ref node list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::node>() == 4);
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::node>() == 4);
+
+      const auto primal_ref_node_list = get_item_ref_ids<ItemType::node>(primal_connectivity);
+      REQUIRE(primal_ref_node_list.size() == 4);
+
+      REQUIRE(primal_ref_node_list.count("XMAXYMAX(11)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMAXYMIN(10)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMINYMAX(9)") == 1);
+      REQUIRE(primal_ref_node_list.count("XMINYMIN(8)") == 1);
+
+      REQUIRE(primal_ref_node_list.at("XMAXYMAX(11)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMAXYMIN(10)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMINYMAX(9)") == 1);
+      REQUIRE(primal_ref_node_list.at("XMINYMIN(8)") == 1);
+
+      const auto dual_ref_node_list = get_item_ref_ids<ItemType::node>(dual_connectivity);
+      REQUIRE(dual_ref_node_list.size() == 4);
+
+      REQUIRE(dual_ref_node_list.count("XMAXYMAX(11)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMAXYMIN(10)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMINYMAX(9)") == 1);
+      REQUIRE(dual_ref_node_list.count("XMINYMIN(8)") == 1);
+
+      REQUIRE(dual_ref_node_list.at("XMAXYMAX(11)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMAXYMIN(10)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMINYMAX(9)") == 1);
+      REQUIRE(dual_ref_node_list.at("XMINYMIN(8)") == 1);
+    }
+
+    SECTION("ref face list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::face>() == 5);
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::face>() == 4);
+
+      const auto primal_ref_face_list = get_item_ref_ids<ItemType::face>(primal_connectivity);
+      REQUIRE(primal_ref_face_list.size() == 5);
+
+      REQUIRE(primal_ref_face_list.count("INTERFACE(5)") == 1);
+      REQUIRE(primal_ref_face_list.count("XMAX(2)") == 1);
+      REQUIRE(primal_ref_face_list.count("XMIN(1)") == 1);
+      REQUIRE(primal_ref_face_list.count("YMAX(3)") == 1);
+      REQUIRE(primal_ref_face_list.count("YMIN(4)") == 1);
+
+      REQUIRE(primal_ref_face_list.at("INTERFACE(5)") == 4);
+      REQUIRE(primal_ref_face_list.at("XMAX(2)") == 4);
+      REQUIRE(primal_ref_face_list.at("XMIN(1)") == 4);
+      REQUIRE(primal_ref_face_list.at("YMAX(3)") == 8);
+      REQUIRE(primal_ref_face_list.at("YMIN(4)") == 8);
+
+      const auto dual_ref_face_list = get_item_ref_ids<ItemType::face>(dual_connectivity);
+      REQUIRE(dual_ref_face_list.size() == 4);
+
+      REQUIRE(dual_ref_face_list.count("XMAX(2)") == 1);
+      REQUIRE(dual_ref_face_list.count("XMIN(1)") == 1);
+      REQUIRE(dual_ref_face_list.count("YMAX(3)") == 1);
+      REQUIRE(dual_ref_face_list.count("YMIN(4)") == 1);
+
+      REQUIRE(dual_ref_face_list.at("XMAX(2)") == 8);
+      REQUIRE(dual_ref_face_list.at("XMIN(1)") == 8);
+      REQUIRE(dual_ref_face_list.at("YMAX(3)") == 16);
+      REQUIRE(dual_ref_face_list.at("YMIN(4)") == 16);
+    }
+
+    SECTION("ref cell list")
+    {
+      REQUIRE(primal_connectivity.numberOfRefItemList<ItemType::cell>() == 2);
+      // Median dual cell references are not computed
+      REQUIRE(dual_connectivity.numberOfRefItemList<ItemType::cell>() == 0);
+
+      const auto primal_ref_cell_list = get_item_ref_ids<ItemType::cell>(primal_connectivity);
+      REQUIRE(primal_ref_cell_list.size() == 2);
+
+      REQUIRE(primal_ref_cell_list.count("LEFT(6)") == 1);
+      REQUIRE(primal_ref_cell_list.count("RIGHT(7)") == 1);
+
+      REQUIRE(primal_ref_cell_list.at("LEFT(6)") == 22);
+      REQUIRE(primal_ref_cell_list.at("RIGHT(7)") == 36);
+
+      const auto dual_ref_cell_list = get_item_ref_ids<ItemType::cell>(dual_connectivity);
+      REQUIRE(dual_ref_cell_list.size() == 0);
+    }
+  }
+}
diff --git a/tests/test_MedianDualMeshBuilder.cpp b/tests/test_MedianDualMeshBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..062c48264f4a766d9a9af64da42417746e10636b
--- /dev/null
+++ b/tests/test_MedianDualMeshBuilder.cpp
@@ -0,0 +1,91 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualMeshManager.hpp>
+#include <mesh/MeshData.hpp>
+#include <mesh/MeshDataManager.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/ItemValueUtils.hpp>
+#include <mesh/Mesh.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("MedianDualMeshBuilder", "[mesh]")
+{
+  SECTION("2D")
+  {
+    constexpr static size_t Dimension = 2;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr p_mesh      = MeshDataBaseForTests::get().hybrid2DMesh();
+    const MeshType& primal_mesh = *p_mesh;
+
+    REQUIRE(primal_mesh.numberOfNodes() == 53);
+    REQUIRE(primal_mesh.numberOfFaces() == 110);
+    REQUIRE(primal_mesh.numberOfCells() == 58);
+
+    std::shared_ptr p_diamond_dual_mesh = DualMeshManager::instance().getMedianDualMesh(primal_mesh);
+    const MeshType& dual_mesh           = *p_diamond_dual_mesh;
+
+    REQUIRE(dual_mesh.numberOfNodes() == 192);
+    REQUIRE(dual_mesh.numberOfFaces() == 244);
+    REQUIRE(dual_mesh.numberOfCells() == 53);
+
+    auto dual_cell_volume = MeshDataManager::instance().getMeshData(dual_mesh).Vj();
+
+    REQUIRE(sum(dual_cell_volume) == Catch::Approx(2));
+
+    auto coord_comp = [](const TinyVector<Dimension>& a, const TinyVector<Dimension>& b) -> bool {
+      return (a[0] < b[0]) or ((a[0] == b[0]) and (a[1] < b[1]));
+    };
+
+    auto dual_cell_to_node    = dual_mesh.connectivity().cellToNodeMatrix();
+    auto primal_node_to_face  = primal_mesh.connectivity().nodeToFaceMatrix();
+    auto primal_node_to_cell  = primal_mesh.connectivity().nodeToCellMatrix();
+    auto primal_face_is_owned = primal_mesh.connectivity().faceIsOwned();
+
+    auto dual_xr   = dual_mesh.xr();
+    auto primal_xr = primal_mesh.xr();
+    auto primal_xl = MeshDataManager::instance().getMeshData(primal_mesh).xl();
+    auto primal_xj = MeshDataManager::instance().getMeshData(primal_mesh).xj();
+
+    using CoordSet = std::set<TinyVector<Dimension>,
+                              std::function<bool(const TinyVector<Dimension>& a, const TinyVector<Dimension>& b)>>;
+
+    auto primal_face_to_cell_matrix = primal_mesh.connectivity().faceToCellMatrix();
+
+    CellId dual_cell_id   = 0;
+    NodeId primal_node_id = 0;
+    for (; dual_cell_id < dual_mesh.numberOfCells(); ++dual_cell_id, ++primal_node_id) {
+      CoordSet dual_cell_node_coords{coord_comp};
+      {
+        auto dual_cell_node_list = dual_cell_to_node[dual_cell_id];
+        for (size_t i_node = 0; i_node < dual_cell_node_list.size(); ++i_node) {
+          dual_cell_node_coords.insert(dual_xr[dual_cell_node_list[i_node]]);
+        }
+      }
+
+      CoordSet primal_coords{coord_comp};
+      {
+        auto primal_node_face_list = primal_node_to_face[primal_node_id];
+        for (size_t i_face = 0; i_face < primal_node_face_list.size(); ++i_face) {
+          const FaceId face_id = primal_node_face_list[i_face];
+          primal_coords.insert(primal_xl[face_id]);
+          if (primal_face_is_owned[face_id] and (primal_face_to_cell_matrix[face_id].size() == 1)) {
+            primal_coords.insert(primal_xr[primal_node_id]);
+          }
+        }
+        auto primal_node_cell_list = primal_node_to_cell[primal_node_id];
+        for (size_t i_cell = 0; i_cell < primal_node_cell_list.size(); ++i_cell) {
+          primal_coords.insert(primal_xj[primal_node_cell_list[i_cell]]);
+        }
+      }
+
+      REQUIRE(primal_coords == dual_cell_node_coords);
+    }
+  }
+}
diff --git a/tests/test_OFStream.cpp b/tests/test_OFStream.cpp
index 32b4ff21b8e725dcaff822b586f9810d617d3a4d..edcee297572862e12d62a8a5032757ea84b364e2 100644
--- a/tests/test_OFStream.cpp
+++ b/tests/test_OFStream.cpp
@@ -12,7 +12,7 @@ TEST_CASE("OFStream", "[language]")
 {
   SECTION("ofstream")
   {
-    const std::string basename = "ofstream_";
+    const std::string basename = std::filesystem::path{PUGS_BINARY_DIR}.append("tests").append("ofstream_");
     const std::string filename = basename + std::to_string(parallel::rank());
 
     // Ensures that the file is closed after this line
diff --git a/tests/test_PrimalToDiamondDualConnectivityDataMapper.cpp b/tests/test_PrimalToDiamondDualConnectivityDataMapper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..39b5012700f9b8d8293829b4ccff65dad93036f6
--- /dev/null
+++ b/tests/test_PrimalToDiamondDualConnectivityDataMapper.cpp
@@ -0,0 +1,367 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/PrimalToDiamondDualConnectivityDataMapper.hpp>
+#include <mesh/PrimalToDual1DConnectivityDataMapper.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("PrimalToDiamondDualConnectivityDataMapper", "[mesh]")
+{
+  SECTION("1D")
+  {
+    constexpr static size_t Dimension = 1;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh();
+    const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+    std::shared_ptr p_diamond_dual_connectivity =
+      DualConnectivityManager::instance().getDiamondDualConnectivity(primal_connectivity);
+
+    std::shared_ptr p_dual_1d_connectivity =
+      DualConnectivityManager::instance().getDual1DConnectivity(primal_connectivity);
+
+    REQUIRE(p_diamond_dual_connectivity == p_dual_1d_connectivity);
+
+    std::shared_ptr p_primal_to_diamond_dual_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(primal_connectivity);
+
+    std::shared_ptr p_primal_to_dual_1d_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToDual1DConnectivityDataMapper(primal_connectivity);
+
+    REQUIRE(p_primal_to_diamond_dual_connectivity_mapper == p_primal_to_dual_1d_connectivity_mapper);
+  }
+
+  SECTION("2D")
+  {
+    constexpr static size_t Dimension = 2;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh();
+    const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+    std::shared_ptr p_diamond_dual_connectivity =
+      DualConnectivityManager::instance().getDiamondDualConnectivity(primal_connectivity);
+    const ConnectivityType& dual_connectivity = *p_diamond_dual_connectivity;
+
+    std::shared_ptr p_primal_to_dual_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(primal_connectivity);
+    const auto& primal_to_dual_connectivity_mapper = *p_primal_to_dual_connectivity_mapper;
+
+    SECTION("check data transfer between primal faces to dual cells")
+    {
+      const auto& primal_face_to_cell_matrix = primal_connectivity.faceToCellMatrix();
+
+      FaceValue<size_t> primal_face_value{primal_connectivity};
+      parallel_for(
+        primal_face_value.numberOfItems(), PUGS_LAMBDA(const FaceId face_id) {
+          primal_face_value[face_id] = primal_face_to_cell_matrix[face_id].size() + face_id;
+        });
+
+      CellValue<size_t> dual_from_primal_face_value{dual_connectivity};
+
+      primal_to_dual_connectivity_mapper.toDualCell(primal_face_value, dual_from_primal_face_value);
+
+      {
+        const auto& dual_cell_to_node_matrix = dual_connectivity.cellToNodeMatrix();
+        bool correct_value                   = true;
+        for (CellId cell_id = 0; cell_id < dual_connectivity.numberOfCells(); ++cell_id) {
+          correct_value &=
+            (dual_cell_to_node_matrix[cell_id].size() + cell_id == dual_from_primal_face_value[cell_id] + 2);
+        }
+        REQUIRE(correct_value);
+      }
+
+      {
+        bool is_same = true;
+        FaceValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+        primal_to_dual_connectivity_mapper.fromDualCell(dual_from_primal_face_value, primal_from_dual_cell_value);
+        for (FaceId face_id = 0; face_id < primal_connectivity.numberOfFaces(); ++face_id) {
+          is_same &= (primal_from_dual_cell_value[face_id] == primal_face_value[face_id]);
+        }
+        REQUIRE(is_same);
+      }
+
+      {
+        const auto& primal_edge_to_cell_matrix = primal_connectivity.edgeToCellMatrix();
+
+        EdgeValue<size_t> primal_edge_value{primal_connectivity};
+        parallel_for(
+          primal_edge_value.numberOfItems(), PUGS_LAMBDA(const EdgeId edge_id) {
+            primal_edge_value[edge_id] = primal_edge_to_cell_matrix[edge_id].size() + edge_id;
+          });
+
+        CellValue<size_t> dual_from_primal_edge_value{dual_connectivity};
+
+        primal_to_dual_connectivity_mapper.toDualCell(primal_edge_value, dual_from_primal_edge_value);
+
+        bool is_same = true;
+        for (CellId cell_id = 0; cell_id < primal_connectivity.numberOfCells(); ++cell_id) {
+          is_same &= (dual_from_primal_edge_value[cell_id] == dual_from_primal_face_value[cell_id]);
+        }
+        REQUIRE(is_same);
+
+        {
+          bool is_same = true;
+          EdgeValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+          primal_to_dual_connectivity_mapper.fromDualCell(dual_from_primal_edge_value, primal_from_dual_cell_value);
+          for (EdgeId edge_id = 0; edge_id < primal_connectivity.numberOfEdges(); ++edge_id) {
+            is_same &= (primal_from_dual_cell_value[edge_id] == primal_edge_value[edge_id]);
+          }
+          REQUIRE(is_same);
+        }
+      }
+
+#ifndef NDEBUG
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualCell(FaceValue<size_t>{dual_connectivity},
+                                                                        dual_from_primal_face_value),
+                          "unexpected connectivity for primal FaceValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualCell(primal_face_value,
+                                                                        CellValue<size_t>{primal_connectivity}),
+                          "unexpected connectivity for dual CellValue");
+
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualCell(dual_from_primal_face_value,
+                                                                          FaceValue<size_t>{dual_connectivity}),
+                          "unexpected connectivity for primal FaceValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualCell(CellValue<size_t>{primal_connectivity},
+                                                                          primal_face_value),
+                          "unexpected connectivity for dual CellValue");
+
+#endif   // NDEBUG
+    }
+
+    SECTION("check data transfer between primal nodes and cells to dual nodes")
+    {
+      const auto& primal_cell_to_node_matrix = primal_connectivity.cellToNodeMatrix();
+      CellValue<size_t> primal_cell_value{primal_connectivity};
+      const size_t number_of_nodes = primal_connectivity.numberOfNodes();
+      parallel_for(
+        primal_cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+          primal_cell_value[cell_id] = primal_cell_to_node_matrix[cell_id].size() + number_of_nodes + cell_id;
+        });
+
+      const auto& primal_node_to_face_matrix = primal_connectivity.nodeToFaceMatrix();
+      NodeValue<size_t> primal_node_value{primal_connectivity};
+      parallel_for(
+        primal_node_value.numberOfItems(), PUGS_LAMBDA(const NodeId node_id) {
+          primal_node_value[node_id] = primal_node_to_face_matrix[node_id].size() + node_id;
+        });
+
+      NodeValue<size_t> dual_from_primal_cell_and_node_value{dual_connectivity};
+
+      primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_cell_value,
+                                                    dual_from_primal_cell_and_node_value);
+
+      {
+        const auto& dual_node_to_cell_matrix = dual_connectivity.nodeToCellMatrix();
+        bool correct_dual_value              = true;
+        for (NodeId node_id = 0; node_id < dual_connectivity.numberOfNodes(); ++node_id) {
+          correct_dual_value &=
+            (dual_from_primal_cell_and_node_value[node_id] == dual_node_to_cell_matrix[node_id].size() + node_id);
+        }
+        REQUIRE(correct_dual_value);
+      }
+
+      {
+        bool is_same = true;
+        NodeValue<size_t> primal_from_dual_node_value{primal_connectivity};
+        CellValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+        primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_cell_and_node_value,
+                                                        primal_from_dual_node_value, primal_from_dual_cell_value);
+        for (CellId cell_id = 0; cell_id < primal_connectivity.numberOfCells(); ++cell_id) {
+          is_same &= (primal_cell_value[cell_id] == primal_from_dual_cell_value[cell_id]);
+        }
+        for (NodeId node_id = 0; node_id < primal_connectivity.numberOfNodes(); ++node_id) {
+          is_same &= (primal_node_value[node_id] == primal_from_dual_node_value[node_id]);
+        }
+
+        REQUIRE(is_same);
+      }
+
+#ifndef NDEBUG
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(NodeValue<size_t>{dual_connectivity},
+                                                                        primal_cell_value,
+                                                                        dual_from_primal_cell_and_node_value),
+                          "unexpected connectivity for primal NodeValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(primal_node_value,
+                                                                        CellValue<size_t>{dual_connectivity},
+                                                                        dual_from_primal_cell_and_node_value),
+                          "unexpected connectivity for primal CellValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_cell_value,
+                                                                        NodeValue<size_t>{primal_connectivity}),
+                          "unexpected connectivity for dual NodeValue");
+
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_cell_and_node_value,
+                                                                          NodeValue<size_t>{dual_connectivity},
+                                                                          primal_cell_value),
+                          "unexpected connectivity for primal NodeValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_cell_and_node_value,
+                                                                          primal_node_value,
+                                                                          CellValue<size_t>{dual_connectivity}),
+                          "unexpected connectivity for primal CellValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(NodeValue<size_t>{primal_connectivity},
+                                                                          primal_node_value, primal_cell_value),
+                          "unexpected connectivity for dual NodeValue");
+
+#endif   // NDEBUG
+    }
+  }
+
+  SECTION("3D")
+  {
+    constexpr static size_t Dimension = 3;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid3DMesh();
+    const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+    std::shared_ptr p_diamond_dual_connectivity =
+      DualConnectivityManager::instance().getDiamondDualConnectivity(primal_connectivity);
+    const ConnectivityType& dual_connectivity = *p_diamond_dual_connectivity;
+
+    std::shared_ptr p_primal_to_dual_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToDiamondDualConnectivityDataMapper(primal_connectivity);
+    const auto& primal_to_dual_connectivity_mapper = *p_primal_to_dual_connectivity_mapper;
+
+    SECTION("check data transfer between primal faces to dual cells")
+    {
+      const auto& primal_face_to_cell_matrix = primal_connectivity.faceToCellMatrix();
+      const auto& primal_face_to_node_matrix = primal_connectivity.faceToNodeMatrix();
+
+      FaceValue<size_t> primal_face_value{primal_connectivity};
+      parallel_for(
+        primal_face_value.numberOfItems(), PUGS_LAMBDA(const FaceId face_id) {
+          primal_face_value[face_id] =
+            primal_face_to_cell_matrix[face_id].size() + primal_face_to_node_matrix[face_id].size() + face_id;
+        });
+
+      CellValue<size_t> dual_from_primal_face_value{dual_connectivity};
+
+      primal_to_dual_connectivity_mapper.toDualCell(primal_face_value, dual_from_primal_face_value);
+
+      {
+        const auto& dual_cell_to_node_matrix = dual_connectivity.cellToNodeMatrix();
+        bool correct_value                   = true;
+        for (CellId cell_id = 0; cell_id < dual_connectivity.numberOfCells(); ++cell_id) {
+          correct_value &= (dual_cell_to_node_matrix[cell_id].size() + cell_id == dual_from_primal_face_value[cell_id]);
+        }
+        REQUIRE(correct_value);
+      }
+
+      {
+        bool is_same = true;
+        FaceValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+        primal_to_dual_connectivity_mapper.fromDualCell(dual_from_primal_face_value, primal_from_dual_cell_value);
+        for (FaceId face_id = 0; face_id < primal_connectivity.numberOfFaces(); ++face_id) {
+          is_same &= (primal_from_dual_cell_value[face_id] == primal_face_value[face_id]);
+        }
+        REQUIRE(is_same);
+      }
+
+#ifndef NDEBUG
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualCell(FaceValue<size_t>{dual_connectivity},
+                                                                        dual_from_primal_face_value),
+                          "unexpected connectivity for primal FaceValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualCell(primal_face_value,
+                                                                        CellValue<size_t>{primal_connectivity}),
+                          "unexpected connectivity for dual CellValue");
+
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualCell(dual_from_primal_face_value,
+                                                                          FaceValue<size_t>{dual_connectivity}),
+                          "unexpected connectivity for primal FaceValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualCell(CellValue<size_t>{primal_connectivity},
+                                                                          primal_face_value),
+                          "unexpected connectivity for dual CellValue");
+
+#endif   // NDEBUG
+    }
+
+    SECTION("check data transfer between primal nodes and cells to dual nodes")
+    {
+      const auto& primal_cell_to_face_matrix = primal_connectivity.cellToFaceMatrix();
+      CellValue<size_t> primal_cell_value{primal_connectivity};
+      const size_t number_of_nodes = primal_connectivity.numberOfNodes();
+      parallel_for(
+        primal_cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+          primal_cell_value[cell_id] = primal_cell_to_face_matrix[cell_id].size() + number_of_nodes + cell_id;
+        });
+
+      const auto& primal_node_to_face_matrix = primal_connectivity.nodeToFaceMatrix();
+      NodeValue<size_t> primal_node_value{primal_connectivity};
+      parallel_for(
+        primal_node_value.numberOfItems(), PUGS_LAMBDA(const NodeId node_id) {
+          primal_node_value[node_id] = primal_node_to_face_matrix[node_id].size() + node_id;
+        });
+
+      NodeValue<size_t> dual_from_primal_cell_and_node_value{dual_connectivity};
+
+      primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_cell_value,
+                                                    dual_from_primal_cell_and_node_value);
+
+      {
+        const auto& dual_node_to_cell_matrix = dual_connectivity.nodeToCellMatrix();
+        bool correct_dual_value              = true;
+        for (NodeId node_id = 0; node_id < dual_connectivity.numberOfNodes(); ++node_id) {
+          correct_dual_value &=
+            (dual_from_primal_cell_and_node_value[node_id] == dual_node_to_cell_matrix[node_id].size() + node_id);
+        }
+        REQUIRE(correct_dual_value);
+      }
+
+      {
+        bool is_same = true;
+        NodeValue<size_t> primal_from_dual_node_value{primal_connectivity};
+        CellValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+        primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_cell_and_node_value,
+                                                        primal_from_dual_node_value, primal_from_dual_cell_value);
+        for (CellId cell_id = 0; cell_id < primal_connectivity.numberOfCells(); ++cell_id) {
+          is_same &= (primal_cell_value[cell_id] == primal_from_dual_cell_value[cell_id]);
+        }
+        for (NodeId node_id = 0; node_id < primal_connectivity.numberOfNodes(); ++node_id) {
+          is_same &= (primal_node_value[node_id] == primal_from_dual_node_value[node_id]);
+        }
+
+        REQUIRE(is_same);
+      }
+
+#ifndef NDEBUG
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(NodeValue<size_t>{dual_connectivity},
+                                                                        primal_cell_value,
+                                                                        dual_from_primal_cell_and_node_value),
+                          "unexpected connectivity for primal NodeValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(primal_node_value,
+                                                                        CellValue<size_t>{dual_connectivity},
+                                                                        dual_from_primal_cell_and_node_value),
+                          "unexpected connectivity for primal CellValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_cell_value,
+                                                                        NodeValue<size_t>{primal_connectivity}),
+                          "unexpected connectivity for dual NodeValue");
+
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_cell_and_node_value,
+                                                                          NodeValue<size_t>{dual_connectivity},
+                                                                          primal_cell_value),
+                          "unexpected connectivity for primal NodeValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_cell_and_node_value,
+                                                                          primal_node_value,
+                                                                          CellValue<size_t>{dual_connectivity}),
+                          "unexpected connectivity for primal CellValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(NodeValue<size_t>{primal_connectivity},
+                                                                          primal_node_value, primal_cell_value),
+                          "unexpected connectivity for dual NodeValue");
+
+#endif   // NDEBUG
+    }
+  }
+}
diff --git a/tests/test_PrimalToDual1DConnectivityDataMapper.cpp b/tests/test_PrimalToDual1DConnectivityDataMapper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6bf365ec4371e26163c7003261fa125fb851ff3f
--- /dev/null
+++ b/tests/test_PrimalToDual1DConnectivityDataMapper.cpp
@@ -0,0 +1,224 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/PrimalToDual1DConnectivityDataMapper.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("PrimalToDual1DConnectivityDataMapper", "[mesh]")
+{
+  constexpr static size_t Dimension = 1;
+
+  using ConnectivityType = Connectivity<Dimension>;
+  using MeshType         = Mesh<ConnectivityType>;
+
+  std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh();
+  const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+  std::shared_ptr p_dual_1d_connectivity =
+    DualConnectivityManager::instance().getDual1DConnectivity(primal_connectivity);
+  const ConnectivityType& dual_connectivity = *p_dual_1d_connectivity;
+
+  std::shared_ptr p_primal_to_dual_connectivity_mapper =
+    DualConnectivityManager::instance().getPrimalToDual1DConnectivityDataMapper(primal_connectivity);
+  const auto& primal_to_dual_connectivity_mapper = *p_primal_to_dual_connectivity_mapper;
+
+  SECTION("check data transfer between primal nodes/edges/faces to dual cells")
+  {
+    NodeValue<size_t> primal_node_value{primal_connectivity};
+    parallel_for(
+      primal_node_value.numberOfItems(), PUGS_LAMBDA(const NodeId node_id) { primal_node_value[node_id] = node_id; });
+
+    CellValue<size_t> dual_from_primal_node_value{dual_connectivity};
+
+    primal_to_dual_connectivity_mapper.toDualCell(primal_node_value, dual_from_primal_node_value);
+
+    {
+      bool correct_value = true;
+      for (CellId cell_id = 0; cell_id < dual_connectivity.numberOfCells(); ++cell_id) {
+        correct_value &= (cell_id == dual_from_primal_node_value[cell_id]);
+      }
+      REQUIRE(correct_value);
+    }
+
+    {
+      bool is_same = true;
+      NodeValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+      primal_to_dual_connectivity_mapper.fromDualCell(dual_from_primal_node_value, primal_from_dual_cell_value);
+      for (NodeId node_id = 0; node_id < primal_connectivity.numberOfFaces(); ++node_id) {
+        is_same &= (primal_from_dual_cell_value[node_id] == primal_node_value[node_id]);
+      }
+      REQUIRE(is_same);
+    }
+
+    {
+      EdgeValue<size_t> primal_edge_value{primal_connectivity};
+      parallel_for(
+        primal_edge_value.numberOfItems(), PUGS_LAMBDA(const EdgeId edge_id) { primal_edge_value[edge_id] = edge_id; });
+
+      CellValue<size_t> dual_from_primal_edge_value{dual_connectivity};
+
+      primal_to_dual_connectivity_mapper.toDualCell(primal_edge_value, dual_from_primal_edge_value);
+
+      bool is_same = true;
+      for (CellId cell_id = 0; cell_id < primal_connectivity.numberOfFaces(); ++cell_id) {
+        is_same &= (dual_from_primal_edge_value[cell_id] == dual_from_primal_node_value[cell_id]);
+      }
+      REQUIRE(is_same);
+    }
+
+    {
+      FaceValue<size_t> primal_face_value{primal_connectivity};
+      parallel_for(
+        primal_face_value.numberOfItems(), PUGS_LAMBDA(const FaceId face_id) { primal_face_value[face_id] = face_id; });
+
+      CellValue<size_t> dual_from_primal_face_value{dual_connectivity};
+
+      primal_to_dual_connectivity_mapper.toDualCell(primal_face_value, dual_from_primal_face_value);
+
+      bool is_same = true;
+      for (CellId cell_id = 0; cell_id < primal_connectivity.numberOfFaces(); ++cell_id) {
+        is_same &= (dual_from_primal_face_value[cell_id] == dual_from_primal_node_value[cell_id]);
+      }
+      REQUIRE(is_same);
+    }
+
+#ifndef NDEBUG
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualCell(FaceValue<size_t>{dual_connectivity},
+                                                                      dual_from_primal_node_value),
+                        "unexpected connectivity for primal NodeValue");
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualCell(primal_node_value,
+                                                                      CellValue<size_t>{primal_connectivity}),
+                        "unexpected connectivity for dual CellValue");
+
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualCell(dual_from_primal_node_value,
+                                                                        FaceValue<size_t>{dual_connectivity}),
+                        "unexpected connectivity for primal NodeValue");
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualCell(CellValue<size_t>{primal_connectivity},
+                                                                        primal_node_value),
+                        "unexpected connectivity for dual CellValue");
+
+#endif   // NDEBUG
+  }
+
+  SECTION("check data transfer between primal nodes and cells to dual nodes")
+  {
+    const auto& primal_cell_to_node_matrix = primal_connectivity.cellToNodeMatrix();
+    CellValue<size_t> primal_cell_value{primal_connectivity};
+    parallel_for(
+      primal_cell_value.numberOfItems(), PUGS_LAMBDA(const CellId cell_id) {
+        // 2 is the number of boundary nodes
+        primal_cell_value[cell_id] = primal_cell_to_node_matrix[cell_id].size() + 2 + cell_id;
+      });
+
+    const auto& primal_node_to_cell_matrix = primal_connectivity.nodeToCellMatrix();
+    NodeValue<size_t> primal_node_value{primal_connectivity};
+    primal_node_value.fill(0);
+    size_t count = 0;
+    for (NodeId node_id = 0; node_id < primal_node_value.numberOfItems(); ++node_id) {
+      if (primal_node_to_cell_matrix[node_id].size() == 1) {
+        primal_node_value[node_id] = primal_node_to_cell_matrix[node_id].size() + count;
+        count++;
+      }
+    }
+    NodeValue<size_t> dual_node_from_primal_cell_and_node_value{dual_connectivity};
+
+    primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_cell_value,
+                                                  dual_node_from_primal_cell_and_node_value);
+
+    {
+      const auto& dual_node_to_cell_matrix = dual_connectivity.nodeToCellMatrix();
+      bool correct_dual_value              = true;
+      for (NodeId node_id = 0; node_id < dual_connectivity.numberOfNodes(); ++node_id) {
+        correct_dual_value &=
+          (dual_node_from_primal_cell_and_node_value[node_id] == dual_node_to_cell_matrix[node_id].size() + node_id);
+      }
+      REQUIRE(correct_dual_value);
+    }
+
+    {
+      EdgeValue<size_t> dual_edge_from_primal_cell_and_node_value{dual_connectivity};
+
+      primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_cell_value,
+                                                    dual_edge_from_primal_cell_and_node_value);
+
+      bool correct_dual_value = true;
+
+      NodeId node_id = 0;
+      EdgeId edge_id = 0;
+      for (; node_id < dual_connectivity.numberOfNodes(); ++node_id, ++edge_id) {
+        correct_dual_value &=
+          (dual_node_from_primal_cell_and_node_value[node_id] == dual_edge_from_primal_cell_and_node_value[edge_id]);
+      }
+      REQUIRE(correct_dual_value);
+    }
+
+    {
+      FaceValue<size_t> dual_face_from_primal_cell_and_node_value{dual_connectivity};
+
+      primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_cell_value,
+                                                    dual_face_from_primal_cell_and_node_value);
+
+      bool correct_dual_value = true;
+
+      NodeId node_id = 0;
+      FaceId face_id = 0;
+      for (; node_id < dual_connectivity.numberOfNodes(); ++node_id, ++face_id) {
+        correct_dual_value &=
+          (dual_node_from_primal_cell_and_node_value[node_id] == dual_face_from_primal_cell_and_node_value[face_id]);
+      }
+      REQUIRE(correct_dual_value);
+    }
+
+    {
+      bool is_same = true;
+      NodeValue<size_t> primal_from_dual_node_value{primal_connectivity};
+      CellValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+      primal_to_dual_connectivity_mapper.fromDualNode(dual_node_from_primal_cell_and_node_value,
+                                                      primal_from_dual_node_value, primal_from_dual_cell_value);
+      for (CellId cell_id = 0; cell_id < primal_connectivity.numberOfCells(); ++cell_id) {
+        is_same &= (primal_cell_value[cell_id] == primal_from_dual_cell_value[cell_id]);
+      }
+
+      for (NodeId node_id = 0; node_id < primal_connectivity.numberOfNodes(); ++node_id) {
+        if (primal_node_to_cell_matrix[node_id].size() == 1) {
+          is_same &= (primal_node_value[node_id] == primal_from_dual_node_value[node_id]);
+        }
+      }
+
+      REQUIRE(is_same);
+    }
+
+#ifndef NDEBUG
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(NodeValue<size_t>{dual_connectivity},
+                                                                      primal_cell_value,
+                                                                      dual_node_from_primal_cell_and_node_value),
+                        "unexpected connectivity for primal NodeValue");
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(primal_node_value,
+                                                                      CellValue<size_t>{dual_connectivity},
+                                                                      dual_node_from_primal_cell_and_node_value),
+                        "unexpected connectivity for primal CellValue");
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_cell_value,
+                                                                      NodeValue<size_t>{primal_connectivity}),
+                        "unexpected connectivity for dual NodeValue");
+
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(dual_node_from_primal_cell_and_node_value,
+                                                                        NodeValue<size_t>{dual_connectivity},
+                                                                        primal_cell_value),
+                        "unexpected connectivity for primal NodeValue");
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(dual_node_from_primal_cell_and_node_value,
+                                                                        primal_node_value,
+                                                                        CellValue<size_t>{dual_connectivity}),
+                        "unexpected connectivity for primal CellValue");
+    REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(NodeValue<size_t>{primal_connectivity},
+                                                                        primal_node_value, primal_cell_value),
+                        "unexpected connectivity for dual NodeValue");
+
+#endif   // NDEBUG
+  }
+}
diff --git a/tests/test_PrimalToMedianDualConnectivityDataMapper.cpp b/tests/test_PrimalToMedianDualConnectivityDataMapper.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2950b0d40c9be3330aecc397e228b9e81bb08682
--- /dev/null
+++ b/tests/test_PrimalToMedianDualConnectivityDataMapper.cpp
@@ -0,0 +1,271 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/PrimalToDual1DConnectivityDataMapper.hpp>
+#include <mesh/PrimalToMedianDualConnectivityDataMapper.hpp>
+
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("PrimalToMedianDualConnectivityDataMapper", "[mesh]")
+{
+  SECTION("1D")
+  {
+    constexpr static size_t Dimension = 1;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().unordered1DMesh();
+    const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+    std::shared_ptr p_median_dual_connectivity =
+      DualConnectivityManager::instance().getMedianDualConnectivity(primal_connectivity);
+
+    std::shared_ptr p_dual_1d_connectivity =
+      DualConnectivityManager::instance().getDual1DConnectivity(primal_connectivity);
+
+    REQUIRE(p_median_dual_connectivity == p_dual_1d_connectivity);
+
+    std::shared_ptr p_primal_to_median_dual_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToMedianDualConnectivityDataMapper(primal_connectivity);
+
+    std::shared_ptr p_primal_to_dual_1d_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToDual1DConnectivityDataMapper(primal_connectivity);
+
+    REQUIRE(p_primal_to_median_dual_connectivity_mapper == p_primal_to_dual_1d_connectivity_mapper);
+  }
+
+  SECTION("2D")
+  {
+    constexpr static size_t Dimension = 2;
+
+    using ConnectivityType = Connectivity<Dimension>;
+    using MeshType         = Mesh<ConnectivityType>;
+
+    std::shared_ptr<const MeshType> mesh        = MeshDataBaseForTests::get().hybrid2DMesh();
+    const ConnectivityType& primal_connectivity = mesh->connectivity();
+
+    std::shared_ptr p_median_dual_connectivity =
+      DualConnectivityManager::instance().getMedianDualConnectivity(primal_connectivity);
+    const ConnectivityType& dual_connectivity = *p_median_dual_connectivity;
+
+    std::shared_ptr p_primal_to_dual_connectivity_mapper =
+      DualConnectivityManager::instance().getPrimalToMedianDualConnectivityDataMapper(primal_connectivity);
+    const auto& primal_to_dual_connectivity_mapper = *p_primal_to_dual_connectivity_mapper;
+
+    SECTION("check data transfer between primal nodes to dual cells")
+    {
+      const auto& primal_node_to_face_matrix = primal_connectivity.nodeToFaceMatrix();
+
+      NodeValue<size_t> primal_node_value{primal_connectivity};
+      parallel_for(
+        primal_node_value.numberOfItems(), PUGS_LAMBDA(const NodeId node_id) {
+          primal_node_value[node_id] = 2 * primal_node_to_face_matrix[node_id].size() + node_id;
+        });
+
+      CellValue<size_t> dual_from_primal_node_value{dual_connectivity};
+
+      primal_to_dual_connectivity_mapper.toDualCell(primal_node_value, dual_from_primal_node_value);
+
+      {
+        const auto& dual_cell_to_node_matrix = dual_connectivity.cellToNodeMatrix();
+        bool correct_value                   = true;
+        for (CellId cell_id = 0; cell_id < dual_connectivity.numberOfCells(); ++cell_id) {
+          correct_value &= (dual_cell_to_node_matrix[cell_id].size() + cell_id == dual_from_primal_node_value[cell_id]);
+        }
+        REQUIRE(correct_value);
+      }
+
+      {
+        bool is_same = true;
+        NodeValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+        primal_to_dual_connectivity_mapper.fromDualCell(dual_from_primal_node_value, primal_from_dual_cell_value);
+        for (NodeId node_id = 0; node_id < primal_connectivity.numberOfNodes(); ++node_id) {
+          is_same &= (primal_from_dual_cell_value[node_id] == primal_node_value[node_id]);
+        }
+        REQUIRE(is_same);
+      }
+
+#ifndef NDEBUG
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualCell(NodeValue<size_t>{dual_connectivity},
+                                                                        dual_from_primal_node_value),
+                          "unexpected connectivity for primal NodeValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualCell(primal_node_value,
+                                                                        CellValue<size_t>{primal_connectivity}),
+                          "unexpected connectivity for dual CellValue");
+
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualCell(dual_from_primal_node_value,
+                                                                          NodeValue<size_t>{dual_connectivity}),
+                          "unexpected connectivity for primal NodeValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualCell(CellValue<size_t>{primal_connectivity},
+                                                                          primal_node_value),
+                          "unexpected connectivity for dual CellValue");
+
+#endif   // NDEBUG
+    }
+
+    SECTION("check data transfer between primal faces and cells to dual nodes")
+    {
+      const auto& primal_cell_to_node_matrix = primal_connectivity.cellToNodeMatrix();
+      CellValue<size_t> primal_cell_value{primal_connectivity};
+      parallel_for(
+        primal_cell_value.numberOfItems(),
+        PUGS_LAMBDA(const CellId cell_id) { primal_cell_value[cell_id] = primal_cell_to_node_matrix[cell_id].size(); });
+
+      const auto& primal_face_to_cell_matrix = primal_connectivity.faceToCellMatrix();
+      FaceValue<size_t> primal_face_value{primal_connectivity};
+      parallel_for(
+        primal_face_value.numberOfItems(), PUGS_LAMBDA(const FaceId face_id) {
+          primal_face_value[face_id] = 2 + (primal_face_to_cell_matrix[face_id].size() == 1);
+        });
+
+      NodeValue<size_t> primal_node_value{primal_connectivity};
+      primal_node_value.fill(2);
+
+      NodeValue<size_t> dual_from_primal_value{dual_connectivity};
+      primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_face_value, primal_cell_value,
+                                                    dual_from_primal_value);
+
+      {
+        const auto& dual_node_to_face_matrix = dual_connectivity.nodeToFaceMatrix();
+        auto dual_node_number                = dual_connectivity.nodeNumber();
+        bool correct_dual_value              = true;
+        for (NodeId node_id = 0; node_id < dual_connectivity.numberOfNodes(); ++node_id) {
+          correct_dual_value &= (dual_from_primal_value[node_id] == dual_node_to_face_matrix[node_id].size());
+        }
+        REQUIRE(correct_dual_value);
+      }
+
+      {
+        bool is_same = true;
+        NodeValue<size_t> primal_from_dual_node_value{primal_connectivity};
+        FaceValue<size_t> primal_from_dual_face_value{primal_connectivity};
+        CellValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+        primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_value,        //
+                                                        primal_from_dual_node_value,   //
+                                                        primal_from_dual_face_value,   //
+                                                        primal_from_dual_cell_value);
+
+        for (CellId cell_id = 0; cell_id < primal_connectivity.numberOfCells(); ++cell_id) {
+          is_same &= (primal_cell_value[cell_id] == primal_from_dual_cell_value[cell_id]);
+        }
+        for (FaceId face_id = 0; face_id < primal_connectivity.numberOfFaces(); ++face_id) {
+          is_same &= (primal_face_value[face_id] == primal_from_dual_face_value[face_id]);
+        }
+        auto primal_face_to_node_matrix = primal_connectivity.faceToNodeMatrix();
+        for (FaceId face_id = 0; face_id < primal_connectivity.numberOfFaces(); ++face_id) {
+          if (primal_face_to_cell_matrix[face_id].size() == 1) {
+            for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) {
+              NodeId node_id = primal_face_to_node_matrix[face_id][i_node];
+              is_same &= (primal_node_value[node_id] == primal_from_dual_node_value[node_id]);
+            }
+          }
+        }
+
+        REQUIRE(is_same);
+      }
+
+      {
+        const auto& primal_edge_to_cell_matrix = primal_connectivity.edgeToCellMatrix();
+        EdgeValue<size_t> primal_edge_value{primal_connectivity};
+        parallel_for(
+          primal_edge_value.numberOfItems(), PUGS_LAMBDA(const EdgeId edge_id) {
+            primal_edge_value[edge_id] = 2 + (primal_edge_to_cell_matrix[edge_id].size() == 1);
+          });
+
+        NodeValue<size_t> primal_node_value{primal_connectivity};
+        primal_node_value.fill(2);
+
+        NodeValue<size_t> dual_from_primal_value{dual_connectivity};
+        primal_to_dual_connectivity_mapper.toDualNode(primal_node_value, primal_edge_value, primal_cell_value,
+                                                      dual_from_primal_value);
+
+        {
+          const auto& dual_node_to_edge_matrix = dual_connectivity.nodeToEdgeMatrix();
+          auto dual_node_number                = dual_connectivity.nodeNumber();
+          bool correct_dual_value              = true;
+          for (NodeId node_id = 0; node_id < dual_connectivity.numberOfNodes(); ++node_id) {
+            correct_dual_value &= (dual_from_primal_value[node_id] == dual_node_to_edge_matrix[node_id].size());
+          }
+          REQUIRE(correct_dual_value);
+        }
+
+        {
+          bool is_same = true;
+          NodeValue<size_t> primal_from_dual_node_value{primal_connectivity};
+          EdgeValue<size_t> primal_from_dual_edge_value{primal_connectivity};
+          CellValue<size_t> primal_from_dual_cell_value{primal_connectivity};
+          primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_value,        //
+                                                          primal_from_dual_node_value,   //
+                                                          primal_from_dual_edge_value,   //
+                                                          primal_from_dual_cell_value);
+
+          for (CellId cell_id = 0; cell_id < primal_connectivity.numberOfCells(); ++cell_id) {
+            is_same &= (primal_cell_value[cell_id] == primal_from_dual_cell_value[cell_id]);
+          }
+          for (EdgeId edge_id = 0; edge_id < primal_connectivity.numberOfFaces(); ++edge_id) {
+            is_same &= (primal_edge_value[edge_id] == primal_from_dual_edge_value[edge_id]);
+          }
+          auto primal_face_to_node_matrix = primal_connectivity.faceToNodeMatrix();
+          for (FaceId face_id = 0; face_id < primal_connectivity.numberOfFaces(); ++face_id) {
+            if (primal_face_to_cell_matrix[face_id].size() == 1) {
+              for (size_t i_node = 0; i_node < primal_face_to_node_matrix[face_id].size(); ++i_node) {
+                NodeId node_id = primal_face_to_node_matrix[face_id][i_node];
+                is_same &= (primal_node_value[node_id] == primal_from_dual_node_value[node_id]);
+              }
+            }
+          }
+
+          REQUIRE(is_same);
+        }
+      }
+
+#ifndef NDEBUG
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(NodeValue<size_t>{dual_connectivity},
+                                                                        primal_face_value,   //
+                                                                        primal_cell_value, dual_from_primal_value),
+                          "unexpected connectivity for primal NodeValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(primal_node_value,
+                                                                        FaceValue<size_t>{dual_connectivity},
+                                                                        primal_cell_value, dual_from_primal_value),
+                          "unexpected connectivity for primal FaceValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(primal_node_value,   //
+                                                                        primal_face_value,   //
+                                                                        CellValue<size_t>{dual_connectivity},
+                                                                        dual_from_primal_value),
+                          "unexpected connectivity for primal CellValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.toDualNode(primal_node_value,   //
+                                                                        primal_face_value,   //
+                                                                        primal_cell_value,
+                                                                        NodeValue<size_t>{primal_connectivity}),
+                          "unexpected connectivity for dual NodeValue");
+
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_value,
+                                                                          NodeValue<size_t>{dual_connectivity},
+                                                                          primal_face_value,   //
+                                                                          primal_cell_value),
+                          "unexpected connectivity for primal NodeValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_value, primal_node_value,
+                                                                          FaceValue<size_t>{dual_connectivity},
+                                                                          primal_cell_value),
+                          "unexpected connectivity for primal FaceValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(dual_from_primal_value,
+                                                                          primal_node_value,   //
+                                                                          primal_face_value,   //
+                                                                          CellValue<size_t>{dual_connectivity}),
+                          "unexpected connectivity for primal CellValue");
+      REQUIRE_THROWS_WITH(primal_to_dual_connectivity_mapper.fromDualNode(NodeValue<size_t>{primal_connectivity},
+                                                                          primal_node_value,   //
+                                                                          primal_face_value,   //
+                                                                          primal_cell_value),
+                          "unexpected connectivity for dual NodeValue");
+
+#endif   // NDEBUG
+    }
+  }
+}
diff --git a/tests/test_PrismGaussQuadrature.cpp b/tests/test_PrismGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..000edeed6a40556d702bd440a26aab90f37b7e5d
--- /dev/null
+++ b/tests/test_PrismGaussQuadrature.cpp
@@ -0,0 +1,690 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/PrismGaussQuadrature.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/TetrahedronTransformation.hpp>
+#include <utils/Exceptions.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("PrismGaussQuadrature", "[analysis]")
+{
+  auto integrate = [](auto f, auto quadrature_formula) {
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(point_list[0]);
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(point_list[i]);
+    }
+
+    return value;
+  };
+
+  auto integrate_on_prism = [](auto f, auto quadrature_formula, const std::array<TinyVector<3>, 4>& tetrahedron) {
+    const auto& A = tetrahedron[0];
+    const auto& B = tetrahedron[1];
+    const auto& C = tetrahedron[2];
+    const auto& D = tetrahedron[3];
+
+    TinyMatrix<3> J;
+    for (size_t i = 0; i < 3; ++i) {
+      J(i, 0) = B[i] - A[i];
+      J(i, 1) = C[i] - A[i];
+      J(i, 2) = 0.5 * (D[i] - A[i]);
+    }
+    TinyVector s = 0.5 * (A + D);
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+    auto value       = weight_list[0] * f(J * (point_list[0]) + s);
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(J * (point_list[i]) + s);
+    }
+
+    return det(J) * value;
+  };
+
+  auto get_order = [&integrate, &integrate_on_prism](auto f, auto quadrature_formula, const double exact_value) {
+    using R3 = TinyVector<3>;
+
+    const R3 A{+0, +0, -1};
+    const R3 B{+1, +0, -1};
+    const R3 C{+0, +1, -1};
+    const R3 D{+0, +0, +1};
+    const R3 E{+1, +0, +1};
+    const R3 F{+0, +1, +1};
+
+    const R3 M_AB   = 0.5 * (A + B);
+    const R3 M_AC   = 0.5 * (A + C);
+    const R3 M_BC   = 0.5 * (B + C);
+    const R3 M_DE   = 0.5 * (D + E);
+    const R3 M_AD   = 0.5 * (A + D);
+    const R3 M_BE   = 0.5 * (B + E);
+    const R3 M_CF   = 0.5 * (C + F);
+    const R3 M_ABED = 0.25 * (A + B + E + D);
+    const R3 M_BCFE = 0.25 * (B + C + E + F);
+    const R3 M_ADFC = 0.25 * (A + D + F + C);
+
+    const double int_T_hat = integrate(f, quadrature_formula);
+    const double int_refined   //
+      = integrate_on_prism(f, quadrature_formula, {A, M_AB, M_AC, M_AD}) +
+        integrate_on_prism(f, quadrature_formula, {B, M_BC, M_AB, M_BE}) +
+        integrate_on_prism(f, quadrature_formula, {C, M_AC, M_BC, M_CF}) +
+        integrate_on_prism(f, quadrature_formula, {M_AB, M_BC, M_AC, M_ABED}) +
+        integrate_on_prism(f, quadrature_formula, {M_AD, M_ABED, M_ADFC, D}) +
+        integrate_on_prism(f, quadrature_formula, {M_BE, M_BCFE, M_ABED, E}) +
+        integrate_on_prism(f, quadrature_formula, {M_CF, M_ADFC, M_BCFE, F}) +
+        integrate_on_prism(f, quadrature_formula, {M_ABED, M_BCFE, M_ADFC, M_DE});
+
+    return -std::log(std::abs(int_refined - exact_value) / std::abs(int_T_hat - exact_value)) / std::log(2);
+  };
+
+  auto p0 = [](const TinyVector<3>&) { return 4; };
+  auto p1 = [](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return 2 * x + 3 * y + z - 1;
+  };
+  auto p2 = [&p1](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p1(X) * (2.5 * x - 3 * y + z + 3);
+  };
+  auto p3 = [&p2](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p2(X) * (3 * x + 2 * y - 3 * z - 1);
+  };
+  auto p4 = [&p3](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p3(X) * (2 * x - 0.5 * y - 1.3 * z + 1);
+  };
+  auto p5 = [&p4](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p4(X) * (-0.1 * x + 1.3 * y - 3 * z + 1);
+  };
+  auto p6 = [&p5](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p5(X) * 7875. / 143443 * (2 * x - y + 4 * z + 1);
+  };
+  auto p7 = [&p6](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p6(X) * (0.7 * x - 2.7 * y + 1.3 * z - 2);
+  };
+  auto p8 = [&p7](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p7(X) * (0.3 * x + 1.2 * y - 0.7 * z + 0.2);
+  };
+  auto p9 = [&p8](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p8(X) * (-0.2 * x + 1.1 * y - 0.5 * z + 0.6);
+  };
+  auto p10 = [&p9](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p9(X) * (0.7 * x - 0.6 * y - 0.7 * z - 0.2);
+  };
+  auto p11 = [&p10](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p10(X) * (-1.3 * x + 0.6 * y - 1.3 * z + 0.7);
+  };
+  auto p12 = [&p11](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p11(X) * (0.3 * x - 0.7 * y + 0.3 * z + 0.7);
+  };
+  auto p13 = [&p12](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p12(X) * (0.9 * x + 0.2 * y - 0.4 * z + 0.5);
+  };
+  auto p14 = [&p13](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p13(X) * (0.6 * x - 1.2 * y + 0.7 * z - 0.4);
+  };
+  auto p15 = [&p14](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p14(X) * (-0.2 * x - 0.7 * y + 0.9 * z + 0.8);
+  };
+  auto p16 = [&p15](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p15(X) * (0.7 * x + 0.2 * y - 0.6 * z + 0.4);
+  };
+  auto p17 = [&p16](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p16(X) * (-0.1 * x + 0.8 * y + 0.3 * z - 0.2);
+  };
+  auto p18 = [&p17](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p17(X) * (0.7 * x - 0.2 * y - 0.3 * z + 0.8);
+  };
+  auto p19 = [&p18](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p18(X) * (-0.7 * x + 1.2 * y + 1.3 * z + 0.8);
+  };
+  auto p20 = [&p19](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p19(X) * (0.7 * x - 1.2 * y + 0.3 * z - 0.6);
+  };
+  auto p21 = [&p20](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p20(X) * (0.7 * x - 1.2 * y + 0.3 * z - 0.6);
+  };
+
+  SECTION("degree 1")
+  {
+    const QuadratureFormula<3>& l1 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(1));
+
+    REQUIRE(l1.numberOfPoints() == 1);
+
+    REQUIRE(integrate(p0, l1) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l1) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l1) != Catch::Approx(47. / 24));
+
+    REQUIRE(get_order(p2, l1, 47. / 24) == Catch::Approx(2));
+  }
+
+  SECTION("degree 2")
+  {
+    const QuadratureFormula<3>& l2 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(2));
+
+    REQUIRE(l2.numberOfPoints() == 5);
+
+    REQUIRE(integrate(p0, l2) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l2) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l2) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l2) != Catch::Approx(-427. / 360));
+
+    // In a weird way, one gets 4th order on this test
+    REQUIRE(get_order(p3, l2, -427. / 360) == Catch::Approx(4));
+  }
+
+  SECTION("degree 3")
+  {
+    const QuadratureFormula<3>& l3 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(3));
+
+    REQUIRE(l3.numberOfPoints() == 8);
+
+    REQUIRE(integrate(p0, l3) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l3) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l3) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l3) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l3) != Catch::Approx(1003. / 3600));
+
+    REQUIRE(get_order(p4, l3, 1003. / 3600) == Catch::Approx(4));
+  }
+
+  SECTION("degree 4")
+  {
+    const QuadratureFormula<3>& l4 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(4));
+
+    REQUIRE(l4.numberOfPoints() == 11);
+
+    REQUIRE(integrate(p0, l4) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l4) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l4) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l4) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l4) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l4) != Catch::Approx(34031. / 18000));
+
+    REQUIRE(get_order(p5, l4, 34031. / 18000) >= Catch::Approx(5));
+  }
+
+  SECTION("degree 5")
+  {
+    const QuadratureFormula<3>& l5 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(5));
+
+    REQUIRE(l5.numberOfPoints() == 16);
+
+    REQUIRE(integrate(p0, l5) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l5) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l5) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l5) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l5) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l5) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l5) != Catch::Approx(36346369. / 55082112));
+
+    REQUIRE(get_order(p6, l5, 36346369. / 55082112) == Catch::Approx(6));
+  }
+
+  SECTION("degree 6")
+  {
+    const QuadratureFormula<3>& l6 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(6));
+
+    REQUIRE(l6.numberOfPoints() == 28);
+
+    REQUIRE(integrate(p0, l6) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l6) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l6) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l6) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l6) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l6) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l6) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l6) != Catch::Approx(-1128861017. / 918035200));
+
+    // In a weird way, one gets 8th order on this test
+    REQUIRE(get_order(p7, l6, -1128861017. / 918035200) == Catch::Approx(8));
+  }
+
+  SECTION("degree 7")
+  {
+    const QuadratureFormula<3>& l7 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(7));
+
+    REQUIRE(l7.numberOfPoints() == 35);
+
+    REQUIRE(integrate(p0, l7) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l7) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l7) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l7) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l7) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l7) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l7) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l7) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l7) != Catch::Approx(-21178319419. / 27541056000));
+
+    REQUIRE(get_order(p8, l7, -21178319419. / 27541056000) == Catch::Approx(8));
+  }
+
+  SECTION("degree 8")
+  {
+    const QuadratureFormula<3>& l8 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(8));
+
+    REQUIRE(l8.numberOfPoints() == 46);
+
+    REQUIRE(integrate(p0, l8) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l8) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l8) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l8) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l8) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l8) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l8) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l8) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l8) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l8) != Catch::Approx(-5483758803191. / 9088548480000));
+
+    // In a weird way, one gets 10th order on this test
+    REQUIRE(get_order(p9, l8, -5483758803191. / 9088548480000) == Catch::Approx(10));
+  }
+
+  SECTION("degree 9")
+  {
+    const QuadratureFormula<3>& l9 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(9));
+
+    REQUIRE(l9.numberOfPoints() == 59);
+
+    REQUIRE(integrate(p0, l9) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l9) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l9) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l9) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l9) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l9) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l9) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l9) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l9) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l9) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l9) != Catch::Approx(-9456848221657. / 22721371200000));
+
+    REQUIRE(get_order(p10, l9, -9456848221657. / 22721371200000) == Catch::Approx(10));
+  }
+
+  SECTION("degree 10")
+  {
+    const QuadratureFormula<3>& l10 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(10));
+
+    REQUIRE(l10.numberOfPoints() == 84);
+
+    REQUIRE(integrate(p0, l10) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l10) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l10) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l10) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l10) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l10) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l10) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l10) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l10) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l10) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l10) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l10) != Catch::Approx(-4571362439539697. / 7518708288000000));
+
+    // In a weird way, one gets 12th order on this test
+    REQUIRE(get_order(p11, l10, -4571362439539697. / 7518708288000000) == Catch::Approx(12));
+  }
+
+  SECTION("degree 11")
+  {
+    const QuadratureFormula<3>& l11 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(11));
+
+    REQUIRE(l11.numberOfPoints() == 99);
+
+    REQUIRE(integrate(p0, l11) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l11) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l11) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l11) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l11) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l11) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l11) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l11) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l11) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l11) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l11) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l11) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l11) != Catch::Approx(-491755535075074133. / 1378429852800000000));
+
+    REQUIRE(get_order(p12, l11, -491755535075074133. / 1378429852800000000) == Catch::Approx(12));
+  }
+
+  SECTION("degree 12")
+  {
+    const QuadratureFormula<3>& l12 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(12));
+
+    REQUIRE(l12.numberOfPoints() == 136);
+
+    REQUIRE(integrate(p0, l12) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l12) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l12) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l12) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l12) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l12) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l12) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l12) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l12) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l12) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l12) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l12) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l12) == Catch::Approx(-491755535075074133. / 1378429852800000000));
+    REQUIRE(integrate(p13, l12) != Catch::Approx(-1620413117251976393. / 4135289558400000000));
+
+    // In a weird way, one gets almost 14th order on this test
+    REQUIRE(get_order(p13, l12, -1620413117251976393. / 4135289558400000000) == Catch::Approx(14).margin(0.01));
+  }
+
+  SECTION("degree 13")
+  {
+    const QuadratureFormula<3>& l13 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(13));
+
+    REQUIRE(l13.numberOfPoints() == 162);
+
+    REQUIRE(integrate(p0, l13) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l13) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l13) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l13) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l13) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l13) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l13) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l13) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l13) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l13) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l13) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l13) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l13) == Catch::Approx(-491755535075074133. / 1378429852800000000));
+    REQUIRE(integrate(p13, l13) == Catch::Approx(-1620413117251976393. / 4135289558400000000));
+    REQUIRE(integrate(p14, l13) != Catch::Approx(296918520496968826367. / 827057911680000000000.));
+
+    REQUIRE(get_order(p14, l13, 296918520496968826367. / 827057911680000000000.) == Catch::Approx(14));
+  }
+
+  SECTION("degree 14")
+  {
+    const QuadratureFormula<3>& l14 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(14));
+
+    REQUIRE(l14.numberOfPoints() == 194);
+
+    REQUIRE(integrate(p0, l14) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l14) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l14) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l14) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l14) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l14) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l14) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l14) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l14) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l14) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l14) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l14) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l14) == Catch::Approx(-491755535075074133. / 1378429852800000000));
+    REQUIRE(integrate(p13, l14) == Catch::Approx(-1620413117251976393. / 4135289558400000000));
+    REQUIRE(integrate(p14, l14) == Catch::Approx(296918520496968826367. / 827057911680000000000.));
+    REQUIRE(integrate(p15, l14) != Catch::Approx(-7727953154629829488841. / 210899767478400000000000.));
+
+    // In a weird way, one gets almost 16th order on this test
+    REQUIRE(get_order(p15, l14, -7727953154629829488841. / 210899767478400000000000.) ==
+            Catch::Approx(16).margin(0.01));
+  }
+
+  SECTION("degree 15")
+  {
+    const QuadratureFormula<3>& l15 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(15));
+
+    REQUIRE(l15.numberOfPoints() == 238);
+
+    REQUIRE(integrate(p0, l15) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l15) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l15) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l15) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l15) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l15) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l15) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l15) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l15) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l15) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l15) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l15) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l15) == Catch::Approx(-491755535075074133. / 1378429852800000000));
+    REQUIRE(integrate(p13, l15) == Catch::Approx(-1620413117251976393. / 4135289558400000000));
+    REQUIRE(integrate(p14, l15) == Catch::Approx(296918520496968826367. / 827057911680000000000.));
+    REQUIRE(integrate(p15, l15) == Catch::Approx(-7727953154629829488841. / 210899767478400000000000.));
+    REQUIRE(integrate(p16, l15) != Catch::Approx(-18153283669186101815689. / 527249418696000000000000.));
+
+    REQUIRE(get_order(p16, l15, -18153283669186101815689. / 527249418696000000000000.) == Catch::Approx(16));
+  }
+
+  SECTION("degree 16")
+  {
+    const QuadratureFormula<3>& l16 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(16));
+
+    REQUIRE(l16.numberOfPoints() == 287);
+
+    REQUIRE(integrate(p0, l16) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l16) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l16) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l16) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l16) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l16) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l16) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l16) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l16) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l16) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l16) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l16) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l16) == Catch::Approx(-491755535075074133. / 1378429852800000000));
+    REQUIRE(integrate(p13, l16) == Catch::Approx(-1620413117251976393. / 4135289558400000000));
+    REQUIRE(integrate(p14, l16) == Catch::Approx(296918520496968826367. / 827057911680000000000.));
+    REQUIRE(integrate(p15, l16) == Catch::Approx(-7727953154629829488841. / 210899767478400000000000.));
+    REQUIRE(integrate(p16, l16) == Catch::Approx(-18153283669186101815689. / 527249418696000000000000.));
+    REQUIRE(integrate(p17, l16) != Catch::Approx(5157361121064510230030071. / 200354779104480000000000000.));
+
+    // In a weird way, one gets almost 18th order on this test
+    REQUIRE(get_order(p17, l16, 5157361121064510230030071. / 200354779104480000000000000.) ==
+            Catch::Approx(18).margin(0.1));
+  }
+
+  SECTION("degree 17")
+  {
+    const QuadratureFormula<3>& l17 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(17));
+
+    REQUIRE(l17.numberOfPoints() == 338);
+
+    REQUIRE(integrate(p0, l17) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l17) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l17) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l17) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l17) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l17) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l17) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l17) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l17) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l17) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l17) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l17) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l17) == Catch::Approx(-491755535075074133. / 1378429852800000000));
+    REQUIRE(integrate(p13, l17) == Catch::Approx(-1620413117251976393. / 4135289558400000000));
+    REQUIRE(integrate(p14, l17) == Catch::Approx(296918520496968826367. / 827057911680000000000.));
+    REQUIRE(integrate(p15, l17) == Catch::Approx(-7727953154629829488841. / 210899767478400000000000.));
+    REQUIRE(integrate(p16, l17) == Catch::Approx(-18153283669186101815689. / 527249418696000000000000.));
+    REQUIRE(integrate(p17, l17) == Catch::Approx(5157361121064510230030071. / 200354779104480000000000000.));
+    REQUIRE(integrate(p18, l17) !=
+            Catch::Approx(195337148397715128549413507. / 5609933814925440000000000000.).epsilon(1E-10));
+
+    REQUIRE(get_order(p18, l17, 195337148397715128549413507. / 5609933814925440000000000000.) == Catch::Approx(18));
+  }
+
+  SECTION("degree 18")
+  {
+    const QuadratureFormula<3>& l18 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(18));
+
+    REQUIRE(l18.numberOfPoints() == 396);
+
+    REQUIRE(integrate(p0, l18) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l18) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l18) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l18) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l18) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l18) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l18) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l18) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l18) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l18) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l18) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l18) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l18) == Catch::Approx(-491755535075074133. / 1378429852800000000));
+    REQUIRE(integrate(p13, l18) == Catch::Approx(-1620413117251976393. / 4135289558400000000));
+    REQUIRE(integrate(p14, l18) == Catch::Approx(296918520496968826367. / 827057911680000000000.));
+    REQUIRE(integrate(p15, l18) == Catch::Approx(-7727953154629829488841. / 210899767478400000000000.));
+    REQUIRE(integrate(p16, l18) == Catch::Approx(-18153283669186101815689. / 527249418696000000000000.));
+    REQUIRE(integrate(p17, l18) == Catch::Approx(5157361121064510230030071. / 200354779104480000000000000.));
+    REQUIRE(integrate(p18, l18) == Catch::Approx(195337148397715128549413507. / 5609933814925440000000000000.));
+    REQUIRE(integrate(p19, l18) !=
+            Catch::Approx(-417563570921497136922189149. / 14024834537313600000000000000.).epsilon(1E-10));
+
+    // In a weird way, one gets almost 20th order on this test
+    REQUIRE(get_order(p19, l18, -417563570921497136922189149. / 14024834537313600000000000000.) ==
+            Catch::Approx(20).margin(0.01));
+  }
+
+  SECTION("degree 19")
+  {
+    const QuadratureFormula<3>& l19 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(19));
+
+    REQUIRE(l19.numberOfPoints() == 420);
+
+    REQUIRE(integrate(p0, l19) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l19) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l19) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l19) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l19) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l19) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l19) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l19) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l19) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l19) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l19) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l19) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l19) == Catch::Approx(-491755535075074133. / 1378429852800000000));
+    REQUIRE(integrate(p13, l19) == Catch::Approx(-1620413117251976393. / 4135289558400000000));
+    REQUIRE(integrate(p14, l19) == Catch::Approx(296918520496968826367. / 827057911680000000000.));
+    REQUIRE(integrate(p15, l19) == Catch::Approx(-7727953154629829488841. / 210899767478400000000000.));
+    REQUIRE(integrate(p16, l19) == Catch::Approx(-18153283669186101815689. / 527249418696000000000000.));
+    REQUIRE(integrate(p17, l19) == Catch::Approx(5157361121064510230030071. / 200354779104480000000000000.));
+    REQUIRE(integrate(p18, l19) == Catch::Approx(195337148397715128549413507. / 5609933814925440000000000000.));
+    REQUIRE(integrate(p19, l19) == Catch::Approx(-417563570921497136922189149. / 14024834537313600000000000000.));
+    REQUIRE(integrate(p20, l19) !=
+            Catch::Approx(4740816174053415637444760963. / 205697573213932800000000000000.).epsilon(1E-10));
+
+    REQUIRE(get_order(p20, l19, 4740816174053415637444760963. / 205697573213932800000000000000.) ==
+            Catch::Approx(20).margin(0.01));
+  }
+
+  SECTION("degree 20")
+  {
+    const QuadratureFormula<3>& l20 = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(20));
+
+    REQUIRE(l20.numberOfPoints() == 518);
+
+    REQUIRE(integrate(p0, l20) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l20) == Catch::Approx(2. / 3));
+    REQUIRE(integrate(p2, l20) == Catch::Approx(47. / 24));
+    REQUIRE(integrate(p3, l20) == Catch::Approx(-427. / 360));
+    REQUIRE(integrate(p4, l20) == Catch::Approx(1003. / 3600));
+    REQUIRE(integrate(p5, l20) == Catch::Approx(34031. / 18000));
+    REQUIRE(integrate(p6, l20) == Catch::Approx(36346369. / 55082112));
+    REQUIRE(integrate(p7, l20) == Catch::Approx(-1128861017. / 918035200));
+    REQUIRE(integrate(p8, l20) == Catch::Approx(-21178319419. / 27541056000));
+    REQUIRE(integrate(p9, l20) == Catch::Approx(-5483758803191. / 9088548480000));
+    REQUIRE(integrate(p10, l20) == Catch::Approx(-9456848221657. / 22721371200000));
+    REQUIRE(integrate(p11, l20) == Catch::Approx(-4571362439539697. / 7518708288000000));
+    REQUIRE(integrate(p12, l20) == Catch::Approx(-491755535075074133. / 1378429852800000000));
+    REQUIRE(integrate(p13, l20) == Catch::Approx(-1620413117251976393. / 4135289558400000000));
+    REQUIRE(integrate(p14, l20) == Catch::Approx(296918520496968826367. / 827057911680000000000.));
+    REQUIRE(integrate(p15, l20) == Catch::Approx(-7727953154629829488841. / 210899767478400000000000.));
+    REQUIRE(integrate(p16, l20) == Catch::Approx(-18153283669186101815689. / 527249418696000000000000.));
+    REQUIRE(integrate(p17, l20) == Catch::Approx(5157361121064510230030071. / 200354779104480000000000000.));
+    REQUIRE(integrate(p18, l20) == Catch::Approx(195337148397715128549413507. / 5609933814925440000000000000.));
+    REQUIRE(integrate(p19, l20) == Catch::Approx(-417563570921497136922189149. / 14024834537313600000000000000.));
+    REQUIRE(integrate(p20, l20) == Catch::Approx(4740816174053415637444760963. / 205697573213932800000000000000.));
+    REQUIRE(integrate(p21, l20) !=
+            Catch::Approx(-164372186128198750911065614811351. / 7096566275880681600000000000000000.).epsilon(1E-10));
+
+    // In a weird way, one gets almost 22th order on this test
+    REQUIRE(get_order(p21, l20, -164372186128198750911065614811351. / 7096566275880681600000000000000000.) ==
+            Catch::Approx(22).margin(0.01));
+  }
+
+  SECTION("max implemented degree")
+  {
+    REQUIRE(QuadratureManager::instance().maxPrismDegree(QuadratureType::Gauss) == PrismGaussQuadrature::max_degree);
+    REQUIRE_THROWS_WITH(QuadratureManager::instance().getPrismFormula(
+                          GaussQuadratureDescriptor(PrismGaussQuadrature ::max_degree + 1)),
+                        "error: Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(PrismGaussQuadrature ::max_degree) + " on prisms");
+  }
+}
diff --git a/tests/test_PrismTransformation.cpp b/tests/test_PrismTransformation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8ffa0516c653820357eaf3b5f1f7cea3c8b757ef
--- /dev/null
+++ b/tests/test_PrismTransformation.cpp
@@ -0,0 +1,106 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/PrismTransformation.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("PrismTransformation", "[geometry]")
+{
+  using R3 = TinyVector<3>;
+
+  const R3 a_hat{0, 0, -1};
+  const R3 b_hat{1, 0, -1};
+  const R3 c_hat{0, 1, -1};
+  const R3 d_hat{0, 0, +1};
+  const R3 e_hat{1, 0, +1};
+  const R3 f_hat{0, 1, +1};
+
+  const R3 m_hat{1. / 3, 1. / 3, 0};
+
+  const R3 a{1, 2, 0};
+  const R3 b{3, 1, 3};
+  const R3 c{2, 5, 2};
+  const R3 d{0, 3, 1};
+  const R3 e{1, 2, 5};
+  const R3 f{3, 1, 7};
+
+  const PrismTransformation t(a, b, c, d, e, f);
+
+  SECTION("points")
+  {
+    REQUIRE(l2Norm(t(a_hat) - a) == Catch::Approx(0));
+    REQUIRE(l2Norm(t(b_hat) - b) == Catch::Approx(0));
+    REQUIRE(l2Norm(t(c_hat) - c) == Catch::Approx(0));
+    REQUIRE(l2Norm(t(d_hat) - d) == Catch::Approx(0));
+    REQUIRE(l2Norm(t(e_hat) - e) == Catch::Approx(0));
+    REQUIRE(l2Norm(t(f_hat) - f) == Catch::Approx(0));
+
+    R3 m = (1. / 6) * (a + b + c + d + e + f);
+
+    REQUIRE(l2Norm(t(m_hat) - m) == Catch::Approx(0).margin(1E-14));
+  }
+
+  SECTION("Jacobian determinant")
+  {
+    SECTION("at points")
+    {
+      auto detJ = [](const R3 X) {
+        const double& x = X[0];
+        const double& y = X[1];
+        const double& z = X[2];
+
+        return ((2 * y + 0.5 * x + 0.5) * (z + 2) - (y - 0.5 * x - 0.5) * (2 * z + 4)) +
+               (1.5 - 0.5 * z) * ((2 * y + 0.5 * x + 0.5) * (0.5 - 2.5 * z) - (0.5 - 2.5 * y) * (2 * z + 4)) +
+               (0.5 * z + 3.5) * ((0.5 - 2.5 * y) * (z + 2) - (y - 0.5 * x - 0.5) * (0.5 - 2.5 * z));
+      };
+
+      REQUIRE(t.jacobianDeterminant(a_hat) == Catch::Approx(detJ(a_hat)));
+      REQUIRE(t.jacobianDeterminant(b_hat) == Catch::Approx(detJ(b_hat)));
+      REQUIRE(t.jacobianDeterminant(c_hat) == Catch::Approx(detJ(c_hat)));
+      REQUIRE(t.jacobianDeterminant(d_hat) == Catch::Approx(detJ(d_hat)));
+      REQUIRE(t.jacobianDeterminant(e_hat) == Catch::Approx(detJ(e_hat)));
+      REQUIRE(t.jacobianDeterminant(f_hat) == Catch::Approx(detJ(f_hat)));
+
+      REQUIRE(t.jacobianDeterminant(m_hat) == Catch::Approx(detJ(m_hat)));
+    }
+
+    SECTION("volume calculation")
+    {
+      // due to the z component of the jacobian determinant, degree 3
+      // polynomials must be exactly integrated
+      const QuadratureFormula<3>& gauss = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(3));
+
+      double volume = 0;
+      for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+        volume += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i));
+      }
+
+      // 11/2 is actually the volume of the prism
+      REQUIRE(volume == Catch::Approx(11. / 2));
+    }
+
+    SECTION("exact polynomial integration")
+    {
+      auto p = [](const R3& X) {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+
+        return 3 * x * x + 2 * y * y + 3 * z * z + 4 * x + 3 * y + 2 * z + 1;
+      };
+
+      // 5 is the minimum quadrature rule to integrate the polynomial on the prism
+      const QuadratureFormula<3>& gauss = QuadratureManager::instance().getPrismFormula(GaussQuadratureDescriptor(5));
+
+      double integral = 0;
+      for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+        integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+      }
+
+      REQUIRE(integral == Catch::Approx(30377. / 90));
+    }
+  }
+}
diff --git a/tests/test_PugsFunctionAdapter.cpp b/tests/test_PugsFunctionAdapter.cpp
index 76f3a06aa3f96c208e6c02c16f8006e22160a8f6..d8708c49a9601d38602251a5179cad939382f291 100644
--- a/tests/test_PugsFunctionAdapter.cpp
+++ b/tests/test_PugsFunctionAdapter.cpp
@@ -33,7 +33,7 @@ class TestBinary<OutputType(InputType...)> : public PugsFunctionAdapter<OutputTy
     auto& expression    = Adapter::getFunctionExpression(function_symbol_id);
     auto convert_result = Adapter::getResultConverter(expression.m_data_type);
 
-    Array<ExecutionPolicy> context_list = Adapter::getContextList(expression);
+    auto context_list = Adapter::getContextList(expression);
 
     auto& execution_policy = context_list[0];
 
@@ -50,7 +50,7 @@ class TestBinary<OutputType(InputType...)> : public PugsFunctionAdapter<OutputTy
     auto& expression    = Adapter::getFunctionExpression(function_symbol_id);
     auto convert_result = Adapter::getResultConverter(expression.m_data_type);
 
-    Array<ExecutionPolicy> context_list = Adapter::getContextList(expression);
+    auto context_list = Adapter::getContextList(expression);
 
     auto& execution_policy = context_list[0];
 
@@ -221,7 +221,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
         FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
         TinyVector<1> result = tests_adapter::TestBinary<TinyVector<1>(bool)>::one_arg(function_symbol_id, b);
 
-        REQUIRE(result == not b);
+        REQUIRE(result == TinyVector<1>{not b});
       }
 
       {
@@ -230,7 +230,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
         FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
         TinyVector<1> result = tests_adapter::TestBinary<TinyVector<1>(bool)>::one_arg(function_symbol_id, b);
 
-        REQUIRE(result == not b);
+        REQUIRE(result == TinyVector<1>{not b});
       }
     }
 
@@ -245,7 +245,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
         FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
         TinyMatrix<1> result = tests_adapter::TestBinary<TinyMatrix<1>(bool)>::one_arg(function_symbol_id, b);
 
-        REQUIRE(result == not b);
+        REQUIRE(result == TinyMatrix<1>{not b});
       }
 
       {
@@ -254,7 +254,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
         FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
         TinyMatrix<1> result = tests_adapter::TestBinary<TinyMatrix<1>(bool)>::one_arg(function_symbol_id, b);
 
-        REQUIRE(result == not b);
+        REQUIRE(result == TinyMatrix<1>{not b});
       }
     }
 
@@ -268,7 +268,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
       FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
       TinyVector<1> result = tests_adapter::TestBinary<TinyVector<1>(uint64_t)>::one_arg(function_symbol_id, n);
 
-      REQUIRE(result == n * n);
+      REQUIRE(result == TinyVector<1>{n * n});
     }
 
     {
@@ -281,7 +281,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
       FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
       TinyMatrix<1> result = tests_adapter::TestBinary<TinyMatrix<1>(uint64_t)>::one_arg(function_symbol_id, n);
 
-      REQUIRE(result == n * n);
+      REQUIRE(result == TinyMatrix<1>{n * n});
     }
 
     {
@@ -294,7 +294,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
       FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
       TinyVector<1> result = tests_adapter::TestBinary<TinyVector<1>(int64_t)>::one_arg(function_symbol_id, z);
 
-      REQUIRE(result == -z);
+      REQUIRE(result == TinyVector<1>{-z});
     }
 
     {
@@ -307,7 +307,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
       FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
       TinyMatrix<1> result = tests_adapter::TestBinary<TinyMatrix<1>(int64_t)>::one_arg(function_symbol_id, z);
 
-      REQUIRE(result == -z);
+      REQUIRE(result == TinyMatrix<1>{-z});
     }
 
     {
@@ -320,7 +320,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
       FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
       TinyVector<1> result = tests_adapter::TestBinary<TinyVector<1>(double)>::one_arg(function_symbol_id, x);
 
-      REQUIRE(result == x * x);
+      REQUIRE(result == TinyVector<1>{x * x});
     }
 
     {
@@ -333,7 +333,7 @@ let R33toR33zero: R^3x3 -> R^3x3, x -> 0;
       FunctionSymbolId function_symbol_id(std::get<uint64_t>(i_symbol->attributes().value()), symbol_table);
       TinyMatrix<1> result = tests_adapter::TestBinary<TinyMatrix<1>(double)>::one_arg(function_symbol_id, x);
 
-      REQUIRE(result == x * x);
+      REQUIRE(result == TinyMatrix<1>{x * x});
     }
 
     {
diff --git a/tests/test_PugsUtils.cpp b/tests/test_PugsUtils.cpp
index 4632ae825bf9089834f90ba082d7481746885d28..19d693c76599937de4b789c2c07785844daa100c 100644
--- a/tests/test_PugsUtils.cpp
+++ b/tests/test_PugsUtils.cpp
@@ -84,7 +84,7 @@ TEST_CASE("PugsUtils", "[utils]")
       unsetenv("OMP_PLACES");
 
       setDefaultOMPEnvironment();
-      REQUIRE(std::string{getenv("OMP_PROC_BIND")} == std::string{"spread"});
+      REQUIRE(std::string{getenv("OMP_PROC_BIND")} == std::string{"spread,close"});
       REQUIRE(std::string{getenv("OMP_PLACES")} == std::string{"threads"});
 
       unsetenv("OMP_PROC_BIND");
diff --git a/tests/test_PyramidGaussQuadrature.cpp b/tests/test_PyramidGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ea022b1360908ccd3f270d3afda53b710a486488
--- /dev/null
+++ b/tests/test_PyramidGaussQuadrature.cpp
@@ -0,0 +1,574 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/PyramidGaussQuadrature.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/TetrahedronTransformation.hpp>
+#include <utils/Exceptions.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("PyramidGaussQuadrature", "[analysis]")
+{
+  auto integrate = [](auto f, auto quadrature_formula) {
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(point_list[0]);
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(point_list[i]);
+    }
+
+    return value;
+  };
+
+  auto p0 = [](const TinyVector<3>&) { return 4; };
+  auto p1 = [](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return 2 * x + 3 * y + z - 1;
+  };
+  auto p2 = [&p1](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p1(X) * (2.5 * x - 3 * y + z + 3);
+  };
+  auto p3 = [&p2](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p2(X) * (3 * x + 2 * y - 3 * z - 1);
+  };
+  auto p4 = [&p3](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p3(X) * (2 * x - 0.5 * y - 1.3 * z + 1);
+  };
+  auto p5 = [&p4](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p4(X) * (-0.1 * x + 1.3 * y - 3 * z + 1);
+  };
+  auto p6 = [&p5](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p5(X) * 7875. / 143443 * (2 * x - y + 4 * z + 1);
+  };
+  auto p7 = [&p6](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p6(X) * (0.7 * x - 2.7 * y + 1.3 * z - 2);
+  };
+  auto p8 = [&p7](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p7(X) * (0.3 * x + 1.2 * y - 0.7 * z + 0.2);
+  };
+  auto p9 = [&p8](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p8(X) * (-0.2 * x + 1.1 * y - 0.5 * z + 0.6);
+  };
+  auto p10 = [&p9](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p9(X) * (0.7 * x - 0.6 * y - 0.7 * z - 0.2);
+  };
+  auto p11 = [&p10](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p10(X) * (-1.3 * x + 0.6 * y - 1.3 * z + 0.7);
+  };
+  auto p12 = [&p11](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p11(X) * (0.3 * x - 0.7 * y + 0.3 * z + 0.7);
+  };
+  auto p13 = [&p12](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p12(X) * (0.9 * x + 0.2 * y - 0.4 * z + 0.5);
+  };
+  auto p14 = [&p13](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p13(X) * (0.6 * x - 1.2 * y + 0.7 * z - 0.4);
+  };
+  auto p15 = [&p14](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p14(X) * (-0.2 * x - 0.7 * y + 0.9 * z + 0.8);
+  };
+  auto p16 = [&p15](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p15(X) * (0.7 * x + 0.2 * y - 0.6 * z + 0.4);
+  };
+  auto p17 = [&p16](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p16(X) * (-0.1 * x + 0.8 * y + 0.3 * z - 0.2);
+  };
+  auto p18 = [&p17](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p17(X) * (0.7 * x - 0.2 * y - 0.3 * z + 0.8);
+  };
+  auto p19 = [&p18](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p18(X) * (-0.7 * x + 1.2 * y + 1.3 * z + 0.8);
+  };
+  auto p20 = [&p19](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p19(X) * (0.7 * x - 1.2 * y + 0.3 * z - 0.6);
+  };
+  auto p21 = [&p20](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p20(X) * (0.7 * x - 1.2 * y + 0.3 * z - 0.6);
+  };
+
+  SECTION("degree 1")
+  {
+    const QuadratureFormula<3>& l1 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(1));
+
+    REQUIRE(l1.numberOfPoints() == 1);
+
+    REQUIRE(integrate(p0, l1) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l1) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l1) != Catch::Approx(-64. / 15));
+  }
+
+  SECTION("degree 2")
+  {
+    const QuadratureFormula<3>& l2 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(2));
+
+    REQUIRE(l2.numberOfPoints() == 5);
+
+    REQUIRE(integrate(p0, l2) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l2) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l2) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l2) != Catch::Approx(83. / 5));
+  }
+
+  SECTION("degree 3")
+  {
+    const QuadratureFormula<3>& l3 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(3));
+
+    REQUIRE(l3.numberOfPoints() == 6);
+
+    REQUIRE(integrate(p0, l3) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l3) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l3) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l3) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l3) != Catch::Approx(26809. / 3150));
+  }
+
+  SECTION("degree 4")
+  {
+    const QuadratureFormula<3>& l4 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(4));
+
+    REQUIRE(l4.numberOfPoints() == 10);
+
+    REQUIRE(integrate(p0, l4) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l4) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l4) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l4) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l4) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l4) != Catch::Approx(42881. / 63000));
+  }
+
+  SECTION("degree 5")
+  {
+    const QuadratureFormula<3>& l5 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(5));
+
+    REQUIRE(l5.numberOfPoints() == 15);
+
+    REQUIRE(integrate(p0, l5) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l5) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l5) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l5) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l5) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l5) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l5) != Catch::Approx(-59509. / 1290987));
+  }
+
+  SECTION("degree 6")
+  {
+    const QuadratureFormula<3>& l6 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(6));
+
+    REQUIRE(l6.numberOfPoints() == 23);
+
+    REQUIRE(integrate(p0, l6) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l6) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l6) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l6) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l6) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l6) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l6) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l6) != Catch::Approx(-79258447. / 64549350));
+  }
+
+  SECTION("degree 7")
+  {
+    const QuadratureFormula<3>& l7 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(7));
+
+    REQUIRE(l7.numberOfPoints() == 31);
+
+    REQUIRE(integrate(p0, l7) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l7) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l7) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l7) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l7) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l7) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l7) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l7) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l7) != Catch::Approx(-64936890181. / 56803428000));
+  }
+
+  SECTION("degree 8")
+  {
+    const QuadratureFormula<3>& l8 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(8));
+
+    REQUIRE(l8.numberOfPoints() == 47);
+
+    REQUIRE(integrate(p0, l8) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l8) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l8) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l8) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l8) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l8) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l8) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l8) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l8) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l8) != Catch::Approx(-46104457917. / 31557460000));
+  }
+
+  SECTION("degree 9")
+  {
+    const QuadratureFormula<3>& l9 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(9));
+
+    REQUIRE(l9.numberOfPoints() == 62);
+
+    REQUIRE(integrate(p0, l9) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l9) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l9) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l9) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l9) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l9) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l9) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l9) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l9) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l9) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l9) != Catch::Approx(14564160020837. / 73844456400000));
+  }
+
+  SECTION("degree 10")
+  {
+    const QuadratureFormula<3>& l10 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(10));
+
+    REQUIRE(l10.numberOfPoints() == 80);
+
+    REQUIRE(integrate(p0, l10) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l10) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l10) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l10) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l10) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l10) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l10) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l10) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l10) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l10) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l10) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l10) != Catch::Approx(70717900459291. / 1723037316000000));
+  }
+
+  SECTION("degree 11")
+  {
+    const QuadratureFormula<3>& l11 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(11));
+
+    REQUIRE(l11.numberOfPoints() == 103);
+
+    REQUIRE(integrate(p0, l11) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l11) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l11) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l11) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l11) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l11) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l11) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l11) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l11) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l11) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l11) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l11) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l11) != Catch::Approx(4088535221940569. / 129227798700000000));
+  }
+
+  SECTION("degree 12")
+  {
+    const QuadratureFormula<3>& l12 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(12));
+
+    REQUIRE(l12.numberOfPoints() == 127);
+
+    REQUIRE(integrate(p0, l12) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l12) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l12) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l12) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l12) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l12) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l12) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l12) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l12) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l12) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l12) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l12) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l12) == Catch::Approx(4088535221940569. / 129227798700000000));
+    REQUIRE(integrate(p13, l12) != Catch::Approx(4202215015498883. / 129227798700000000));
+  }
+
+  SECTION("degree 13")
+  {
+    const QuadratureFormula<3>& l13 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(13));
+
+    REQUIRE(l13.numberOfPoints() == 152);
+
+    REQUIRE(integrate(p0, l13) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l13) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l13) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l13) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l13) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l13) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l13) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l13) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l13) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l13) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l13) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l13) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l13) == Catch::Approx(4088535221940569. / 129227798700000000));
+    REQUIRE(integrate(p13, l13) == Catch::Approx(4202215015498883. / 129227798700000000));
+    REQUIRE(integrate(p14, l13) != Catch::Approx(-13139133580740403. / 4992892222500000000.));
+  }
+
+  SECTION("degree 14")
+  {
+    const QuadratureFormula<3>& l14 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(14));
+
+    REQUIRE(l14.numberOfPoints() == 184);
+
+    REQUIRE(integrate(p0, l14) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l14) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l14) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l14) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l14) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l14) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l14) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l14) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l14) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l14) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l14) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l14) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l14) == Catch::Approx(4088535221940569. / 129227798700000000));
+    REQUIRE(integrate(p13, l14) == Catch::Approx(4202215015498883. / 129227798700000000));
+    REQUIRE(integrate(p14, l14) == Catch::Approx(-13139133580740403. / 4992892222500000000.));
+    REQUIRE(integrate(p15, l14) != Catch::Approx(50695835504084747233. / 3295308866850000000000.));
+  }
+
+  SECTION("degree 15")
+  {
+    const QuadratureFormula<3>& l15 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(15));
+
+    REQUIRE(l15.numberOfPoints() == 234);
+
+    REQUIRE(integrate(p0, l15) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l15) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l15) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l15) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l15) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l15) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l15) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l15) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l15) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l15) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l15) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l15) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l15) == Catch::Approx(4088535221940569. / 129227798700000000));
+    REQUIRE(integrate(p13, l15) == Catch::Approx(4202215015498883. / 129227798700000000));
+    REQUIRE(integrate(p14, l15) == Catch::Approx(-13139133580740403. / 4992892222500000000.));
+    REQUIRE(integrate(p15, l15) == Catch::Approx(50695835504084747233. / 3295308866850000000000.));
+    REQUIRE(integrate(p16, l15) != Catch::Approx(7438848232461834482681. / 834811579602000000000000.));
+  }
+
+  SECTION("degree 16")
+  {
+    const QuadratureFormula<3>& l16 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(16));
+
+    REQUIRE(l16.numberOfPoints() == 285);
+
+    REQUIRE(integrate(p0, l16) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l16) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l16) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l16) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l16) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l16) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l16) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l16) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l16) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l16) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l16) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l16) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l16) == Catch::Approx(4088535221940569. / 129227798700000000));
+    REQUIRE(integrate(p13, l16) == Catch::Approx(4202215015498883. / 129227798700000000));
+    REQUIRE(integrate(p14, l16) == Catch::Approx(-13139133580740403. / 4992892222500000000.));
+    REQUIRE(integrate(p15, l16) == Catch::Approx(50695835504084747233. / 3295308866850000000000.));
+    REQUIRE(integrate(p16, l16) == Catch::Approx(7438848232461834482681. / 834811579602000000000000.));
+    REQUIRE(integrate(p17, l16) != Catch::Approx(-49370451351776632471. / 4384514598750000000000.));
+  }
+
+  SECTION("degree 17")
+  {
+    const QuadratureFormula<3>& l17 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(17));
+
+    REQUIRE(l17.numberOfPoints() == 319);
+
+    REQUIRE(integrate(p0, l17) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l17) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l17) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l17) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l17) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l17) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l17) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l17) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l17) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l17) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l17) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l17) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l17) == Catch::Approx(4088535221940569. / 129227798700000000));
+    REQUIRE(integrate(p13, l17) == Catch::Approx(4202215015498883. / 129227798700000000));
+    REQUIRE(integrate(p14, l17) == Catch::Approx(-13139133580740403. / 4992892222500000000.));
+    REQUIRE(integrate(p15, l17) == Catch::Approx(50695835504084747233. / 3295308866850000000000.));
+    REQUIRE(integrate(p16, l17) == Catch::Approx(7438848232461834482681. / 834811579602000000000000.));
+    REQUIRE(integrate(p17, l17) == Catch::Approx(-49370451351776632471. / 4384514598750000000000.));
+    REQUIRE(integrate(p18, l17) != Catch::Approx(-3041981344499113218848083. / 194789368573800000000000000.));
+  }
+
+  SECTION("degree 18")
+  {
+    const QuadratureFormula<3>& l18 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(18));
+
+    REQUIRE(l18.numberOfPoints() == 357);
+
+    REQUIRE(integrate(p0, l18) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l18) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l18) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l18) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l18) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l18) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l18) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l18) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l18) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l18) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l18) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l18) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l18) == Catch::Approx(4088535221940569. / 129227798700000000));
+    REQUIRE(integrate(p13, l18) == Catch::Approx(4202215015498883. / 129227798700000000));
+    REQUIRE(integrate(p14, l18) == Catch::Approx(-13139133580740403. / 4992892222500000000.));
+    REQUIRE(integrate(p15, l18) == Catch::Approx(50695835504084747233. / 3295308866850000000000.));
+    REQUIRE(integrate(p16, l18) == Catch::Approx(7438848232461834482681. / 834811579602000000000000.));
+    REQUIRE(integrate(p17, l18) == Catch::Approx(-49370451351776632471. / 4384514598750000000000.));
+    REQUIRE(integrate(p18, l18) == Catch::Approx(-3041981344499113218848083. / 194789368573800000000000000.));
+    REQUIRE(integrate(p19, l18) != Catch::Approx(6741839335620301740899793. / 892784605963250000000000000.));
+  }
+
+  SECTION("degree 19")
+  {
+    const QuadratureFormula<3>& l19 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(19));
+
+    REQUIRE(l19.numberOfPoints() == 418);
+
+    REQUIRE(integrate(p0, l19) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l19) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l19) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l19) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l19) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l19) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l19) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l19) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l19) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l19) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l19) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l19) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l19) == Catch::Approx(4088535221940569. / 129227798700000000));
+    REQUIRE(integrate(p13, l19) == Catch::Approx(4202215015498883. / 129227798700000000));
+    REQUIRE(integrate(p14, l19) == Catch::Approx(-13139133580740403. / 4992892222500000000.));
+    REQUIRE(integrate(p15, l19) == Catch::Approx(50695835504084747233. / 3295308866850000000000.));
+    REQUIRE(integrate(p16, l19) == Catch::Approx(7438848232461834482681. / 834811579602000000000000.));
+    REQUIRE(integrate(p17, l19) == Catch::Approx(-49370451351776632471. / 4384514598750000000000.));
+    REQUIRE(integrate(p18, l19) == Catch::Approx(-3041981344499113218848083. / 194789368573800000000000000.));
+    REQUIRE(integrate(p19, l19) == Catch::Approx(6741839335620301740899793. / 892784605963250000000000000.));
+    REQUIRE(integrate(p20, l19) != Catch::Approx(50574805739660969727328017511. / 4928171024917140000000000000000.));
+  }
+
+  SECTION("degree 20")
+  {
+    const QuadratureFormula<3>& l20 = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(20));
+
+    REQUIRE(l20.numberOfPoints() == 489);
+
+    REQUIRE(integrate(p0, l20) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l20) == Catch::Approx(-1));
+    REQUIRE(integrate(p2, l20) == Catch::Approx(-64. / 15));
+    REQUIRE(integrate(p3, l20) == Catch::Approx(83. / 5));
+    REQUIRE(integrate(p4, l20) == Catch::Approx(26809. / 3150));
+    REQUIRE(integrate(p5, l20) == Catch::Approx(42881. / 63000));
+    REQUIRE(integrate(p6, l20) == Catch::Approx(-59509. / 1290987));
+    REQUIRE(integrate(p7, l20) == Catch::Approx(-79258447. / 64549350));
+    REQUIRE(integrate(p8, l20) == Catch::Approx(-64936890181. / 56803428000));
+    REQUIRE(integrate(p9, l20) == Catch::Approx(-46104457917. / 31557460000));
+    REQUIRE(integrate(p10, l20) == Catch::Approx(14564160020837. / 73844456400000));
+    REQUIRE(integrate(p11, l20) == Catch::Approx(70717900459291. / 1723037316000000));
+    REQUIRE(integrate(p12, l20) == Catch::Approx(4088535221940569. / 129227798700000000));
+    REQUIRE(integrate(p13, l20) == Catch::Approx(4202215015498883. / 129227798700000000));
+    REQUIRE(integrate(p14, l20) == Catch::Approx(-13139133580740403. / 4992892222500000000.));
+    REQUIRE(integrate(p15, l20) == Catch::Approx(50695835504084747233. / 3295308866850000000000.));
+    REQUIRE(integrate(p16, l20) == Catch::Approx(7438848232461834482681. / 834811579602000000000000.));
+    REQUIRE(integrate(p17, l20) == Catch::Approx(-49370451351776632471. / 4384514598750000000000.));
+    REQUIRE(integrate(p18, l20) == Catch::Approx(-3041981344499113218848083. / 194789368573800000000000000.));
+    REQUIRE(integrate(p19, l20) == Catch::Approx(6741839335620301740899793. / 892784605963250000000000000.));
+    REQUIRE(integrate(p20, l20) == Catch::Approx(50574805739660969727328017511. / 4928171024917140000000000000000.));
+    REQUIRE(integrate(p21, l20) != Catch::Approx(796248143552124247176376796357. / 110883848060635650000000000000000.));
+  }
+
+  SECTION("max implemented degree")
+  {
+    REQUIRE(QuadratureManager::instance().maxPyramidDegree(QuadratureType::Gauss) ==
+            PyramidGaussQuadrature::max_degree);
+    REQUIRE_THROWS_WITH(QuadratureManager::instance().getPyramidFormula(
+                          GaussQuadratureDescriptor(PyramidGaussQuadrature ::max_degree + 1)),
+                        "error: Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(PyramidGaussQuadrature ::max_degree) + " on pyramids");
+  }
+}
diff --git a/tests/test_PyramidTransformation.cpp b/tests/test_PyramidTransformation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8e1a41889036674e8afcd2e6c12be4cec4157825
--- /dev/null
+++ b/tests/test_PyramidTransformation.cpp
@@ -0,0 +1,98 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/CubeTransformation.hpp>
+#include <geometry/PyramidTransformation.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("PyramidTransformation", "[geometry]")
+{
+  using R3 = TinyVector<3>;
+
+  const R3 a_hat{-1, -1, +0};
+  const R3 b_hat{+1, -1, +0};
+  const R3 c_hat{+1, +1, +0};
+  const R3 d_hat{-1, +1, +0};
+  const R3 e_hat{+0, +0, +1};
+
+  const R3 m_hat{0, 0, 1. / 5};
+
+  const R3 a{1, 2, 0};
+  const R3 b{3, 1, 3};
+  const R3 c{2, 5, 2};
+  const R3 d{0, 3, 1};
+  const R3 e{1, 2, 5};
+
+  const PyramidTransformation t(a, b, c, d, e);
+
+  SECTION("points")
+  {
+    REQUIRE(l2Norm(t(a_hat) - a) == Catch::Approx(0));
+    REQUIRE(l2Norm(t(b_hat) - b) == Catch::Approx(0));
+    REQUIRE(l2Norm(t(c_hat) - c) == Catch::Approx(0));
+    REQUIRE(l2Norm(t(d_hat) - d) == Catch::Approx(0));
+    REQUIRE(l2Norm(t(e_hat) - e) == Catch::Approx(0));
+
+    R3 m = (1. / 5) * (a + b + c + d + e);
+    REQUIRE(l2Norm(t(m_hat) - m) == Catch::Approx(0).margin(1E-14));
+  }
+
+  SECTION("Jacobian determinant")
+  {
+    SECTION("at points")
+    {
+      auto detJ = [](const R3 X) {
+        const double& x = X[0];
+        const double& y = X[1];
+
+        return (43 * x + 13 * y + 93) / 16;
+      };
+
+      REQUIRE(t.jacobianDeterminant(a_hat) == Catch::Approx(detJ(a_hat)));
+      REQUIRE(t.jacobianDeterminant(b_hat) == Catch::Approx(detJ(b_hat)));
+      REQUIRE(t.jacobianDeterminant(c_hat) == Catch::Approx(detJ(c_hat)));
+      REQUIRE(t.jacobianDeterminant(d_hat) == Catch::Approx(detJ(d_hat)));
+      REQUIRE(t.jacobianDeterminant(e_hat) == Catch::Approx(detJ(e_hat)));
+
+      REQUIRE(t.jacobianDeterminant(m_hat) == Catch::Approx(detJ(m_hat)));
+    }
+
+    SECTION("volume calculation")
+    {
+      // The jacobian determinant is a degree 1 polynomial
+      const QuadratureFormula<3>& gauss = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(1));
+
+      double volume = 0;
+      for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+        volume += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i));
+      }
+
+      // 31 / 4 is actually the volume of the pyramid
+      REQUIRE(volume == Catch::Approx(31. / 4));
+    }
+
+    SECTION("exact polynomial integration")
+    {
+      auto p = [](const R3& X) {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+
+        return 3 * x * x + 2 * y * y + 3 * z * z + 4 * x + 3 * y + 2 * z + 1;
+      };
+
+      // 4 is the minimum quadrature rule to integrate the polynomial on the pyramid
+      const QuadratureFormula<3>& gauss = QuadratureManager::instance().getPyramidFormula(GaussQuadratureDescriptor(4));
+      double integral                   = 0;
+      for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+        integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+      }
+
+      REQUIRE(integral == Catch::Approx(213095. / 448));
+    }
+  }
+}
diff --git a/tests/test_QuadratureType.cpp b/tests/test_QuadratureType.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..89519aa97562e6b73647f826930ffc92add1ad76
--- /dev/null
+++ b/tests/test_QuadratureType.cpp
@@ -0,0 +1,16 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/QuadratureType.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("QuadratureType", "[analysis]")
+{
+  SECTION("name")
+  {
+    REQUIRE(name(QuadratureType::Gauss) == "Gauss");
+    REQUIRE(name(QuadratureType::GaussLegendre) == "Gauss-Legendre");
+    REQUIRE(name(QuadratureType::GaussLobatto) == "Gauss-Lobatto");
+  }
+}
diff --git a/tests/test_RefId.cpp b/tests/test_RefId.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f14f42826ff1a4234136f72b2332b8734f272ba0
--- /dev/null
+++ b/tests/test_RefId.cpp
@@ -0,0 +1,69 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <mesh/RefId.hpp>
+
+#include <sstream>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("RefId", "[mesh]")
+{
+  SECTION("tag with name")
+  {
+    RefId ref_id0(3, "tag_name");
+    REQUIRE(ref_id0.tagNumber() == 3);
+    REQUIRE(ref_id0.tagName() == "tag_name");
+
+    RefId ref_id1(ref_id0);
+    REQUIRE(ref_id1.tagNumber() == 3);
+    REQUIRE(ref_id1.tagName() == "tag_name");
+    REQUIRE(ref_id0 == ref_id1);
+
+    RefId ref_id2(2, "another_tag_name");
+    REQUIRE(ref_id2 < ref_id1);
+
+    RefId ref_id3;
+    ref_id3 = ref_id0;
+    REQUIRE(ref_id3 == ref_id0);
+
+    RefId ref_id4 = std::move(ref_id3);
+    REQUIRE(ref_id4 == ref_id0);
+
+    RefId ref_id5(std::move(ref_id4));
+    REQUIRE(ref_id5 == ref_id0);
+
+    std::stringstream os;
+    os << ref_id0;
+    REQUIRE(os.str() == "tag_name(3)");
+  }
+
+  SECTION("tag with no name")
+  {
+    RefId ref_id0(3);
+    REQUIRE(ref_id0.tagNumber() == 3);
+    REQUIRE(ref_id0.tagName() == "3");
+
+    RefId ref_id1(ref_id0);
+    REQUIRE(ref_id1.tagNumber() == 3);
+    REQUIRE(ref_id1.tagName() == "3");
+    REQUIRE(ref_id0 == ref_id1);
+
+    RefId ref_id2(2);
+    REQUIRE(ref_id2 < ref_id1);
+
+    RefId ref_id3;
+    ref_id3 = ref_id0;
+    REQUIRE(ref_id3 == ref_id0);
+
+    RefId ref_id4 = std::move(ref_id3);
+    REQUIRE(ref_id4 == ref_id0);
+
+    RefId ref_id5(std::move(ref_id4));
+    REQUIRE(ref_id5 == ref_id0);
+
+    std::stringstream os;
+    os << ref_id0;
+    REQUIRE(os.str() == "3");
+  }
+}
diff --git a/tests/test_RefItemList.cpp b/tests/test_RefItemList.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4a9a7b7b2dc3a7aad46fd29703769d4550fc67dd
--- /dev/null
+++ b/tests/test_RefItemList.cpp
@@ -0,0 +1,137 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <mesh/RefItemList.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("RefItemList", "[mesh]")
+{
+  SECTION("nodes")
+  {
+    const Array<NodeId> node_id_array = convert_to_array(std::vector<NodeId>{1, 3, 7, 2, 4, 11});
+    const RefId ref_id{3, "my_reference"};
+
+    RefItemList<ItemType::node> ref_node_list{ref_id, node_id_array};
+    REQUIRE(ref_node_list.refId() == ref_id);
+    REQUIRE(ref_node_list.list().size() == node_id_array.size());
+    REQUIRE(&(ref_node_list.list()[0]) == &(node_id_array[0]));
+
+    {
+      RefItemList copy_ref_node_list{ref_node_list};
+      REQUIRE(copy_ref_node_list.refId() == ref_id);
+      REQUIRE(copy_ref_node_list.list().size() == node_id_array.size());
+      REQUIRE(&(copy_ref_node_list.list()[0]) == &(node_id_array[0]));
+    }
+
+    {
+      RefItemList<ItemType::node> affect_ref_node_list;
+      affect_ref_node_list = ref_node_list;
+      REQUIRE(affect_ref_node_list.refId() == ref_id);
+      REQUIRE(affect_ref_node_list.list().size() == node_id_array.size());
+      REQUIRE(&(affect_ref_node_list.list()[0]) == &(node_id_array[0]));
+
+      RefItemList<ItemType::node> move_ref_node_list;
+      move_ref_node_list = std::move(affect_ref_node_list);
+      REQUIRE(move_ref_node_list.refId() == ref_id);
+      REQUIRE(move_ref_node_list.list().size() == node_id_array.size());
+      REQUIRE(&(move_ref_node_list.list()[0]) == &(node_id_array[0]));
+    }
+  }
+
+  SECTION("edges")
+  {
+    const Array<EdgeId> edge_id_array = convert_to_array(std::vector<EdgeId>{1, 3, 7, 2, 4, 11});
+    const RefId ref_id{3, "my_reference"};
+
+    RefItemList<ItemType::edge> ref_edge_list{ref_id, edge_id_array};
+    REQUIRE(ref_edge_list.refId() == ref_id);
+    REQUIRE(ref_edge_list.list().size() == edge_id_array.size());
+    REQUIRE(&(ref_edge_list.list()[0]) == &(edge_id_array[0]));
+
+    {
+      RefItemList copy_ref_edge_list{ref_edge_list};
+      REQUIRE(copy_ref_edge_list.refId() == ref_id);
+      REQUIRE(copy_ref_edge_list.list().size() == edge_id_array.size());
+      REQUIRE(&(copy_ref_edge_list.list()[0]) == &(edge_id_array[0]));
+    }
+
+    {
+      RefItemList<ItemType::edge> affect_ref_edge_list;
+      affect_ref_edge_list = ref_edge_list;
+      REQUIRE(affect_ref_edge_list.refId() == ref_id);
+      REQUIRE(affect_ref_edge_list.list().size() == edge_id_array.size());
+      REQUIRE(&(affect_ref_edge_list.list()[0]) == &(edge_id_array[0]));
+
+      RefItemList<ItemType::edge> move_ref_edge_list;
+      move_ref_edge_list = std::move(affect_ref_edge_list);
+      REQUIRE(move_ref_edge_list.refId() == ref_id);
+      REQUIRE(move_ref_edge_list.list().size() == edge_id_array.size());
+      REQUIRE(&(move_ref_edge_list.list()[0]) == &(edge_id_array[0]));
+    }
+  }
+
+  SECTION("faces")
+  {
+    const Array<FaceId> face_id_array = convert_to_array(std::vector<FaceId>{1, 3, 7, 2, 4, 11});
+    const RefId ref_id{3, "my_reference"};
+
+    RefItemList<ItemType::face> ref_face_list{ref_id, face_id_array};
+    REQUIRE(ref_face_list.refId() == ref_id);
+    REQUIRE(ref_face_list.list().size() == face_id_array.size());
+    REQUIRE(&(ref_face_list.list()[0]) == &(face_id_array[0]));
+
+    {
+      RefItemList copy_ref_face_list{ref_face_list};
+      REQUIRE(copy_ref_face_list.refId() == ref_id);
+      REQUIRE(copy_ref_face_list.list().size() == face_id_array.size());
+      REQUIRE(&(copy_ref_face_list.list()[0]) == &(face_id_array[0]));
+    }
+
+    {
+      RefItemList<ItemType::face> affect_ref_face_list;
+      affect_ref_face_list = ref_face_list;
+      REQUIRE(affect_ref_face_list.refId() == ref_id);
+      REQUIRE(affect_ref_face_list.list().size() == face_id_array.size());
+      REQUIRE(&(affect_ref_face_list.list()[0]) == &(face_id_array[0]));
+
+      RefItemList<ItemType::face> move_ref_face_list;
+      move_ref_face_list = std::move(affect_ref_face_list);
+      REQUIRE(move_ref_face_list.refId() == ref_id);
+      REQUIRE(move_ref_face_list.list().size() == face_id_array.size());
+      REQUIRE(&(move_ref_face_list.list()[0]) == &(face_id_array[0]));
+    }
+  }
+
+  SECTION("cells")
+  {
+    const Array<CellId> cell_id_array = convert_to_array(std::vector<CellId>{1, 3, 7, 2, 4, 11});
+    const RefId ref_id{3, "my_reference"};
+
+    RefItemList<ItemType::cell> ref_cell_list{ref_id, cell_id_array};
+    REQUIRE(ref_cell_list.refId() == ref_id);
+    REQUIRE(ref_cell_list.list().size() == cell_id_array.size());
+    REQUIRE(&(ref_cell_list.list()[0]) == &(cell_id_array[0]));
+
+    {
+      RefItemList copy_ref_cell_list{ref_cell_list};
+      REQUIRE(copy_ref_cell_list.refId() == ref_id);
+      REQUIRE(copy_ref_cell_list.list().size() == cell_id_array.size());
+      REQUIRE(&(copy_ref_cell_list.list()[0]) == &(cell_id_array[0]));
+    }
+
+    {
+      RefItemList<ItemType::cell> affect_ref_cell_list;
+      affect_ref_cell_list = ref_cell_list;
+      REQUIRE(affect_ref_cell_list.refId() == ref_id);
+      REQUIRE(affect_ref_cell_list.list().size() == cell_id_array.size());
+      REQUIRE(&(affect_ref_cell_list.list()[0]) == &(cell_id_array[0]));
+
+      RefItemList<ItemType::cell> move_ref_cell_list;
+      move_ref_cell_list = std::move(affect_ref_cell_list);
+      REQUIRE(move_ref_cell_list.refId() == ref_id);
+      REQUIRE(move_ref_cell_list.list().size() == cell_id_array.size());
+      REQUIRE(&(move_ref_cell_list.list()[0]) == &(cell_id_array[0]));
+    }
+  }
+}
diff --git a/tests/test_SmallArray.cpp b/tests/test_SmallArray.cpp
index 9b5d482791f9df0a19c41c64cb55070c5bf8be24..2565ac0d7eb82beb828076840fda74d9b3ffbfa5 100644
--- a/tests/test_SmallArray.cpp
+++ b/tests/test_SmallArray.cpp
@@ -1,6 +1,8 @@
 #include <catch2/catch_test_macros.hpp>
 #include <catch2/matchers/catch_matchers_all.hpp>
 
+#include <algebra/TinyMatrix.hpp>
+#include <algebra/TinyVector.hpp>
 #include <utils/PugsAssert.hpp>
 #include <utils/SmallArray.hpp>
 #include <utils/Types.hpp>
@@ -281,5 +283,71 @@ TEST_CASE("SmallArray", "[utils]")
       REQUIRE(array[i] == std::numeric_limits<int>::max() / 2);
     }
   }
+
+  SECTION("checking for SmallArray reductions")
+  {
+    SmallArray<int> a(10);
+    a[0] = 13;
+    a[1] = 1;
+    a[2] = 8;
+    a[3] = -3;
+    a[4] = 23;
+    a[5] = -1;
+    a[6] = 13;
+    a[7] = 0;
+    a[8] = 12;
+    a[9] = 9;
+
+    SECTION("Min")
+    {
+      REQUIRE(min(a) == -3);
+    }
+
+    SECTION("Max")
+    {
+      REQUIRE(max(a) == 23);
+    }
+
+    SECTION("Sum")
+    {
+      REQUIRE((sum(a) == 75));
+    }
+
+    SECTION("TinyVector Sum")
+    {
+      using N2 = TinyVector<2, int>;
+      SmallArray<N2> b(10);
+      b[0] = N2{13, 2};
+      b[1] = N2{1, 3};
+      b[2] = N2{8, -2};
+      b[3] = N2{-3, 2};
+      b[4] = N2{23, 4};
+      b[5] = N2{-1, -3};
+      b[6] = N2{13, 17};
+      b[7] = N2{0, 9};
+      b[8] = N2{12, 13};
+      b[9] = N2{9, -17};
+
+      REQUIRE((sum(b) == N2{75, 28}));
+    }
+
+    SECTION("TinyMatrix Sum")
+    {
+      using N22 = TinyMatrix<2, 2, int>;
+      SmallArray<N22> b(10);
+      b[0] = N22{13, 2, 0, 1};
+      b[1] = N22{1, 3, 6, 3};
+      b[2] = N22{8, -2, -1, 21};
+      b[3] = N22{-3, 2, 5, 12};
+      b[4] = N22{23, 4, 7, 1};
+      b[5] = N22{-1, -3, 33, 11};
+      b[6] = N22{13, 17, 12, 13};
+      b[7] = N22{0, 9, 1, 14};
+      b[8] = N22{12, 13, -3, -71};
+      b[9] = N22{9, -17, 0, 16};
+
+      REQUIRE((sum(b) == N22{75, 28, 60, 21}));
+    }
+  }
 #endif   // NDEBUG
 }
diff --git a/tests/test_SmallVector.cpp b/tests/test_SmallVector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3b499c3ab3e984247b7f20f96b626f8ee413763a
--- /dev/null
+++ b/tests/test_SmallVector.cpp
@@ -0,0 +1,472 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <utils/PugsAssert.hpp>
+
+#include <algebra/SmallVector.hpp>
+#include <algebra/TinyVector.hpp>
+
+// Instantiate to ensure full coverage is performed
+template class SmallVector<int>;
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("SmallVector", "[algebra]")
+{
+  SECTION("size")
+  {
+    SmallVector<int> x{5};
+    REQUIRE(x.size() == 5);
+  }
+
+  SECTION("write access")
+  {
+    SmallVector<int> x{5};
+    x[0] = 0;
+    x[1] = 1;
+    x[2] = 2;
+    x[3] = 3;
+    x[4] = 4;
+
+    REQUIRE(x[0] == 0);
+    REQUIRE(x[1] == 1);
+    REQUIRE(x[2] == 2);
+    REQUIRE(x[3] == 3);
+    REQUIRE(x[4] == 4);
+  }
+
+  SECTION("fill")
+  {
+    SmallVector<int> x{5};
+    x.fill(2);
+
+    REQUIRE(x[0] == 2);
+    REQUIRE(x[1] == 2);
+    REQUIRE(x[2] == 2);
+    REQUIRE(x[3] == 2);
+    REQUIRE(x[4] == 2);
+  }
+
+  SECTION("copy constructor (shallow)")
+  {
+    SmallVector<int> x{5};
+    x[0] = 0;
+    x[1] = 1;
+    x[2] = 2;
+    x[3] = 3;
+    x[4] = 4;
+
+    const SmallVector<int> y = x;
+    REQUIRE(y[0] == 0);
+    REQUIRE(y[1] == 1);
+    REQUIRE(y[2] == 2);
+    REQUIRE(y[3] == 3);
+    REQUIRE(y[4] == 4);
+  }
+
+  SECTION("copy constructor (move)")
+  {
+    SmallVector<int> x{5};
+    x[0] = 0;
+    x[1] = 1;
+    x[2] = 2;
+    x[3] = 3;
+    x[4] = 4;
+
+    const SmallVector<int> y = std::move(x);
+    REQUIRE(y[0] == 0);
+    REQUIRE(y[1] == 1);
+    REQUIRE(y[2] == 2);
+    REQUIRE(y[3] == 3);
+    REQUIRE(y[4] == 4);
+  }
+
+  SECTION("copy (shallow)")
+  {
+    SmallVector<int> x{5};
+    x[0] = 0;
+    x[1] = 1;
+    x[2] = 2;
+    x[3] = 3;
+    x[4] = 4;
+
+    SmallVector<int> y{5};
+    y = x;
+    REQUIRE(y[0] == 0);
+    REQUIRE(y[1] == 1);
+    REQUIRE(y[2] == 2);
+    REQUIRE(y[3] == 3);
+    REQUIRE(y[4] == 4);
+
+    x[0] = 14;
+    x[1] = 13;
+    x[2] = 12;
+    x[3] = 11;
+    x[4] = 10;
+
+    REQUIRE(x[0] == 14);
+    REQUIRE(x[1] == 13);
+    REQUIRE(x[2] == 12);
+    REQUIRE(x[3] == 11);
+    REQUIRE(x[4] == 10);
+
+    REQUIRE(y[0] == 14);
+    REQUIRE(y[1] == 13);
+    REQUIRE(y[2] == 12);
+    REQUIRE(y[3] == 11);
+    REQUIRE(y[4] == 10);
+  }
+
+  SECTION("copy (deep)")
+  {
+    SmallVector<int> x{5};
+    x[0] = 0;
+    x[1] = 1;
+    x[2] = 2;
+    x[3] = 3;
+    x[4] = 4;
+
+    SmallVector<int> y{5};
+    y = copy(x);
+
+    x[0] = 14;
+    x[1] = 13;
+    x[2] = 12;
+    x[3] = 11;
+    x[4] = 10;
+
+    REQUIRE(y[0] == 0);
+    REQUIRE(y[1] == 1);
+    REQUIRE(y[2] == 2);
+    REQUIRE(y[3] == 3);
+    REQUIRE(y[4] == 4);
+
+    REQUIRE(x[0] == 14);
+    REQUIRE(x[1] == 13);
+    REQUIRE(x[2] == 12);
+    REQUIRE(x[3] == 11);
+    REQUIRE(x[4] == 10);
+  }
+
+  SECTION("copy to const (shallow)")
+  {
+    SmallVector<int> x{5};
+    x[0] = 0;
+    x[1] = 1;
+    x[2] = 2;
+    x[3] = 3;
+    x[4] = 4;
+
+    SmallVector<const int> y;
+    y = x;
+    REQUIRE(y[0] == 0);
+    REQUIRE(y[1] == 1);
+    REQUIRE(y[2] == 2);
+    REQUIRE(y[3] == 3);
+    REQUIRE(y[4] == 4);
+
+    SmallVector<int> z{5};
+    z[0] = 14;
+    z[1] = 13;
+    z[2] = 12;
+    z[3] = 11;
+    z[4] = 10;
+
+    y = z;
+    REQUIRE(z[0] == 14);
+    REQUIRE(z[1] == 13);
+    REQUIRE(z[2] == 12);
+    REQUIRE(z[3] == 11);
+    REQUIRE(z[4] == 10);
+
+    REQUIRE(y[0] == 14);
+    REQUIRE(y[1] == 13);
+    REQUIRE(y[2] == 12);
+    REQUIRE(y[3] == 11);
+    REQUIRE(y[4] == 10);
+  }
+
+  SECTION("self scalar multiplication")
+  {
+    SmallVector<int> x{5};
+    x[0] = 0;
+    x[1] = 1;
+    x[2] = 2;
+    x[3] = 3;
+    x[4] = 4;
+
+    x *= 2;
+
+    REQUIRE(x[0] == 0);
+    REQUIRE(x[1] == 2);
+    REQUIRE(x[2] == 4);
+    REQUIRE(x[3] == 6);
+    REQUIRE(x[4] == 8);
+  }
+
+  SECTION("left scalar multiplication")
+  {
+    SmallVector<int> x{5};
+    x[0] = 0;
+    x[1] = 1;
+    x[2] = 2;
+    x[3] = 3;
+    x[4] = 4;
+
+    const SmallVector<int> y = 2 * x;
+
+    REQUIRE(y[0] == 0);
+    REQUIRE(y[1] == 2);
+    REQUIRE(y[2] == 4);
+    REQUIRE(y[3] == 6);
+    REQUIRE(y[4] == 8);
+  }
+
+  SECTION("dot product")
+  {
+    SmallVector<int> x{5};
+    x[0] = 0;
+    x[1] = 1;
+    x[2] = 2;
+    x[3] = 3;
+    x[4] = 4;
+
+    SmallVector<int> y{5};
+    y[0] = 7;
+    y[1] = 3;
+    y[2] = 4;
+    y[3] = 2;
+    y[4] = 8;
+
+    const int s = dot(x, y);
+    REQUIRE(s == 49);
+  }
+
+  SECTION("self scalar division")
+  {
+    SmallVector<int> x{5};
+    x[0] = 2;
+    x[1] = 3;
+    x[2] = 5;
+    x[3] = 7;
+    x[4] = 8;
+
+    x /= 2;
+
+    REQUIRE(x[0] == 1);
+    REQUIRE(x[1] == 1);
+    REQUIRE(x[2] == 2);
+    REQUIRE(x[3] == 3);
+    REQUIRE(x[4] == 4);
+  }
+
+  SECTION("self minus")
+  {
+    SmallVector<int> x{5};
+    x[0] = 2;
+    x[1] = 3;
+    x[2] = 5;
+    x[3] = 7;
+    x[4] = 8;
+
+    SmallVector<int> y{5};
+    y[0] = 3;
+    y[1] = 8;
+    y[2] = 6;
+    y[3] = 2;
+    y[4] = 4;
+
+    x -= y;
+
+    REQUIRE(x[0] == -1);
+    REQUIRE(x[1] == -5);
+    REQUIRE(x[2] == -1);
+    REQUIRE(x[3] == 5);
+    REQUIRE(x[4] == 4);
+  }
+
+  SECTION("self sum")
+  {
+    SmallVector<int> x{5};
+    x[0] = 2;
+    x[1] = 3;
+    x[2] = 5;
+    x[3] = 7;
+    x[4] = 8;
+
+    SmallVector<int> y{5};
+    y[0] = 3;
+    y[1] = 8;
+    y[2] = 6;
+    y[3] = 2;
+    y[4] = 4;
+
+    x += y;
+
+    REQUIRE(x[0] == 5);
+    REQUIRE(x[1] == 11);
+    REQUIRE(x[2] == 11);
+    REQUIRE(x[3] == 9);
+    REQUIRE(x[4] == 12);
+  }
+
+  SECTION("sum")
+  {
+    SmallVector<int> x{5};
+    x[0] = 2;
+    x[1] = 3;
+    x[2] = 5;
+    x[3] = 7;
+    x[4] = 8;
+
+    SmallVector<int> y{5};
+    y[0] = 3;
+    y[1] = 8;
+    y[2] = 6;
+    y[3] = 2;
+    y[4] = 4;
+
+    SmallVector z = x + y;
+
+    REQUIRE(z[0] == 5);
+    REQUIRE(z[1] == 11);
+    REQUIRE(z[2] == 11);
+    REQUIRE(z[3] == 9);
+    REQUIRE(z[4] == 12);
+  }
+
+  SECTION("difference")
+  {
+    SmallVector<int> x{5};
+    x[0] = 2;
+    x[1] = 3;
+    x[2] = 5;
+    x[3] = 7;
+    x[4] = 8;
+
+    SmallVector<int> y{5};
+    y[0] = 3;
+    y[1] = 8;
+    y[2] = 6;
+    y[3] = 2;
+    y[4] = 4;
+
+    SmallVector z = x - y;
+
+    REQUIRE(z[0] == -1);
+    REQUIRE(z[1] == -5);
+    REQUIRE(z[2] == -1);
+    REQUIRE(z[3] == 5);
+    REQUIRE(z[4] == 4);
+  }
+
+  SECTION("output")
+  {
+    SmallVector<int> x{5};
+    x[0] = 3;
+    x[1] = 7;
+    x[2] = 2;
+    x[3] = 1;
+    x[4] = -4;
+
+    std::ostringstream vector_ost;
+    vector_ost << x;
+    std::ostringstream ref_ost;
+    ref_ost << 0 << ':' << x[0];
+    for (size_t i = 1; i < x.size(); ++i) {
+      ref_ost << ' ' << i << ':' << x[i];
+    }
+    REQUIRE(vector_ost.str() == ref_ost.str());
+  }
+
+  SECTION("SmallVector from TinyVector")
+  {
+    TinyVector<5> tiny_vector{1, 3, 5, 7, 9};
+
+    SmallVector vector{tiny_vector};
+
+    REQUIRE(vector[0] == 1);
+    REQUIRE(vector[1] == 3);
+    REQUIRE(vector[2] == 5);
+    REQUIRE(vector[3] == 7);
+    REQUIRE(vector[4] == 9);
+
+    SECTION("ensures deep copy")
+    {
+      tiny_vector = zero;
+
+      REQUIRE(tiny_vector[0] == 0);
+      REQUIRE(tiny_vector[1] == 0);
+      REQUIRE(tiny_vector[2] == 0);
+      REQUIRE(tiny_vector[3] == 0);
+      REQUIRE(tiny_vector[4] == 0);
+
+      REQUIRE(vector[0] == 1);
+      REQUIRE(vector[1] == 3);
+      REQUIRE(vector[2] == 5);
+      REQUIRE(vector[3] == 7);
+      REQUIRE(vector[4] == 9);
+    }
+  }
+
+#ifndef NDEBUG
+
+  SECTION("output with signaling NaN")
+  {
+    SmallVector<double> x{5};
+    x[0] = 3;
+    x[2] = 2;
+
+    std::ostringstream vector_ost;
+    vector_ost << x;
+    std::ostringstream ref_ost;
+    ref_ost << 0 << ':' << 3 << ' ';
+    ref_ost << 1 << ":nan ";
+    ref_ost << 2 << ':' << 2 << ' ';
+    ref_ost << 3 << ":nan ";
+    ref_ost << 4 << ":nan";
+    REQUIRE(vector_ost.str() == ref_ost.str());
+  }
+
+  SECTION("invalid dot product")
+  {
+    SmallVector<int> x{5};
+    SmallVector<int> y{4};
+
+    REQUIRE_THROWS_WITH(dot(x, y), "cannot compute dot product: incompatible vector sizes");
+  }
+
+  SECTION("invalid substract")
+  {
+    SmallVector<int> x{5};
+    SmallVector<int> y{4};
+
+    REQUIRE_THROWS_WITH(x -= y, "cannot substract vector: incompatible sizes");
+  }
+
+  SECTION("invalid add")
+  {
+    SmallVector<int> x{5};
+    SmallVector<int> y{4};
+
+    REQUIRE_THROWS_WITH(x += y, "cannot add vector: incompatible sizes");
+  }
+
+  SECTION("invalid difference")
+  {
+    SmallVector<int> x{5};
+    SmallVector<int> y{4};
+
+    REQUIRE_THROWS_WITH(x - y, "cannot compute vector difference: incompatible sizes");
+  }
+
+  SECTION("invalid sum")
+  {
+    SmallVector<int> x{5};
+    SmallVector<int> y{4};
+
+    REQUIRE_THROWS_WITH(x + y, "cannot compute vector sum: incompatible sizes");
+  }
+
+#endif   // NDEBUG
+}
diff --git a/tests/test_Socket.cpp b/tests/test_Socket.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3293f6b8d7fe18db31a0db1aaac0ae30474d8808
--- /dev/null
+++ b/tests/test_Socket.cpp
@@ -0,0 +1,116 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <utils/PugsAssert.hpp>
+
+#include <utils/Socket.hpp>
+
+#include <sstream>
+#include <thread>
+
+#include <netdb.h>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("Socket", "[utils]")
+{
+  SECTION("create/connect and simple read/write")
+  {
+    auto self_client = [](int port) {
+      Socket self_server      = connectServerSocket("localhost", port);
+      std::vector<int> values = {1, 2, 4, 7};
+      write(self_server, values.size());
+      write(self_server, values);
+    };
+
+    Socket server = createServerSocket(0);
+
+    std::thread t(self_client, server.portNumber());
+
+    Socket client = acceptClientSocket(server);
+
+    size_t vector_size = [&] {
+      size_t vector_size;
+      read(client, vector_size);
+      return vector_size;
+    }();
+
+    REQUIRE(vector_size == 4);
+
+    std::vector<int> v(vector_size);
+    read(client, v);
+
+    REQUIRE(v == std::vector<int>{1, 2, 4, 7});
+
+    t.join();
+  }
+
+  SECTION("move constructor")
+  {
+    Socket server   = createServerSocket(0);
+    int port_number = server.portNumber();
+
+    Socket moved_server(std::move(server));
+
+    REQUIRE(port_number == moved_server.portNumber());
+  }
+
+  SECTION("host and port info")
+  {
+    Socket server   = createServerSocket(0);
+    int port_number = server.portNumber();
+
+    std::ostringstream info;
+    info << server;
+
+    std::ostringstream expected;
+    char hbuf[NI_MAXHOST];
+    ::gethostname(hbuf, NI_MAXHOST);
+    expected << hbuf << ':' << port_number;
+
+    REQUIRE(info.str() == expected.str());
+  }
+
+  SECTION("errors")
+  {
+    SECTION("connection")
+    {
+      {
+        auto server = createServerSocket(0);
+        REQUIRE_THROWS_WITH(createServerSocket(server.portNumber()), "error: Address already in use");
+      }
+      REQUIRE_THROWS_WITH(connectServerSocket("localhost", 1), "error: Connection refused");
+
+      // The error message is not checked since it can depend on the
+      // network connection itself
+      REQUIRE_THROWS(connectServerSocket("an invalid host name", 1));
+    }
+
+    SECTION("server <-> server")
+    {
+      Socket server = createServerSocket(0);
+      REQUIRE_THROWS_WITH(write(server, 12), "error: Server cannot write to server socket");
+      REQUIRE_THROWS_WITH(write(server, std::vector<int>{1, 2, 3}), "error: Server cannot write to server socket");
+
+      double x;
+      REQUIRE_THROWS_WITH(read(server, x), "error: Server cannot read from server socket");
+      std::vector<double> v(1);
+      REQUIRE_THROWS_WITH(read(server, v), "error: Server cannot read from server socket");
+    }
+
+    SECTION("invalid read")
+    {
+      auto self_client = [](int port) { Socket self_server = connectServerSocket("localhost", port); };
+
+      Socket server = createServerSocket(0);
+
+      std::thread t(self_client, server.portNumber());
+      Socket client = acceptClientSocket(server);
+      t.join();
+
+      double x;
+      REQUIRE_THROWS_WITH(read(client, x), "error: Could not read data");
+      // One cannot test easily write errors...
+    }
+  }
+}
diff --git a/tests/test_SocketModule.cpp b/tests/test_SocketModule.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a3c3a3edecd3c3f28e2bb6506a8c05205a7dce0
--- /dev/null
+++ b/tests/test_SocketModule.cpp
@@ -0,0 +1,711 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <language/modules/SocketModule.hpp>
+#include <language/utils/BuiltinFunctionEmbedder.hpp>
+#include <language/utils/OperatorRepository.hpp>
+#include <utils/Socket.hpp>
+
+#include <thread>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("SocketModule", "[language]")
+{
+  SocketModule socket_module;
+  const auto& name_builtin_function = socket_module.getNameBuiltinFunctionMap();
+
+  REQUIRE(name_builtin_function.size() == 25);
+
+  auto create_socket_server = [&name_builtin_function] {
+    auto i_function = name_builtin_function.find("createSocketServer:N");
+    REQUIRE(i_function != name_builtin_function.end());
+
+    uint64_t arg            = 0;
+    DataVariant arg_variant = arg;
+
+    IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+    DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+    REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()));
+
+    return dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()).data_ptr();
+  };
+
+  auto accept_socket_client = [&name_builtin_function](const std::shared_ptr<const Socket>& server) {
+    auto i_function = name_builtin_function.find("acceptSocketClient:socket");
+    REQUIRE(i_function != name_builtin_function.end());
+
+    DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(server));
+
+    IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+    DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+    REQUIRE_NOTHROW(dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()));
+
+    return dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()).data_ptr();
+  };
+
+  auto connect_socket_server = [&name_builtin_function](const std::string& hostname, int64_t port) {
+    auto i_function = name_builtin_function.find("connectSocketServer:string*N");
+    if (i_function == name_builtin_function.end()) {
+      FAIL_CHECK("Cannot connect to server. This should NEVER happen");
+    }
+
+    DataVariant hostname_variant = hostname;
+    DataVariant port_variant     = port;
+
+    IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+    DataVariant result_variant                  = function_embedder.apply({hostname_variant, port_variant});
+
+    return dynamic_cast<const DataHandler<const Socket>&>(std::get<EmbeddedData>(result_variant).get()).data_ptr();
+  };
+
+  SECTION("read")
+  {
+    SECTION("read_B")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        bool b = true;
+        write(*self_server, b);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_B:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<bool>(result_variant) == true);
+
+      t.join();
+    }
+
+    SECTION("read_N")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        uint64_t n = 12;
+        write(*self_server, n);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_N:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<uint64_t>(result_variant) == 12);
+
+      t.join();
+    }
+
+    SECTION("read_Z")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        int64_t k = -3;
+        write(*self_server, k);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_Z:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<int64_t>(result_variant) == -3);
+
+      t.join();
+    }
+
+    SECTION("read_R")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        double x = -3.141;
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<double>(result_variant) == -3.141);
+
+      t.join();
+    }
+
+    SECTION("read_R1")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<1> x{1.414};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R1:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyVector<1>>(result_variant) == TinyVector<1>{1.414});
+
+      t.join();
+    }
+
+    SECTION("read_R2")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<2> x{1.414, -3.7};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R2:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyVector<2>>(result_variant) == TinyVector<2>{1.414, -3.7});
+
+      t.join();
+    }
+
+    SECTION("read_R3")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<3> x{1.414, -3.7, 5.19};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R3:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyVector<3>>(result_variant) == TinyVector<3>{1.414, -3.7, 5.19});
+
+      t.join();
+    }
+
+    SECTION("read_R1x1")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<1> x{1.414};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R1x1:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyMatrix<1>>(result_variant) == TinyMatrix<1>{1.414});
+
+      t.join();
+    }
+
+    SECTION("read_R2x2")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<2> x{1.414, -3.7, 2.4, -6};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R2x2:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyMatrix<2>>(result_variant) == TinyMatrix<2>{1.414, -3.7, 2.4, -6});
+
+      t.join();
+    }
+
+    SECTION("read_R3x3")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<3> x{1.414, -3.7, 5.19, 2, 0, -2.6, 1.2, -6, 9.3};
+        write(*self_server, x);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_R3x3:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<TinyMatrix<3>>(result_variant) == TinyMatrix<3>{1.414, -3.7, 5.19, 2, 0, -2.6, 1.2, -6, 9.3});
+
+      t.join();
+    }
+
+    SECTION("read_string")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        std::string s = "foobar";
+        write(*self_server, s.size());
+        write(*self_server, s);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("read_string:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+
+      REQUIRE(std::get<std::string>(result_variant) == std::string{"foobar"});
+
+      t.join();
+    }
+  }
+
+  SECTION("write")
+  {
+    auto get_result = [&name_builtin_function](std::shared_ptr<const Socket> p_client) {
+      auto i_function = name_builtin_function.find("read_B:socket");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant arg_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({arg_variant});
+      return std::get<bool>(result_variant);
+    };
+
+    SECTION("B")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        bool b;
+        read(*self_server, b);
+        const bool result = (b == true);
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*B");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = true;
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("N")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        uint64_t n;
+        read(*self_server, n);
+        const bool result = (n == 17);
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*N");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = uint64_t{17};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("Z")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        int64_t k;
+        read(*self_server, k);
+        const bool result = (k == -13);
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*Z");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = int64_t{-13};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        double x;
+        read(*self_server, x);
+        const bool result = (x == -3.1415);
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = double{-3.1415};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^1")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<1> x;
+        read(*self_server, x);
+        const bool result = (x == TinyVector<1>{-3.1415});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^1");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyVector<1>{-3.1415};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^2")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<2> x;
+        read(*self_server, x);
+        const bool result = (x == TinyVector<2>{-3.1415, 1.414});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^2");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyVector<2>{-3.1415, 1.414};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^3")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyVector<3> x;
+        read(*self_server, x);
+        const bool result = (x == TinyVector<3>{-3.1415, 1.414, 1.3});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^3");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyVector<3>{-3.1415, 1.414, 1.3};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^1x1")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<1> x;
+        read(*self_server, x);
+        const bool result = (x == TinyMatrix<1>{-3.1415});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^1x1");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyMatrix<1>{-3.1415};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^2x2")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<2> x;
+        read(*self_server, x);
+        const bool result = (x == TinyMatrix<2>{3, -3.1415, -1.2, 5.6});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^2x2");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyMatrix<2>{3, -3.1415, -1.2, 5.6};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("R^3x3")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        TinyMatrix<3> x;
+        read(*self_server, x);
+        const bool result = (x == TinyMatrix<3>{3, -3.1415, -1.2, 5.6, -4, 1.7, -5.2, 3.4, 2});
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*R^3x3");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = TinyMatrix<3>{3, -3.1415, -1.2, 5.6, -4, 1.7, -5.2, 3.4, 2};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+
+    SECTION("string")
+    {
+      std::shared_ptr p_server = create_socket_server();
+
+      auto self_client = [&connect_socket_server](int port) {
+        std::shared_ptr self_server = connect_socket_server("localhost", port);
+
+        size_t size;
+        read(*self_server, size);
+        std::string s;
+        s.resize(size);
+        read(*self_server, s);
+        const bool result = (s == "foobar");
+        write(*self_server, result);
+      };
+      std::thread t(self_client, p_server->portNumber());
+
+      std::shared_ptr p_client = accept_socket_client(p_server);
+
+      auto i_function = name_builtin_function.find("write:socket*string");
+      REQUIRE(i_function != name_builtin_function.end());
+
+      DataVariant socket_variant = EmbeddedData(std::make_shared<DataHandler<const Socket>>(p_client));
+      DataVariant value_variant  = std::string{"foobar"};
+
+      IBuiltinFunctionEmbedder& function_embedder = *i_function->second;
+      DataVariant result_variant                  = function_embedder.apply({socket_variant, value_variant});
+
+      REQUIRE(get_result(p_client));
+
+      t.join();
+    }
+  }
+}
diff --git a/tests/test_SquareGaussQuadrature.cpp b/tests/test_SquareGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..74f442a3154e23e4436c9b170a9649e72a61d4ae
--- /dev/null
+++ b/tests/test_SquareGaussQuadrature.cpp
@@ -0,0 +1,484 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <algebra/TinyMatrix.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <analysis/SquareGaussQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("SquareGaussQuadrature", "[analysis]")
+{
+  auto integrate = [](auto f, auto quadrature_formula) {
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(point_list[0]);
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(point_list[i]);
+    }
+
+    return value;
+  };
+
+  auto integrate_on_rectangle = [](auto f, auto quadrature_formula, const std::array<TinyVector<2>, 3>& triangle) {
+    const auto& A = triangle[0];
+    const auto& B = triangle[1];
+    const auto& C = triangle[2];
+
+    TinyMatrix<2> J;
+    for (size_t i = 0; i < 2; ++i) {
+      J(i, 0) = 0.5 * (B[i] - A[i]);
+      J(i, 1) = 0.5 * (C[i] - A[i]);
+    }
+    TinyVector s = 0.5 * (B + C);
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(J * (point_list[0]) + s);
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(J * (point_list[i]) + s);
+    }
+
+    return det(J) * value;
+  };
+
+  auto get_order = [&integrate, &integrate_on_rectangle](auto f, auto quadrature_formula, const double exact_value) {
+    using R2               = TinyVector<2>;
+    const double int_K_hat = integrate(f, quadrature_formula);
+    const double int_refined   //
+      = integrate_on_rectangle(f, quadrature_formula, {R2{-1, -1}, R2{0, -1}, R2{-1, 0}}) +
+        integrate_on_rectangle(f, quadrature_formula, {R2{0, -1}, R2{1, -1}, R2{0, 0}}) +
+        integrate_on_rectangle(f, quadrature_formula, {R2{-1, 0}, R2{0, 0}, R2{-1, 1}}) +
+        integrate_on_rectangle(f, quadrature_formula, {R2{0, 0}, R2{1, 0}, R2{0, 1}});
+
+    return -std::log((int_refined - exact_value) / (int_K_hat - exact_value)) / std::log(2);
+  };
+
+  auto p0 = [](const TinyVector<2>&) { return 2; };
+  auto p1 = [](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return 2 * x + 3 * y + 1;
+  };
+  auto p2 = [&p1](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p1(X) * (2.5 * x - 3 * y + 3);
+  };
+  auto p3 = [&p2](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p2(X) * (-1.5 * x + 3 * y - 3);
+  };
+  auto p4 = [&p3](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p3(X) * (x + y + 1);
+  };
+  auto p5 = [&p4](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p4(X) * (-0.2 * x - 1.3 * y - 0.7);
+  };
+  auto p6 = [&p5](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p5(X) * (3 * x - 2 * y + 3);
+  };
+  auto p7 = [&p6](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p6(X) * (-2 * x + 4 * y - 7);
+  };
+  auto p8 = [&p7](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p7(X) * (2 * x - 3 * y - 3);
+  };
+  auto p9 = [&p8](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p8(X) * (x + 2 * y - 1.7);
+  };
+  auto p10 = [&p9](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p9(X) * (-1.3 * x - 1.7 * y + 1.3);
+  };
+  auto p11 = [&p10](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p10(X) * (0.8 * x - 3.1 * y + 0.6);
+  };
+  auto p12 = [&p11](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p11(X) * (1.8 * x + 1.3 * y - 0.3);
+  };
+  auto p13 = [&p12](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p12(X) * (-0.9 * x + 1.1 * y - 0.6);
+  };
+  auto p14 = [&p13](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p13(X) * (0.6 * x + 0.3 * y + 1.1);
+  };
+  auto p15 = [&p14](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p14(X) * (0.5 * x - 0.4 * y - 1.1);
+  };
+  auto p16 = [&p15](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p15(X) * (-0.3 * x - 0.3 * y - 0.2);
+  };
+  auto p17 = [&p16](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p16(X) * (-0.1 * x - 0.4 * y - 0.3);
+  };
+  auto p18 = [&p17](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p17(X) * (0.2 * x + 0.3 * y + 0.3);
+  };
+  auto p19 = [&p18](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p18(X) * (2.1 * x + 3.3 * y - 0.3);
+  };
+  auto p20 = [&p19](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p19(X) * (1.2 * x - 2.1 * y + 0.6);
+  };
+  auto p21 = [&p20](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p20(X) * (-1.3 * x - 1.2 * y + 0.7);
+  };
+  auto p22 = [&p21](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p21(X) * (1.1 * x - 2.2 * y - 0.3);
+  };
+
+  SECTION("degree 0 and 1")
+  {
+    const QuadratureFormula<2>& l1 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(1));
+
+    REQUIRE(l1.numberOfPoints() == 1);
+
+    REQUIRE(integrate(p0, l1) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l1) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l1) != Catch::Approx(20. / 3));
+
+    REQUIRE(get_order(p2, l1, 20. / 3) == Catch::Approx(2));
+  }
+
+  SECTION("degree 2 and 3")
+  {
+    const QuadratureFormula<2>& l2 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(2));
+    const QuadratureFormula<2>& l3 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(3));
+
+    REQUIRE(&l2 == &l3);
+
+    REQUIRE(l3.numberOfPoints() == 4);
+
+    REQUIRE(integrate(p0, l3) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l3) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l3) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l3) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l3) != Catch::Approx(-1184. / 15));
+
+    REQUIRE(get_order(p4, l3, -1184. / 15) == Catch::Approx(4));
+  }
+
+  SECTION("degree 4 and 5")
+  {
+    const QuadratureFormula<2>& l4 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(4));
+    const QuadratureFormula<2>& l5 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(5));
+
+    REQUIRE(&l4 == &l5);
+
+    REQUIRE(l5.numberOfPoints() == 8);
+
+    REQUIRE(integrate(p0, l5) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l5) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l5) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l5) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l5) == Catch::Approx(-1184. / 15));
+    REQUIRE(integrate(p5, l5) == Catch::Approx(1971. / 25));
+    REQUIRE(integrate(p6, l5) != Catch::Approx(60441. / 175));
+
+    REQUIRE(get_order(p6, l5, 60441. / 175) == Catch::Approx(6));
+  }
+
+  SECTION("degree 6 and 7")
+  {
+    const QuadratureFormula<2>& l6 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(6));
+    const QuadratureFormula<2>& l7 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(7));
+
+    REQUIRE(&l6 == &l7);
+
+    REQUIRE(l7.numberOfPoints() == 12);
+
+    REQUIRE(integrate(p0, l7) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l7) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l7) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l7) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l7) == Catch::Approx(-1184. / 15));
+    REQUIRE(integrate(p5, l7) == Catch::Approx(1971. / 25));
+    REQUIRE(integrate(p6, l7) == Catch::Approx(60441. / 175));
+    REQUIRE(integrate(p7, l7) == Catch::Approx(-1307119. / 525));
+    REQUIRE(integrate(p8, l7) != Catch::Approx(957697. / 175));
+
+    REQUIRE(get_order(p8, l7, 957697. / 175) == Catch::Approx(8));
+  }
+
+  SECTION("degree 8 and 9")
+  {
+    const QuadratureFormula<2>& l8 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(8));
+    const QuadratureFormula<2>& l9 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(9));
+
+    REQUIRE(&l8 == &l9);
+
+    REQUIRE(l9.numberOfPoints() == 20);
+
+    REQUIRE(integrate(p0, l9) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l9) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l9) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l9) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l9) == Catch::Approx(-1184. / 15));
+    REQUIRE(integrate(p5, l9) == Catch::Approx(1971. / 25));
+    REQUIRE(integrate(p6, l9) == Catch::Approx(60441. / 175));
+    REQUIRE(integrate(p7, l9) == Catch::Approx(-1307119. / 525));
+    REQUIRE(integrate(p8, l9) == Catch::Approx(957697. / 175));
+    REQUIRE(integrate(p9, l9) == Catch::Approx(-196981. / 5250));
+    REQUIRE(integrate(p10, l9) != Catch::Approx(78447601. / 577500));
+
+    REQUIRE(get_order(p10, l9, 78447601. / 577500) == Catch::Approx(10));
+  }
+
+  SECTION("degree 10 and 11")
+  {
+    const QuadratureFormula<2>& l10 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(10));
+    const QuadratureFormula<2>& l11 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(11));
+
+    REQUIRE(&l10 == &l11);
+
+    REQUIRE(l11.numberOfPoints() == 28);
+
+    REQUIRE(integrate(p0, l11) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l11) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l11) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l11) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l11) == Catch::Approx(-1184. / 15));
+    REQUIRE(integrate(p5, l11) == Catch::Approx(1971. / 25));
+    REQUIRE(integrate(p6, l11) == Catch::Approx(60441. / 175));
+    REQUIRE(integrate(p7, l11) == Catch::Approx(-1307119. / 525));
+    REQUIRE(integrate(p8, l11) == Catch::Approx(957697. / 175));
+    REQUIRE(integrate(p9, l11) == Catch::Approx(-196981. / 5250));
+    REQUIRE(integrate(p10, l11) == Catch::Approx(78447601. / 577500));
+    REQUIRE(integrate(p11, l11) == Catch::Approx(673235482069. / 86625000));
+    REQUIRE(integrate(p12, l11) != Catch::Approx(-4092600398251. / 303187500));
+
+    REQUIRE(get_order(p12, l11, -4092600398251. / 303187500) == Catch::Approx(12));
+  }
+
+  SECTION("degree 12 and 13")
+  {
+    const QuadratureFormula<2>& l12 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(12));
+    const QuadratureFormula<2>& l13 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(13));
+
+    REQUIRE(&l12 == &l13);
+
+    REQUIRE(l13.numberOfPoints() == 37);
+
+    REQUIRE(integrate(p0, l13) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l13) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l13) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l13) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l13) == Catch::Approx(-1184. / 15));
+    REQUIRE(integrate(p5, l13) == Catch::Approx(1971. / 25));
+    REQUIRE(integrate(p6, l13) == Catch::Approx(60441. / 175));
+    REQUIRE(integrate(p7, l13) == Catch::Approx(-1307119. / 525));
+    REQUIRE(integrate(p8, l13) == Catch::Approx(957697. / 175));
+    REQUIRE(integrate(p9, l13) == Catch::Approx(-196981. / 5250));
+    REQUIRE(integrate(p10, l13) == Catch::Approx(78447601. / 577500));
+    REQUIRE(integrate(p11, l13) == Catch::Approx(673235482069. / 86625000));
+    REQUIRE(integrate(p12, l13) == Catch::Approx(-4092600398251. / 303187500));
+    REQUIRE(integrate(p13, l13) == Catch::Approx(77231697272647. / 5053125000));
+    REQUIRE(integrate(p14, l13) != Catch::Approx(46574962939049. / 9384375000));
+
+    REQUIRE(get_order(p14, l13, 46574962939049. / 9384375000) == Catch::Approx(14));
+  }
+
+  SECTION("degree 14 and 15")
+  {
+    const QuadratureFormula<2>& l14 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(14));
+    const QuadratureFormula<2>& l15 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(15));
+
+    REQUIRE(&l14 == &l15);
+
+    REQUIRE(l15.numberOfPoints() == 48);
+
+    REQUIRE(integrate(p0, l15) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l15) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l15) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l15) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l15) == Catch::Approx(-1184. / 15));
+    REQUIRE(integrate(p5, l15) == Catch::Approx(1971. / 25));
+    REQUIRE(integrate(p6, l15) == Catch::Approx(60441. / 175));
+    REQUIRE(integrate(p7, l15) == Catch::Approx(-1307119. / 525));
+    REQUIRE(integrate(p8, l15) == Catch::Approx(957697. / 175));
+    REQUIRE(integrate(p9, l15) == Catch::Approx(-196981. / 5250));
+    REQUIRE(integrate(p10, l15) == Catch::Approx(78447601. / 577500));
+    REQUIRE(integrate(p11, l15) == Catch::Approx(673235482069. / 86625000));
+    REQUIRE(integrate(p12, l15) == Catch::Approx(-4092600398251. / 303187500));
+    REQUIRE(integrate(p13, l15) == Catch::Approx(77231697272647. / 5053125000));
+    REQUIRE(integrate(p14, l15) == Catch::Approx(46574962939049. / 9384375000));
+    REQUIRE(integrate(p15, l15) == Catch::Approx(-4818864487842259. / 1094843750000));
+    REQUIRE(integrate(p16, l15) != Catch::Approx(-89885697514686141. / 23265429687500));
+
+    REQUIRE(get_order(p16, l15, -89885697514686141. / 23265429687500) == Catch::Approx(16));
+  }
+
+  SECTION("degree 16 and 17")
+  {
+    const QuadratureFormula<2>& l16 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(16));
+    const QuadratureFormula<2>& l17 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(17));
+
+    REQUIRE(&l16 == &l17);
+
+    REQUIRE(l17.numberOfPoints() == 60);
+
+    REQUIRE(integrate(p0, l17) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l17) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l17) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l17) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l17) == Catch::Approx(-1184. / 15));
+    REQUIRE(integrate(p5, l17) == Catch::Approx(1971. / 25));
+    REQUIRE(integrate(p6, l17) == Catch::Approx(60441. / 175));
+    REQUIRE(integrate(p7, l17) == Catch::Approx(-1307119. / 525));
+    REQUIRE(integrate(p8, l17) == Catch::Approx(957697. / 175));
+    REQUIRE(integrate(p9, l17) == Catch::Approx(-196981. / 5250));
+    REQUIRE(integrate(p10, l17) == Catch::Approx(78447601. / 577500));
+    REQUIRE(integrate(p11, l17) == Catch::Approx(673235482069. / 86625000));
+    REQUIRE(integrate(p12, l17) == Catch::Approx(-4092600398251. / 303187500));
+    REQUIRE(integrate(p13, l17) == Catch::Approx(77231697272647. / 5053125000));
+    REQUIRE(integrate(p14, l17) == Catch::Approx(46574962939049. / 9384375000));
+    REQUIRE(integrate(p15, l17) == Catch::Approx(-4818864487842259. / 1094843750000));
+    REQUIRE(integrate(p16, l17) == Catch::Approx(-89885697514686141. / 23265429687500));
+    REQUIRE(integrate(p17, l17) == Catch::Approx(16706355156097391. / 12085937500000));
+    REQUIRE(integrate(p18, l17) != Catch::Approx(495866230514635109957. / 397838847656250000.).epsilon(1E-8));
+
+    REQUIRE(get_order(p18, l17, 495866230514635109957. / 397838847656250000.) == Catch::Approx(18).margin(0.001));
+  }
+
+  SECTION("degree 18 and 19")
+  {
+    const QuadratureFormula<2>& l18 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(18));
+    const QuadratureFormula<2>& l19 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(19));
+
+    REQUIRE(&l18 == &l19);
+
+    REQUIRE(l19.numberOfPoints() == 72);
+
+    REQUIRE(integrate(p0, l19) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l19) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l19) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l19) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l19) == Catch::Approx(-1184. / 15));
+    REQUIRE(integrate(p5, l19) == Catch::Approx(1971. / 25));
+    REQUIRE(integrate(p6, l19) == Catch::Approx(60441. / 175));
+    REQUIRE(integrate(p7, l19) == Catch::Approx(-1307119. / 525));
+    REQUIRE(integrate(p8, l19) == Catch::Approx(957697. / 175));
+    REQUIRE(integrate(p9, l19) == Catch::Approx(-196981. / 5250));
+    REQUIRE(integrate(p10, l19) == Catch::Approx(78447601. / 577500));
+    REQUIRE(integrate(p11, l19) == Catch::Approx(673235482069. / 86625000));
+    REQUIRE(integrate(p12, l19) == Catch::Approx(-4092600398251. / 303187500));
+    REQUIRE(integrate(p13, l19) == Catch::Approx(77231697272647. / 5053125000));
+    REQUIRE(integrate(p14, l19) == Catch::Approx(46574962939049. / 9384375000));
+    REQUIRE(integrate(p15, l19) == Catch::Approx(-4818864487842259. / 1094843750000));
+    REQUIRE(integrate(p16, l19) == Catch::Approx(-89885697514686141. / 23265429687500));
+    REQUIRE(integrate(p17, l19) == Catch::Approx(16706355156097391. / 12085937500000));
+    REQUIRE(integrate(p18, l19) == Catch::Approx(495866230514635109957. / 397838847656250000.));
+    REQUIRE(integrate(p19, l19) == Catch::Approx(703712939580204375319. / 132612949218750000.));
+    REQUIRE(integrate(p20, l19) != Catch::Approx(-891851528496270127477. / 884086328125000000.).epsilon(1E-8));
+
+    REQUIRE(get_order(p20, l19, -891851528496270127477. / 884086328125000000.) == Catch::Approx(20).margin(0.001));
+  }
+
+  SECTION("degree 20 and 21")
+  {
+    const QuadratureFormula<2>& l20 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(20));
+    const QuadratureFormula<2>& l21 = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(21));
+
+    REQUIRE(&l20 == &l21);
+
+    REQUIRE(l21.numberOfPoints() == 85);
+
+    REQUIRE(integrate(p0, l21) == Catch::Approx(8));
+    REQUIRE(integrate(p1, l21) == Catch::Approx(4));
+    REQUIRE(integrate(p2, l21) == Catch::Approx(20. / 3));
+    REQUIRE(integrate(p3, l21) == Catch::Approx(-13));
+    REQUIRE(integrate(p4, l21) == Catch::Approx(-1184. / 15));
+    REQUIRE(integrate(p5, l21) == Catch::Approx(1971. / 25));
+    REQUIRE(integrate(p6, l21) == Catch::Approx(60441. / 175));
+    REQUIRE(integrate(p7, l21) == Catch::Approx(-1307119. / 525));
+    REQUIRE(integrate(p8, l21) == Catch::Approx(957697. / 175));
+    REQUIRE(integrate(p9, l21) == Catch::Approx(-196981. / 5250));
+    REQUIRE(integrate(p10, l21) == Catch::Approx(78447601. / 577500));
+    REQUIRE(integrate(p11, l21) == Catch::Approx(673235482069. / 86625000));
+    REQUIRE(integrate(p12, l21) == Catch::Approx(-4092600398251. / 303187500));
+    REQUIRE(integrate(p13, l21) == Catch::Approx(77231697272647. / 5053125000));
+    REQUIRE(integrate(p14, l21) == Catch::Approx(46574962939049. / 9384375000));
+    REQUIRE(integrate(p15, l21) == Catch::Approx(-4818864487842259. / 1094843750000));
+    REQUIRE(integrate(p16, l21) == Catch::Approx(-89885697514686141. / 23265429687500));
+    REQUIRE(integrate(p17, l21) == Catch::Approx(16706355156097391. / 12085937500000));
+    REQUIRE(integrate(p18, l21) == Catch::Approx(495866230514635109957. / 397838847656250000.));
+    REQUIRE(integrate(p19, l21) == Catch::Approx(703712939580204375319. / 132612949218750000.));
+    REQUIRE(integrate(p20, l21) == Catch::Approx(-891851528496270127477. / 884086328125000000.));
+    REQUIRE(integrate(p21, l21) == Catch::Approx(31710268999580650802107. / 66306474609375000000.));
+    REQUIRE(integrate(p22, l21) != Catch::Approx(-1827205780869627586647799. / 726213769531250000000.).epsilon(1E-8));
+
+    REQUIRE(get_order(p22, l21, -1827205780869627586647799. / 726213769531250000000.) ==
+            Catch::Approx(22).margin(0.01));
+  }
+
+  SECTION("max implemented degree")
+  {
+    REQUIRE(QuadratureManager::instance().maxSquareDegree(QuadratureType::Gauss) == SquareGaussQuadrature::max_degree);
+    REQUIRE_THROWS_WITH(QuadratureManager::instance().getSquareFormula(
+                          GaussQuadratureDescriptor(SquareGaussQuadrature ::max_degree + 1)),
+                        "error: Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(SquareGaussQuadrature ::max_degree) + " on squares");
+  }
+
+  SECTION("Access functions")
+  {
+    const QuadratureFormula<2>& quadrature_formula =
+      QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(7));
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    REQUIRE(point_list.size() == quadrature_formula.numberOfPoints());
+    REQUIRE(weight_list.size() == quadrature_formula.numberOfPoints());
+
+    for (size_t i = 0; i < quadrature_formula.numberOfPoints(); ++i) {
+      REQUIRE(&point_list[i] == &quadrature_formula.point(i));
+      REQUIRE(&weight_list[i] == &quadrature_formula.weight(i));
+    }
+  }
+}
diff --git a/tests/test_SquareTransformation.cpp b/tests/test_SquareTransformation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5b3723e766886d8d6883900463a803ebbb94c216
--- /dev/null
+++ b/tests/test_SquareTransformation.cpp
@@ -0,0 +1,273 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/SquareTransformation.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("SquareTransformation", "[geometry]")
+{
+  SECTION("2D")
+  {
+    using R2 = TinyVector<2>;
+
+    const R2 a_hat{-1, -1};
+    const R2 b_hat{+1, -1};
+    const R2 c_hat{+1, +1};
+    const R2 d_hat{-1, +1};
+
+    const R2 m_hat = zero;
+
+    const R2 a{0, 0};
+    const R2 b{8, -2};
+    const R2 c{12, 7};
+    const R2 d{3, 7};
+
+    const R2 m = 0.25 * (a + b + c + d);
+
+    const SquareTransformation<2> t(a, b, c, d);
+
+    SECTION("values")
+    {
+      REQUIRE(t(a_hat)[0] == Catch::Approx(a[0]));
+      REQUIRE(t(a_hat)[1] == Catch::Approx(a[1]));
+
+      REQUIRE(t(b_hat)[0] == Catch::Approx(b[0]));
+      REQUIRE(t(b_hat)[1] == Catch::Approx(b[1]));
+
+      REQUIRE(t(c_hat)[0] == Catch::Approx(c[0]));
+      REQUIRE(t(c_hat)[1] == Catch::Approx(c[1]));
+
+      REQUIRE(t(d_hat)[0] == Catch::Approx(d[0]));
+      REQUIRE(t(d_hat)[1] == Catch::Approx(d[1]));
+
+      REQUIRE(t(m_hat)[0] == Catch::Approx(m[0]));
+      REQUIRE(t(m_hat)[1] == Catch::Approx(m[1]));
+    }
+
+    SECTION("Jacobian determinant")
+    {
+      SECTION("at points")
+      {
+        auto detJ = [](const R2& X) {
+          const double x = X[0];
+          const double y = X[1];
+
+          return 0.25 * ((0.5 * x + 4) * (y + 17) - 0.5 * (x + 7) * (y - 1));
+        };
+
+        REQUIRE(t.jacobianDeterminant(a_hat) == Catch::Approx(detJ(a_hat)));
+        REQUIRE(t.jacobianDeterminant(b_hat) == Catch::Approx(detJ(b_hat)));
+        REQUIRE(t.jacobianDeterminant(c_hat) == Catch::Approx(detJ(c_hat)));
+        REQUIRE(t.jacobianDeterminant(d_hat) == Catch::Approx(detJ(d_hat)));
+
+        REQUIRE(t.jacobianDeterminant(m_hat) == Catch::Approx(detJ(m_hat)));
+      }
+
+      SECTION("Gauss order 1")
+      {
+        // One point is enough in 2d
+        const QuadratureFormula<2>& gauss =
+          QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(1));
+
+        double surface = 0;
+        for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+          surface += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i));
+        }
+
+        // 71.5 is actually the surface of the quadrangle
+        REQUIRE(surface == Catch::Approx(71.5));
+      }
+
+      SECTION("Gauss order 3")
+      {
+        auto p = [](const R2& X) {
+          const double& x = X[0];
+          const double& y = X[1];
+
+          return (2 * x + 3 * y + 2) * (x - 2 * y - 1);
+        };
+
+        // Jacbian determinant is a degree 1 polynomial, so the
+        // following formula is required to reach exactness
+        const QuadratureFormula<2>& gauss =
+          QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(3));
+
+        double integral = 0;
+        for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+          integral += gauss.weight(i) * t.jacobianDeterminant(gauss.point(i)) * p(t(gauss.point(i)));
+        }
+
+        REQUIRE(integral == Catch::Approx(-76277. / 24));
+      }
+    }
+  }
+
+  SECTION("degenerate 2D ")
+  {
+    SECTION("2D")
+    {
+      using R2 = TinyVector<2>;
+
+      const R2 a{1, 2};
+      const R2 b{3, 1};
+      const R2 c{2, 5};
+
+      const SquareTransformation<2> t(a, b, c, c);
+
+      auto p = [](const R2& X) {
+        const double x = X[0];
+        const double y = X[1];
+        return 2 * x * x + 3 * x * y + y * y + 3 * y + 1;
+      };
+
+      SECTION("Gauss")
+      {
+        QuadratureFormula<2> qf = QuadratureManager::instance().getSquareFormula(GaussQuadratureDescriptor(2));
+
+        double sum = 0;
+        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+          R2 xi = qf.point(i);
+          sum += qf.weight(i) * t.jacobianDeterminant(xi) * p(t(xi));
+        }
+
+        REQUIRE(sum == Catch::Approx(3437. / 24));
+      }
+
+      SECTION("Gauss Legendre")
+      {
+        QuadratureFormula<2> qf = QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(2));
+
+        double sum = 0;
+        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+          R2 xi = qf.point(i);
+          sum += qf.weight(i) * t.jacobianDeterminant(xi) * p(t(xi));
+        }
+
+        REQUIRE(sum == Catch::Approx(3437. / 24));
+      }
+
+      SECTION("Gauss Lobatto")
+      {
+        QuadratureFormula<2> qf = QuadratureManager::instance().getSquareFormula(GaussLobattoQuadratureDescriptor(2));
+
+        double sum = 0;
+        for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+          R2 xi = qf.point(i);
+          sum += qf.weight(i) * t.jacobianDeterminant(xi) * p(t(xi));
+        }
+
+        REQUIRE(sum == Catch::Approx(3437. / 24));
+      }
+    }
+  }
+
+  SECTION("3D")
+  {
+    using R2 = TinyVector<2>;
+
+    const R2 a_hat{-1, -1};
+    const R2 b_hat{+1, -1};
+    const R2 c_hat{+1, +1};
+    const R2 d_hat{-1, +1};
+
+    const R2 m_hat = zero;
+
+    using R3 = TinyVector<3>;
+
+    const R3 a{0, 0, -1};
+    const R3 b{8, -2, 3};
+    const R3 c{12, 7, 2};
+    const R3 d{3, 7, 1};
+
+    const R3 m = 0.25 * (a + b + c + d);
+
+    const SquareTransformation<3> t(a, b, c, d);
+
+    SECTION("values")
+    {
+      REQUIRE(t(a_hat)[0] == Catch::Approx(a[0]));
+      REQUIRE(t(a_hat)[1] == Catch::Approx(a[1]));
+      REQUIRE(t(a_hat)[2] == Catch::Approx(a[2]));
+
+      REQUIRE(t(b_hat)[0] == Catch::Approx(b[0]));
+      REQUIRE(t(b_hat)[1] == Catch::Approx(b[1]));
+      REQUIRE(t(b_hat)[2] == Catch::Approx(b[2]));
+
+      REQUIRE(t(c_hat)[0] == Catch::Approx(c[0]));
+      REQUIRE(t(c_hat)[1] == Catch::Approx(c[1]));
+      REQUIRE(t(c_hat)[2] == Catch::Approx(c[2]));
+
+      REQUIRE(t(d_hat)[0] == Catch::Approx(d[0]));
+      REQUIRE(t(d_hat)[1] == Catch::Approx(d[1]));
+      REQUIRE(t(d_hat)[2] == Catch::Approx(d[2]));
+
+      REQUIRE(t(m_hat)[0] == Catch::Approx(m[0]));
+      REQUIRE(t(m_hat)[1] == Catch::Approx(m[1]));
+      REQUIRE(t(m_hat)[2] == Catch::Approx(m[2]));
+    }
+
+    SECTION("Area variation norm")
+    {
+      auto area_variation_norm = [&](const R2& X) {
+        const double x = X[0];
+        const double y = X[1];
+
+        const R3 J1 = 0.25 * (-a + b + c - d);
+        const R3 J2 = 0.25 * (-a - b + c + d);
+        const R3 J3 = 0.25 * (a - b + c - d);
+
+        return l2Norm(crossProduct(J1 + y * J3, J2 + x * J3));
+      };
+
+      SECTION("at points")
+      {
+        REQUIRE(t.areaVariationNorm(a_hat) == Catch::Approx(area_variation_norm(a_hat)));
+        REQUIRE(t.areaVariationNorm(b_hat) == Catch::Approx(area_variation_norm(b_hat)));
+        REQUIRE(t.areaVariationNorm(c_hat) == Catch::Approx(area_variation_norm(c_hat)));
+        REQUIRE(t.areaVariationNorm(d_hat) == Catch::Approx(area_variation_norm(d_hat)));
+
+        REQUIRE(t.areaVariationNorm(m_hat) == Catch::Approx(area_variation_norm(m_hat)));
+      }
+
+      auto p = [](const R3& X) {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return 2 * x * x + 3 * x - 3 * y * y + y + 2 * z * z - 0.5 * z + 2;
+      };
+
+      SECTION("Gauss order 3")
+      {
+        // Due to the area variation term (square root of a
+        // polynomial), Gauss-like quadrature cannot be exact for
+        // general 3d quadrangle.
+        //
+        // Moreover, even the surface of general 3d quadrangle cannot
+        // be computed exactly.
+        //
+        // So for this test we integrate the following function:
+        // f(x,y,z) := p(x,y,z) * |dA(t^-1(x,y,z))|
+        //
+        // dA denotes the area change on the reference element. This
+        // function can be exactly integrated since computing the
+        // integral on the reference quadrangle, one gets a dA^2,
+        // which is a polynomial.
+        const QuadratureFormula<2>& gauss =
+          QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(4));
+
+        double integral = 0;
+        for (size_t i = 0; i < gauss.numberOfPoints(); ++i) {
+          const double fX = (t.areaVariationNorm(gauss.point(i)) * p(t(gauss.point(i))));
+          integral += gauss.weight(i) * t.areaVariationNorm(gauss.point(i)) * fX;
+        }
+
+        REQUIRE(integral == Catch::Approx(60519715. / 576));
+      }
+    }
+  }
+}
diff --git a/tests/test_SubItemArrayPerItem.cpp b/tests/test_SubItemArrayPerItem.cpp
index d1cb315046b5450e975b7ced5e795103bb6453ad..71a226f01379cccd86ca48c27683ca0b467f80fa 100644
--- a/tests/test_SubItemArrayPerItem.cpp
+++ b/tests/test_SubItemArrayPerItem.cpp
@@ -64,457 +64,483 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
 
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("per cell")
-      {
-        NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
-        REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
-        REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
-
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &=
-              (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id)) and
-              (node_array_per_cell.itemTable(cell_id).numberOfRows() == node_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        const NodeArrayPerCell<const int> const_node_array_per_cell = node_array_per_cell;
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (const_node_array_per_cell.itemTable(cell_id).numberOfRows() ==
-                           node_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
-        REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
-        REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+          auto mesh_1d = named_mesh.mesh();
+
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
+            REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
+            REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &=
+                  (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id)) and
+                  (node_array_per_cell.itemTable(cell_id).numberOfRows() ==
+                   node_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
+            const NodeArrayPerCell<const int> const_node_array_per_cell = node_array_per_cell;
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (const_node_array_per_cell.itemTable(cell_id).numberOfRows() ==
+                               node_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        FaceArrayPerCell<int> face_array_per_cell{connectivity, 2};
-        REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
-        REQUIRE(face_array_per_cell.sizeOfArrays() == 2);
+            EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
+            REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
+            REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+            FaceArrayPerCell<int> face_array_per_cell{connectivity, 2};
+            REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
+            REQUIRE(face_array_per_cell.sizeOfArrays() == 2);
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellArrayPerFace<int> cell_array_per_face{connectivity, 2};
-        REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
-        REQUIRE(cell_array_per_face.sizeOfArrays() == 2);
 
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+          SECTION("per face")
+          {
+            CellArrayPerFace<int> cell_array_per_face{connectivity, 2};
+            REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
+            REQUIRE(cell_array_per_face.sizeOfArrays() == 2);
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per edge")
-      {
-        CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
-        REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
-        REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+          SECTION("per edge")
+          {
+            CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
+            REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
+            REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
 
-      SECTION("per node")
-      {
-        CellArrayPerNode<int> cell_array_per_node{connectivity, 4};
-        REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
-        REQUIRE(cell_array_per_node.sizeOfArrays() == 4);
-
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+          SECTION("per node")
+          {
+            CellArrayPerNode<int> cell_array_per_node{connectivity, 4};
+            REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
+            REQUIRE(cell_array_per_node.sizeOfArrays() == 4);
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
-
-      SECTION("per cell")
-      {
-        NodeArrayPerCell<int> node_array_per_cell{connectivity, 5};
-        REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
-        REQUIRE(node_array_per_cell.sizeOfArrays() == 5);
-
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
-        REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
-        REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
-
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
-        REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
-        REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
-
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
-        REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
-        REQUIRE(cell_array_per_face.sizeOfArrays() == 3);
-
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
-        REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(node_array_per_face.numberOfArrays() == number_of_arrays(node_array_per_face));
-        REQUIRE(node_array_per_face.sizeOfArrays() == 2);
-
-        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
+          auto mesh_2d = named_mesh.mesh();
+
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeArrayPerCell<int> node_array_per_cell{connectivity, 5};
+            REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
+            REQUIRE(node_array_per_cell.sizeOfArrays() == 5);
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-      SECTION("per edge")
-      {
-        CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
-        REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
-        REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+            EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
+            REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
+            REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+            FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
+            REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
+            REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
 
-        NodeArrayPerEdge<int> node_array_per_edge{connectivity, 5};
-        REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(node_array_per_edge.numberOfArrays() == number_of_arrays(node_array_per_edge));
-        REQUIRE(node_array_per_edge.sizeOfArrays() == 5);
+          SECTION("per face")
+          {
+            CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
+            REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
+            REQUIRE(cell_array_per_face.sizeOfArrays() == 3);
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
+            NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
+            REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(node_array_per_face.numberOfArrays() == number_of_arrays(node_array_per_face));
+            REQUIRE(node_array_per_face.sizeOfArrays() == 2);
+
+            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
 
-      SECTION("per node")
-      {
-        EdgeArrayPerNode<int> edge_array_per_node{connectivity, 4};
-        REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(edge_array_per_node.numberOfArrays() == number_of_arrays(edge_array_per_node));
-        REQUIRE(edge_array_per_node.sizeOfArrays() == 4);
+          SECTION("per edge")
+          {
+            CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
+            REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
+            REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+            NodeArrayPerEdge<int> node_array_per_edge{connectivity, 5};
+            REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(node_array_per_edge.numberOfArrays() == number_of_arrays(node_array_per_edge));
+            REQUIRE(node_array_per_edge.sizeOfArrays() == 5);
+
+            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-
-        FaceArrayPerNode<int> face_array_per_node{connectivity, 3};
-        REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(face_array_per_node.numberOfArrays() == number_of_arrays(face_array_per_node));
-        REQUIRE(face_array_per_node.sizeOfArrays() == 3);
 
-        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
-          }
-          REQUIRE(is_correct);
-        }
+          SECTION("per node")
+          {
+            EdgeArrayPerNode<int> edge_array_per_node{connectivity, 4};
+            REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(edge_array_per_node.numberOfArrays() == number_of_arrays(edge_array_per_node));
+            REQUIRE(edge_array_per_node.sizeOfArrays() == 4);
+
+            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        CellArrayPerNode<int> cell_array_per_node{connectivity, 2};
-        REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
-        REQUIRE(cell_array_per_node.sizeOfArrays() == 2);
+            FaceArrayPerNode<int> face_array_per_node{connectivity, 3};
+            REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(face_array_per_node.numberOfArrays() == number_of_arrays(face_array_per_node));
+            REQUIRE(face_array_per_node.sizeOfArrays() == 3);
+
+            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+            CellArrayPerNode<int> cell_array_per_node{connectivity, 2};
+            REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
+            REQUIRE(cell_array_per_node.sizeOfArrays() == 2);
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
     }
+
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      SECTION("per cell")
-      {
-        NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
-        REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
-        REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
-
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
-        REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
-        REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
-
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
-        REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
-        REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
-
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellArrayPerFace<int> cell_array_per_face{connectivity, 5};
-        REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
-        REQUIRE(cell_array_per_face.sizeOfArrays() == 5);
-
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        EdgeArrayPerFace<int> edge_array_per_face{connectivity, 3};
-        REQUIRE(edge_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(edge_array_per_face.numberOfArrays() == number_of_arrays(edge_array_per_face));
-        REQUIRE(edge_array_per_face.sizeOfArrays() == 3);
-
-        auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_edge_matrix[face_id].size() == edge_array_per_face.numberOfSubArrays(face_id));
-          }
-          REQUIRE(is_correct);
-        }
+          auto mesh_3d = named_mesh.mesh();
+
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeArrayPerCell<int> node_array_per_cell{connectivity, 3};
+            REQUIRE(node_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_array_per_cell.numberOfArrays() == number_of_arrays(node_array_per_cell));
+            REQUIRE(node_array_per_cell.sizeOfArrays() == 3);
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_node_matrix[cell_id].size() == node_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
-        REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(node_array_per_face.numberOfArrays() == number_of_arrays(node_array_per_face));
-        REQUIRE(node_array_per_face.sizeOfArrays() == 2);
+            EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 4};
+            REQUIRE(edge_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_array_per_cell.numberOfArrays() == number_of_arrays(edge_array_per_cell));
+            REQUIRE(edge_array_per_cell.sizeOfArrays() == 4);
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
+            FaceArrayPerCell<int> face_array_per_cell{connectivity, 3};
+            REQUIRE(face_array_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_array_per_cell.numberOfArrays() == number_of_arrays(face_array_per_cell));
+            REQUIRE(face_array_per_cell.sizeOfArrays() == 3);
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_array_per_cell.numberOfSubArrays(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per edge")
-      {
-        CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
-        REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
-        REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
+          SECTION("per face")
+          {
+            CellArrayPerFace<int> cell_array_per_face{connectivity, 5};
+            REQUIRE(cell_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_array_per_face.numberOfArrays() == number_of_arrays(cell_array_per_face));
+            REQUIRE(cell_array_per_face.sizeOfArrays() == 5);
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        FaceArrayPerEdge<int> face_array_per_edge{connectivity, 5};
-        REQUIRE(face_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(face_array_per_edge.numberOfArrays() == number_of_arrays(face_array_per_edge));
-        REQUIRE(face_array_per_edge.sizeOfArrays() == 5);
+            EdgeArrayPerFace<int> edge_array_per_face{connectivity, 3};
+            REQUIRE(edge_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(edge_array_per_face.numberOfArrays() == number_of_arrays(edge_array_per_face));
+            REQUIRE(edge_array_per_face.sizeOfArrays() == 3);
+
+            auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_edge_matrix[face_id].size() == edge_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_face_matrix[edge_id].size() == face_array_per_edge.numberOfSubArrays(edge_id));
+            NodeArrayPerFace<int> node_array_per_face{connectivity, 2};
+            REQUIRE(node_array_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(node_array_per_face.numberOfArrays() == number_of_arrays(node_array_per_face));
+            REQUIRE(node_array_per_face.sizeOfArrays() == 2);
+
+            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_node_matrix[face_id].size() == node_array_per_face.numberOfSubArrays(face_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
 
-        NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
-        REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(node_array_per_edge.numberOfArrays() == number_of_arrays(node_array_per_edge));
-        REQUIRE(node_array_per_edge.sizeOfArrays() == 3);
-
-        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
-      }
+          SECTION("per edge")
+          {
+            CellArrayPerEdge<int> cell_array_per_edge{connectivity, 3};
+            REQUIRE(cell_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_array_per_edge.numberOfArrays() == number_of_arrays(cell_array_per_edge));
+            REQUIRE(cell_array_per_edge.sizeOfArrays() == 3);
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-      SECTION("per node")
-      {
-        EdgeArrayPerNode<int> edge_array_per_node{connectivity, 3};
-        REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(edge_array_per_node.numberOfArrays() == number_of_arrays(edge_array_per_node));
-        REQUIRE(edge_array_per_node.sizeOfArrays() == 3);
+            FaceArrayPerEdge<int> face_array_per_edge{connectivity, 5};
+            REQUIRE(face_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(face_array_per_edge.numberOfArrays() == number_of_arrays(face_array_per_edge));
+            REQUIRE(face_array_per_edge.sizeOfArrays() == 5);
+
+            auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_face_matrix[edge_id].size() == face_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+            NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
+            REQUIRE(node_array_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(node_array_per_edge.numberOfArrays() == number_of_arrays(node_array_per_edge));
+            REQUIRE(node_array_per_edge.sizeOfArrays() == 3);
+
+            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_node_matrix[edge_id].size() == node_array_per_edge.numberOfSubArrays(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-
-        FaceArrayPerNode<int> face_array_per_node{connectivity, 4};
-        REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(face_array_per_node.numberOfArrays() == number_of_arrays(face_array_per_node));
-        REQUIRE(face_array_per_node.sizeOfArrays() == 4);
 
-        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
-          }
-          REQUIRE(is_correct);
-        }
+          SECTION("per node")
+          {
+            EdgeArrayPerNode<int> edge_array_per_node{connectivity, 3};
+            REQUIRE(edge_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(edge_array_per_node.numberOfArrays() == number_of_arrays(edge_array_per_node));
+            REQUIRE(edge_array_per_node.sizeOfArrays() == 3);
+
+            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_edge_matrix[node_id].size() == edge_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        CellArrayPerNode<int> cell_array_per_node{connectivity, 5};
-        REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
-        REQUIRE(cell_array_per_node.sizeOfArrays() == 5);
+            FaceArrayPerNode<int> face_array_per_node{connectivity, 4};
+            REQUIRE(face_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(face_array_per_node.numberOfArrays() == number_of_arrays(face_array_per_node));
+            REQUIRE(face_array_per_node.sizeOfArrays() == 4);
+
+            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_face_matrix[node_id].size() == face_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
 
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+            CellArrayPerNode<int> cell_array_per_node{connectivity, 5};
+            REQUIRE(cell_array_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_array_per_node.numberOfArrays() == number_of_arrays(cell_array_per_node));
+            REQUIRE(cell_array_per_node.sizeOfArrays() == 5);
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_array_per_node.numberOfSubArrays(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
     }
@@ -524,325 +550,478 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
   {
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      EdgeArrayPerCell<size_t> edge_arrays_per_cell{connectivity, 3};
-      {
-        size_t array = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
-            for (size_t i = 0; i < edge_arrays_per_cell(cell_id, i_edge).size(); ++i) {
-              edge_arrays_per_cell(cell_id, i_edge)[i] = array++;
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_1d = named_mesh.mesh();
+
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+          EdgeArrayPerCell<size_t> edge_arrays_per_cell{connectivity, 3};
+          {
+            size_t array = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
+                for (size_t i = 0; i < edge_arrays_per_cell(cell_id, i_edge).size(); ++i) {
+                  edge_arrays_per_cell(cell_id, i_edge)[i] = array++;
+                }
+              }
             }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t k     = 0;
-        for (size_t i = 0; i < edge_arrays_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < edge_arrays_per_cell.sizeOfArrays(); ++j, ++k) {
-            is_same &= (edge_arrays_per_cell[i][j] == k);
+          {
+            bool is_same = true;
+            size_t k     = 0;
+            for (size_t i = 0; i < edge_arrays_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < edge_arrays_per_cell.sizeOfArrays(); ++j, ++k) {
+                is_same &= (edge_arrays_per_cell[i][j] == k);
+              }
+            }
+            REQUIRE(is_same);
           }
-        }
-        REQUIRE(is_same);
-      }
 
-      {
-        size_t k = 0;
-        for (size_t i = 0; i < edge_arrays_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < edge_arrays_per_cell.sizeOfArrays(); ++j, ++k) {
-            edge_arrays_per_cell[i][j] = k * k + 1;
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < edge_arrays_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < edge_arrays_per_cell.sizeOfArrays(); ++j, ++k) {
+                edge_arrays_per_cell[i][j] = k * k + 1;
+              }
+            }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
-            for (size_t l = 0; l < edge_arrays_per_cell(cell_id, i_edge).size(); ++l, ++i) {
-              is_same &= (edge_arrays_per_cell(cell_id, i_edge)[l] == i * i + 1);
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_edge = 0; i_edge < edge_arrays_per_cell.numberOfSubArrays(cell_id); ++i_edge) {
+                for (size_t l = 0; l < edge_arrays_per_cell(cell_id, i_edge).size(); ++l, ++i) {
+                  is_same &= (edge_arrays_per_cell(cell_id, i_edge)[l] == i * i + 1);
+                }
+              }
             }
+            REQUIRE(is_same);
           }
-        }
-        REQUIRE(is_same);
-      }
 
-      const EdgeArrayPerCell<const size_t> const_edge_arrays_per_cell = edge_arrays_per_cell;
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          const auto& cell_table = const_edge_arrays_per_cell.itemTable(cell_id);
-          for (size_t i_edge = 0; i_edge < cell_table.numberOfRows(); ++i_edge) {
-            const auto& array = cell_table[i_edge];
-            for (size_t l = 0; l < array.size(); ++l, ++i) {
-              is_same &= (array[l] == i * i + 1);
+          const EdgeArrayPerCell<const size_t> const_edge_arrays_per_cell = edge_arrays_per_cell;
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              const auto& cell_table = const_edge_arrays_per_cell.itemTable(cell_id);
+              for (size_t i_edge = 0; i_edge < cell_table.numberOfRows(); ++i_edge) {
+                const auto& array = cell_table[i_edge];
+                for (size_t l = 0; l < array.size(); ++l, ++i) {
+                  is_same &= (array[l] == i * i + 1);
+                }
+              }
             }
+            REQUIRE(is_same);
           }
         }
-        REQUIRE(is_same);
       }
     }
 
     SECTION("2D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-      CellArrayPerFace<size_t> cell_arrays_per_face{connectivity, 3};
-      {
-        size_t array = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
-            for (size_t i = 0; i < cell_arrays_per_face(face_id, i_cell).size(); ++i) {
-              cell_arrays_per_face(face_id, i_cell)[i] = array++;
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_2d = named_mesh.mesh();
+
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+          CellArrayPerFace<size_t> cell_arrays_per_face{connectivity, 3};
+          {
+            size_t array = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
+                for (size_t i = 0; i < cell_arrays_per_face(face_id, i_cell).size(); ++i) {
+                  cell_arrays_per_face(face_id, i_cell)[i] = array++;
+                }
+              }
             }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t k     = 0;
-        for (size_t i = 0; i < cell_arrays_per_face.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < cell_arrays_per_face.sizeOfArrays(); ++j, ++k) {
-            is_same &= (cell_arrays_per_face[i][j] == k);
+          {
+            bool is_same = true;
+            size_t k     = 0;
+            for (size_t i = 0; i < cell_arrays_per_face.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < cell_arrays_per_face.sizeOfArrays(); ++j, ++k) {
+                is_same &= (cell_arrays_per_face[i][j] == k);
+              }
+            }
+            REQUIRE(is_same);
           }
-        }
-        REQUIRE(is_same);
-      }
-      {
-        size_t k = 0;
-        for (size_t i = 0; i < cell_arrays_per_face.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < cell_arrays_per_face.sizeOfArrays(); ++j, ++k) {
-            cell_arrays_per_face[i][j] = 3 * k + 1;
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < cell_arrays_per_face.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < cell_arrays_per_face.sizeOfArrays(); ++j, ++k) {
+                cell_arrays_per_face[i][j] = 3 * k + 1;
+              }
+            }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
-            for (size_t l = 0; l < cell_arrays_per_face(face_id, i_cell).size(); ++l, ++i) {
-              is_same &= (cell_arrays_per_face(face_id, i_cell)[l] == 3 * i + 1);
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              for (size_t i_cell = 0; i_cell < cell_arrays_per_face.numberOfSubArrays(face_id); ++i_cell) {
+                for (size_t l = 0; l < cell_arrays_per_face(face_id, i_cell).size(); ++l, ++i) {
+                  is_same &= (cell_arrays_per_face(face_id, i_cell)[l] == 3 * i + 1);
+                }
+              }
             }
+            REQUIRE(is_same);
           }
-        }
-        REQUIRE(is_same);
-      }
 
-      const CellArrayPerFace<const size_t> const_cell_arrays_per_face = cell_arrays_per_face;
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          const auto& face_table = const_cell_arrays_per_face.itemTable(face_id);
-          for (size_t i_cell = 0; i_cell < face_table.numberOfRows(); ++i_cell) {
-            const auto& array = face_table[i_cell];
-            for (size_t l = 0; l < array.size(); ++l, ++i) {
-              is_same &= (array[l] == 3 * i + 1);
+          const CellArrayPerFace<const size_t> const_cell_arrays_per_face = cell_arrays_per_face;
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              const auto& face_table = const_cell_arrays_per_face.itemTable(face_id);
+              for (size_t i_cell = 0; i_cell < face_table.numberOfRows(); ++i_cell) {
+                const auto& array = face_table[i_cell];
+                for (size_t l = 0; l < array.size(); ++l, ++i) {
+                  is_same &= (array[l] == 3 * i + 1);
+                }
+              }
             }
+            REQUIRE(is_same);
           }
         }
-        REQUIRE(is_same);
       }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      FaceArrayPerNode<size_t> face_arrays_per_node{connectivity, 3};
-      {
-        size_t array = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
-            for (size_t i = 0; i < face_arrays_per_node(node_id, i_face).size(); ++i)
-              face_arrays_per_node(node_id, i_face)[i] = array++;
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
+
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+          FaceArrayPerNode<size_t> face_arrays_per_node{connectivity, 3};
+          {
+            size_t array = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
+                for (size_t i = 0; i < face_arrays_per_node(node_id, i_face).size(); ++i)
+                  face_arrays_per_node(node_id, i_face)[i] = array++;
+              }
+            }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t k     = 0;
-        for (size_t i = 0; i < face_arrays_per_node.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < face_arrays_per_node.sizeOfArrays(); ++j, ++k) {
-            is_same &= (face_arrays_per_node[i][j] == k);
+          {
+            bool is_same = true;
+            size_t k     = 0;
+            for (size_t i = 0; i < face_arrays_per_node.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < face_arrays_per_node.sizeOfArrays(); ++j, ++k) {
+                is_same &= (face_arrays_per_node[i][j] == k);
+              }
+              REQUIRE(is_same);
+            }
           }
-          REQUIRE(is_same);
-        }
-      }
-      {
-        size_t k = 0;
-        for (size_t i = 0; i < face_arrays_per_node.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < face_arrays_per_node.sizeOfArrays(); ++j, ++k) {
-            face_arrays_per_node[i][j] = 3 + k * k;
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < face_arrays_per_node.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < face_arrays_per_node.sizeOfArrays(); ++j, ++k) {
+                face_arrays_per_node[i][j] = 3 + k * k;
+              }
+            }
           }
-        }
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
-            for (size_t l = 0; l < face_arrays_per_node(node_id, i_face).size(); ++l, ++i) {
-              is_same &= (face_arrays_per_node(node_id, i_face)[l] == 3 + i * i);
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              for (size_t i_face = 0; i_face < face_arrays_per_node.numberOfSubArrays(node_id); ++i_face) {
+                for (size_t l = 0; l < face_arrays_per_node(node_id, i_face).size(); ++l, ++i) {
+                  is_same &= (face_arrays_per_node(node_id, i_face)[l] == 3 + i * i);
+                }
+              }
             }
+            REQUIRE(is_same);
           }
-        }
-        REQUIRE(is_same);
-      }
 
-      const FaceArrayPerNode<const size_t> const_face_arrays_per_node = face_arrays_per_node;
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          const auto& node_table = const_face_arrays_per_node.itemTable(node_id);
-          for (size_t i_face = 0; i_face < node_table.numberOfRows(); ++i_face) {
-            const auto& array = node_table[i_face];
-            for (size_t l = 0; l < array.size(); ++l, ++i) {
-              is_same &= (array[l] == 3 + i * i);
+          const FaceArrayPerNode<const size_t> const_face_arrays_per_node = face_arrays_per_node;
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              const auto& node_table = const_face_arrays_per_node.itemTable(node_id);
+              for (size_t i_face = 0; i_face < node_table.numberOfRows(); ++i_face) {
+                const auto& array = node_table[i_face];
+                for (size_t l = 0; l < array.size(); ++l, ++i) {
+                  is_same &= (array[l] == 3 + i * i);
+                }
+              }
             }
+            REQUIRE(is_same);
           }
         }
-        REQUIRE(is_same);
       }
     }
   }
 
   SECTION("copy")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-    SECTION("classic")
-    {
-      NodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
       {
-        size_t k = 0;
-        for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
-            node_array_per_cell[i][j] = k;
+        auto mesh_3d = named_mesh.mesh();
+
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+        SECTION("classic")
+        {
+          NodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
+                node_array_per_cell[i][j] = k;
+              }
+            }
           }
-        }
-      }
-      NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
-            is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+          NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
+            }
+
+            REQUIRE(is_same);
           }
-        }
 
-        REQUIRE(is_same);
-      }
+          {
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              auto cell_table = node_array_per_cell.itemTable(cell_id);
+              for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
+                auto node_array = cell_table[i_node];
+                for (size_t i = 0; i < node_array.size(); ++i) {
+                  node_array[i] = cell_id + i_node + i;
+                }
+              }
+            }
+          }
 
-      {
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          auto cell_table = node_array_per_cell.itemTable(cell_id);
-          for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
-            auto node_array = cell_table[i_node];
-            for (size_t i = 0; i < node_array.size(); ++i) {
-              node_array[i] = cell_id + i_node + i;
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < copy_node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
             }
+
+            REQUIRE(not is_same);
           }
         }
-      }
 
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < copy_node_array_per_cell.sizeOfArrays(); ++j) {
-            is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+        SECTION("from weak")
+        {
+          WeakNodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
+                node_array_per_cell[i][j] = k;
+              }
+            }
           }
-        }
 
-        REQUIRE(not is_same);
-      }
-    }
+          WeakNodeArrayPerCell<const size_t> node_const_array_per_cell = node_array_per_cell;
 
-    SECTION("from weak")
-    {
-      WeakNodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
-      {
-        size_t k = 0;
-        for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
-            node_array_per_cell[i][j] = k;
+          NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_const_array_per_cell);
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
+            }
+
+            REQUIRE(is_same);
+          }
+
+          {
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              auto cell_table = node_array_per_cell.itemTable(cell_id);
+              for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
+                auto node_array = cell_table[i_node];
+                for (size_t i = 0; i < node_array.size(); ++i) {
+                  node_array[i] = cell_id + i_node + i;
+                }
+              }
+            }
+          }
+
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
+            }
+
+            REQUIRE(not is_same);
           }
         }
       }
+    }
+  }
+
+  SECTION("fill")
+  {
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
       {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
-            is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+        auto mesh_3d = named_mesh.mesh();
+
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+        SECTION("classic")
+        {
+          NodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
+                node_array_per_cell[i][j] = k;
+              }
+            }
           }
-        }
+          NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
+            }
 
-        REQUIRE(is_same);
-      }
+            REQUIRE(is_same);
+          }
 
-      {
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          auto cell_table = node_array_per_cell.itemTable(cell_id);
-          for (size_t i_node = 0; i_node < node_array_per_cell.numberOfSubArrays(cell_id); ++i_node) {
-            auto node_array = cell_table[i_node];
-            for (size_t i = 0; i < node_array.size(); ++i) {
-              node_array[i] = cell_id + i_node + i;
+          node_array_per_cell.fill(11);
+
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < copy_node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
             }
+
+            REQUIRE(not is_same);
           }
-        }
-      }
 
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
-          for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
-            is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+          {
+            bool is_filled_with_11 = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < copy_node_array_per_cell.sizeOfArrays(); ++j) {
+                is_filled_with_11 &= (node_array_per_cell[i][j] == 11);
+              }
+            }
+
+            REQUIRE(is_filled_with_11);
           }
         }
 
-        REQUIRE(not is_same);
+        SECTION("from weak")
+        {
+          WeakNodeArrayPerCell<size_t> node_array_per_cell{connectivity, 3};
+          {
+            size_t k = 0;
+            for (size_t i = 0; i < node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j, ++k) {
+                node_array_per_cell[i][j] = k;
+              }
+            }
+          }
+
+          NodeArrayPerCell<size_t> copy_node_array_per_cell = copy(node_array_per_cell);
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
+            }
+
+            REQUIRE(is_same);
+          }
+
+          node_array_per_cell.fill(13);
+
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < node_array_per_cell.sizeOfArrays(); ++j) {
+                is_same &= (copy_node_array_per_cell[i][j] == node_array_per_cell[i][j]);
+              }
+            }
+
+            REQUIRE(not is_same);
+          }
+
+          {
+            bool is_filled_with_13 = true;
+            for (size_t i = 0; i < copy_node_array_per_cell.numberOfArrays(); ++i) {
+              for (size_t j = 0; j < copy_node_array_per_cell.sizeOfArrays(); ++j) {
+                is_filled_with_13 &= (node_array_per_cell[i][j] == 13);
+              }
+            }
+
+            REQUIRE(is_filled_with_13);
+          }
+        }
       }
     }
   }
 
   SECTION("WeakSubItemArrayPerItem")
   {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-    WeakFaceArrayPerCell<int> weak_face_array_per_cell{connectivity, 3};
-    {
-      size_t k = 0;
-      for (size_t i = 0; i < weak_face_array_per_cell.numberOfArrays(); ++i) {
-        for (size_t j = 0; j < weak_face_array_per_cell.sizeOfArrays(); ++j, ++k) {
-          weak_face_array_per_cell[i][j] = k;
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_2d = named_mesh.mesh();
+
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+        WeakFaceArrayPerCell<int> weak_face_array_per_cell{connectivity, 3};
+        {
+          size_t k = 0;
+          for (size_t i = 0; i < weak_face_array_per_cell.numberOfArrays(); ++i) {
+            for (size_t j = 0; j < weak_face_array_per_cell.sizeOfArrays(); ++j, ++k) {
+              weak_face_array_per_cell[i][j] = k;
+            }
+          }
         }
-      }
-    }
 
-    FaceArrayPerCell<const int> face_array_per_cell{weak_face_array_per_cell};
+        FaceArrayPerCell<const int> face_array_per_cell{weak_face_array_per_cell};
 
-    REQUIRE(face_array_per_cell.connectivity_ptr() == weak_face_array_per_cell.connectivity_ptr());
-    REQUIRE(face_array_per_cell.sizeOfArrays() == weak_face_array_per_cell.sizeOfArrays());
+        REQUIRE(face_array_per_cell.connectivity_ptr() == weak_face_array_per_cell.connectivity_ptr());
+        REQUIRE(face_array_per_cell.sizeOfArrays() == weak_face_array_per_cell.sizeOfArrays());
 
-    bool is_same = true;
-    for (size_t i = 0; i < weak_face_array_per_cell.numberOfArrays(); ++i) {
-      for (size_t j = 0; j < weak_face_array_per_cell.sizeOfArrays(); ++j) {
-        is_same &= (face_array_per_cell[i][j] == weak_face_array_per_cell[i][j]);
+        bool is_same = true;
+        for (size_t i = 0; i < weak_face_array_per_cell.numberOfArrays(); ++i) {
+          for (size_t j = 0; j < weak_face_array_per_cell.sizeOfArrays(); ++j) {
+            is_same &= (face_array_per_cell[i][j] == weak_face_array_per_cell[i][j]);
+          }
+        }
+        REQUIRE(is_same);
       }
     }
-    REQUIRE(is_same);
   }
 
 #ifndef NDEBUG
@@ -889,71 +1068,83 @@ TEST_CASE("SubItemArrayPerItem", "[mesh]")
 
     SECTION("checking for bounds violation")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
-      {
-        FaceId invalid_face_id = connectivity.numberOfFaces();
-        REQUIRE_THROWS_AS(cell_array_per_face(invalid_face_id, 0), AssertError);
-      }
-      if (connectivity.numberOfFaces() > 0) {
-        FaceId face_id         = 0;
-        const auto& face_table = cell_array_per_face.itemTable(face_id);
-        REQUIRE_THROWS_AS(cell_array_per_face(face_id, face_table.numberOfRows()), AssertError);
-        REQUIRE_THROWS_AS(face_table[face_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[face_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[0][cell_array_per_face.sizeOfArrays()], AssertError);
-        REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[0][cell_array_per_face.sizeOfArrays()] = 2,
-                          AssertError);
-      }
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-      FaceArrayPerNode<int> face_array_per_node{connectivity, 5};
-      {
-        NodeId invalid_node_id = connectivity.numberOfNodes();
-        REQUIRE_THROWS_AS(face_array_per_node(invalid_node_id, 0), AssertError);
-      }
-      if (connectivity.numberOfNodes() > 0) {
-        NodeId node_id         = 0;
-        const auto& node_table = face_array_per_node.itemTable(node_id);
-        REQUIRE_THROWS_AS(face_array_per_node(node_id, node_table.numberOfRows()), AssertError);
-        REQUIRE_THROWS_AS(node_table[node_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[node_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[0][face_array_per_node.sizeOfArrays()], AssertError);
-        REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[0][face_array_per_node.sizeOfArrays()] = 2,
-                          AssertError);
-      }
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 3};
-      {
-        CellId invalid_cell_id = connectivity.numberOfCells();
-        REQUIRE_THROWS_AS(edge_array_per_cell(invalid_cell_id, 0), AssertError);
-      }
-      if (connectivity.numberOfCells() > 0) {
-        CellId cell_id         = 0;
-        const auto& cell_table = edge_array_per_cell.itemTable(cell_id);
-        REQUIRE_THROWS_AS(edge_array_per_cell(cell_id, cell_table.numberOfRows()), AssertError);
-        REQUIRE_THROWS_AS(cell_table[cell_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[cell_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[0][edge_array_per_cell.sizeOfArrays()], AssertError);
-        REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[0][edge_array_per_cell.sizeOfArrays()] == 2,
-                          AssertError);
-      }
+          CellArrayPerFace<int> cell_array_per_face{connectivity, 3};
+          {
+            FaceId invalid_face_id = connectivity.numberOfFaces();
+            REQUIRE_THROWS_AS(cell_array_per_face(invalid_face_id, 0), AssertError);
+          }
+          if (connectivity.numberOfFaces() > 0) {
+            FaceId face_id         = 0;
+            const auto& face_table = cell_array_per_face.itemTable(face_id);
+            REQUIRE_THROWS_AS(cell_array_per_face(face_id, face_table.numberOfRows()), AssertError);
+            REQUIRE_THROWS_AS(face_table[face_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[face_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[0][cell_array_per_face.sizeOfArrays()],
+                              AssertError);
+            REQUIRE_THROWS_AS(cell_array_per_face.itemTable(face_id)[0][cell_array_per_face.sizeOfArrays()] = 2,
+                              AssertError);
+          }
 
-      NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
-      {
-        EdgeId invalid_edge_id = connectivity.numberOfEdges();
-        REQUIRE_THROWS_AS(node_array_per_edge(invalid_edge_id, 0), AssertError);
-      }
-      if (connectivity.numberOfEdges() > 0) {
-        EdgeId edge_id         = 0;
-        const auto& edge_table = node_array_per_edge.itemTable(edge_id);
-        REQUIRE_THROWS_AS(node_array_per_edge(edge_id, edge_table.numberOfRows()), AssertError);
-        REQUIRE_THROWS_AS(edge_table[edge_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[edge_table.numberOfRows()], AssertError);
-        REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[0][node_array_per_edge.sizeOfArrays()], AssertError);
-        REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[0][node_array_per_edge.sizeOfArrays()] = 2,
-                          AssertError);
+          FaceArrayPerNode<int> face_array_per_node{connectivity, 5};
+          {
+            NodeId invalid_node_id = connectivity.numberOfNodes();
+            REQUIRE_THROWS_AS(face_array_per_node(invalid_node_id, 0), AssertError);
+          }
+          if (connectivity.numberOfNodes() > 0) {
+            NodeId node_id         = 0;
+            const auto& node_table = face_array_per_node.itemTable(node_id);
+            REQUIRE_THROWS_AS(face_array_per_node(node_id, node_table.numberOfRows()), AssertError);
+            REQUIRE_THROWS_AS(node_table[node_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[node_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[0][face_array_per_node.sizeOfArrays()],
+                              AssertError);
+            REQUIRE_THROWS_AS(face_array_per_node.itemTable(node_id)[0][face_array_per_node.sizeOfArrays()] = 2,
+                              AssertError);
+          }
+
+          EdgeArrayPerCell<int> edge_array_per_cell{connectivity, 3};
+          {
+            CellId invalid_cell_id = connectivity.numberOfCells();
+            REQUIRE_THROWS_AS(edge_array_per_cell(invalid_cell_id, 0), AssertError);
+          }
+          if (connectivity.numberOfCells() > 0) {
+            CellId cell_id         = 0;
+            const auto& cell_table = edge_array_per_cell.itemTable(cell_id);
+            REQUIRE_THROWS_AS(edge_array_per_cell(cell_id, cell_table.numberOfRows()), AssertError);
+            REQUIRE_THROWS_AS(cell_table[cell_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[cell_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[0][edge_array_per_cell.sizeOfArrays()],
+                              AssertError);
+            REQUIRE_THROWS_AS(edge_array_per_cell.itemTable(cell_id)[0][edge_array_per_cell.sizeOfArrays()] == 2,
+                              AssertError);
+          }
+
+          NodeArrayPerEdge<int> node_array_per_edge{connectivity, 3};
+          {
+            EdgeId invalid_edge_id = connectivity.numberOfEdges();
+            REQUIRE_THROWS_AS(node_array_per_edge(invalid_edge_id, 0), AssertError);
+          }
+          if (connectivity.numberOfEdges() > 0) {
+            EdgeId edge_id         = 0;
+            const auto& edge_table = node_array_per_edge.itemTable(edge_id);
+            REQUIRE_THROWS_AS(node_array_per_edge(edge_id, edge_table.numberOfRows()), AssertError);
+            REQUIRE_THROWS_AS(edge_table[edge_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[edge_table.numberOfRows()], AssertError);
+            REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[0][node_array_per_edge.sizeOfArrays()],
+                              AssertError);
+            REQUIRE_THROWS_AS(node_array_per_edge.itemTable(edge_id)[0][node_array_per_edge.sizeOfArrays()] = 2,
+                              AssertError);
+          }
+        }
       }
     }
   }
diff --git a/tests/test_SubItemValuePerItem.cpp b/tests/test_SubItemValuePerItem.cpp
index f9b71010b411c3a53b968703202882f1d4fd0df1..4063133a30f71e2304d865fb1250e975470c7ed0 100644
--- a/tests/test_SubItemValuePerItem.cpp
+++ b/tests/test_SubItemValuePerItem.cpp
@@ -64,660 +64,725 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
 
     SECTION("1D")
     {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("per cell")
-      {
-        NodeValuePerCell<int> node_value_per_cell{connectivity};
-        REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
-
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &=
-              (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id)) and
-              (node_value_per_cell.itemValues(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        const NodeValuePerCell<const int> const_node_value_per_cell = node_value_per_cell;
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &=
-              (const_node_value_per_cell.itemValues(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        EdgeValuePerCell<int> edge_value_per_cell{connectivity};
-        REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
-
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
-
-        FaceValuePerCell<int> face_value_per_cell{connectivity};
-        REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
-
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+          auto mesh_1d = named_mesh.mesh();
+
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeValuePerCell<int> node_value_per_cell{connectivity};
+            REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &=
+                  (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id)) and
+                  (node_value_per_cell.itemValues(cell_id).size() == node_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            const NodeValuePerCell<const int> const_node_value_per_cell = node_value_per_cell;
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (const_node_value_per_cell.itemValues(cell_id).size() ==
+                               node_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+            REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerCell<int> face_value_per_cell{connectivity};
+            REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per face")
+          {
+            CellValuePerFace<int> cell_value_per_face{connectivity};
+            REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per edge")
+          {
+            CellValuePerEdge<int> cell_value_per_edge{connectivity};
+            REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per node")
+          {
+            CellValuePerNode<int> cell_value_per_node{connectivity};
+            REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellValuePerFace<int> cell_value_per_face{connectivity};
-        REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
-
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
-          }
-          REQUIRE(is_correct);
         }
       }
+    }
 
-      SECTION("per edge")
-      {
-        CellValuePerEdge<int> cell_value_per_edge{connectivity};
-        REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+    SECTION("2D")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+          auto mesh_2d = named_mesh.mesh();
+
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeValuePerCell<int> node_value_per_cell{connectivity};
+            REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+            REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerCell<int> face_value_per_cell{connectivity};
+            REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per face")
+          {
+            CellValuePerFace<int> cell_value_per_face{connectivity};
+            REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            NodeValuePerFace<int> node_value_per_face{connectivity};
+            REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
+
+            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per edge")
+          {
+            CellValuePerEdge<int> cell_value_per_edge{connectivity};
+            REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            NodeValuePerEdge<int> node_value_per_edge{connectivity};
+            REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
+
+            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per node")
+          {
+            EdgeValuePerNode<int> edge_value_per_node{connectivity};
+            REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
+
+            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerNode<int> face_value_per_node{connectivity};
+            REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
+
+            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            CellValuePerNode<int> cell_value_per_node{connectivity};
+            REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
+    }
 
-      SECTION("per node")
-      {
-        CellValuePerNode<int> cell_value_per_node{connectivity};
-        REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+    SECTION("3D")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+          auto mesh_3d = named_mesh.mesh();
+
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
+
+          SECTION("per cell")
+          {
+            NodeValuePerCell<int> node_value_per_cell{connectivity};
+            REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+
+            auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+            REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+
+            auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerCell<int> face_value_per_cell{connectivity};
+            REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
+            REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
+
+            auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+                is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per face")
+          {
+            CellValuePerFace<int> cell_value_per_face{connectivity};
+            REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+
+            auto face_to_cell_matrix = connectivity.faceToCellMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            EdgeValuePerFace<int> edge_value_per_face{connectivity};
+            REQUIRE(edge_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(edge_value_per_face.numberOfValues() == number_of_values(edge_value_per_face));
+
+            auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_edge_matrix[face_id].size() == edge_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            NodeValuePerFace<int> node_value_per_face{connectivity};
+            REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
+            REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
+
+            auto face_to_node_matrix = connectivity.faceToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+                is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per edge")
+          {
+            CellValuePerEdge<int> cell_value_per_edge{connectivity};
+            REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+
+            auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerEdge<int> face_value_per_edge{connectivity};
+            REQUIRE(face_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(face_value_per_edge.numberOfValues() == number_of_values(face_value_per_edge));
+
+            auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_face_matrix[edge_id].size() == face_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            NodeValuePerEdge<int> node_value_per_edge{connectivity};
+            REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
+            REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
+
+            auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
+            {
+              bool is_correct = true;
+              for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
+                is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
+              }
+              REQUIRE(is_correct);
+            }
+          }
+
+          SECTION("per node")
+          {
+            EdgeValuePerNode<int> edge_value_per_node{connectivity};
+            REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
+
+            auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            FaceValuePerNode<int> face_value_per_node{connectivity};
+            REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
+
+            auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
+
+            CellValuePerNode<int> cell_value_per_node{connectivity};
+            REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
+            REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
+
+            auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
+            {
+              bool is_correct = true;
+              for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+                is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+              }
+              REQUIRE(is_correct);
+            }
           }
-          REQUIRE(is_correct);
         }
       }
     }
+  }
 
-    SECTION("2D")
+  SECTION("array view")
+  {
+    SECTION("1D")
     {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all1DMeshes();
 
-      SECTION("per cell")
-      {
-        NodeValuePerCell<int> node_value_per_cell{connectivity};
-        REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
-
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
+          auto mesh_1d = named_mesh.mesh();
 
-        EdgeValuePerCell<int> edge_value_per_cell{connectivity};
-        REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+          const Connectivity<1>& connectivity = mesh_1d->connectivity();
 
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+          EdgeValuePerCell<size_t> edge_values_per_cell{connectivity};
+          {
+            size_t value = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge) {
+                edge_values_per_cell(cell_id, i_edge) = value++;
+              }
+            }
           }
-          REQUIRE(is_correct);
-        }
-
-        FaceValuePerCell<int> face_value_per_cell{connectivity};
-        REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
-
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
+              is_same &= (edge_values_per_cell[i] == i);
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per face")
-      {
-        CellValuePerFace<int> cell_value_per_face{connectivity};
-        REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
 
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
+          for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
+            edge_values_per_cell[i] = i * i + 1;
           }
-          REQUIRE(is_correct);
-        }
-
-        NodeValuePerFace<int> node_value_per_face{connectivity};
-        REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
-
-        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge, ++i) {
+                is_same &= (edge_values_per_cell(cell_id, i_edge) == i * i + 1);
+              }
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
         }
       }
+    }
 
-      SECTION("per edge")
-      {
-        CellValuePerEdge<int> cell_value_per_edge{connectivity};
-        REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
+    SECTION("2D")
+    {
+      std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
+          auto mesh_2d = named_mesh.mesh();
 
-        NodeValuePerEdge<int> node_value_per_edge{connectivity};
-        REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
+          const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
+          CellValuePerFace<size_t> cell_values_per_face{connectivity};
+          {
+            size_t value = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell) {
+                cell_values_per_face(face_id, i_cell) = value++;
+              }
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
-
-      SECTION("per node")
-      {
-        EdgeValuePerNode<int> edge_value_per_node{connectivity};
-        REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
-
-        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
+              is_same &= (cell_values_per_face[i] == i);
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
-        }
-
-        FaceValuePerNode<int> face_value_per_node{connectivity};
-        REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
-
-        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
+          for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
+            cell_values_per_face[i] = 3 * i + 1;
           }
-          REQUIRE(is_correct);
-        }
-
-        CellValuePerNode<int> cell_value_per_node{connectivity};
-        REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
-
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
+              for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell, ++i) {
+                is_same &= (cell_values_per_face(face_id, i_cell) == 3 * i + 1);
+              }
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
         }
       }
     }
 
     SECTION("3D")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-      SECTION("per cell")
-      {
-        NodeValuePerCell<int> node_value_per_cell{connectivity};
-        REQUIRE(node_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(node_value_per_cell.numberOfValues() == number_of_values(node_value_per_cell));
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        auto cell_to_node_matrix = connectivity.cellToNodeMatrix();
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
         {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_node_matrix[cell_id].size() == node_value_per_cell.numberOfSubValues(cell_id));
-          }
-          REQUIRE(is_correct);
-        }
+          auto mesh_3d = named_mesh.mesh();
 
-        EdgeValuePerCell<int> edge_value_per_cell{connectivity};
-        REQUIRE(edge_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(edge_value_per_cell.numberOfValues() == number_of_values(edge_value_per_cell));
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-        auto cell_to_edge_matrix = connectivity.cellToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_edge_matrix[cell_id].size() == edge_value_per_cell.numberOfSubValues(cell_id));
+          FaceValuePerNode<size_t> face_values_per_node{connectivity};
+          {
+            size_t value = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face) {
+                face_values_per_node.itemValues(node_id)[i_face] = value++;
+              }
+            }
+          }
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
+              is_same &= (face_values_per_node[i] == i);
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
-        }
-
-        FaceValuePerCell<int> face_value_per_cell{connectivity};
-        REQUIRE(face_value_per_cell.numberOfItems() == connectivity.numberOfCells());
-        REQUIRE(face_value_per_cell.numberOfValues() == number_of_values(face_value_per_cell));
 
-        auto cell_to_face_matrix = connectivity.cellToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-            is_correct &= (cell_to_face_matrix[cell_id].size() == face_value_per_cell.numberOfSubValues(cell_id));
+          for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
+            face_values_per_node[i] = 3 + i * i;
+          }
+          {
+            bool is_same = true;
+            size_t i     = 0;
+            for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
+              for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face, ++i) {
+                is_same &= (face_values_per_node.itemValues(node_id)[i_face] == 3 + i * i);
+              }
+            }
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
         }
       }
+    }
+  }
 
-      SECTION("per face")
-      {
-        CellValuePerFace<int> cell_value_per_face{connectivity};
-        REQUIRE(cell_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(cell_value_per_face.numberOfValues() == number_of_values(cell_value_per_face));
+  SECTION("copy")
+  {
+    std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-        auto face_to_cell_matrix = connectivity.faceToCellMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_cell_matrix[face_id].size() == cell_value_per_face.numberOfSubValues(face_id));
-          }
-          REQUIRE(is_correct);
-        }
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
+      {
+        auto mesh_3d = named_mesh.mesh();
 
-        EdgeValuePerFace<int> edge_value_per_face{connectivity};
-        REQUIRE(edge_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(edge_value_per_face.numberOfValues() == number_of_values(edge_value_per_face));
+        const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-        auto face_to_edge_matrix = connectivity.faceToEdgeMatrix();
+        SECTION("classic")
         {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_edge_matrix[face_id].size() == edge_value_per_face.numberOfSubValues(face_id));
-          }
-          REQUIRE(is_correct);
-        }
+          NodeValuePerCell<size_t> node_value_per_cell{connectivity};
 
-        NodeValuePerFace<int> node_value_per_face{connectivity};
-        REQUIRE(node_value_per_face.numberOfItems() == connectivity.numberOfFaces());
-        REQUIRE(node_value_per_face.numberOfValues() == number_of_values(node_value_per_face));
-
-        auto face_to_node_matrix = connectivity.faceToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-            is_correct &= (face_to_node_matrix[face_id].size() == node_value_per_face.numberOfSubValues(face_id));
+          {
+            size_t value = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+                node_value_per_cell.itemValues(cell_id)[i_node] = value++;
+              }
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
 
-      SECTION("per edge")
-      {
-        CellValuePerEdge<int> cell_value_per_edge{connectivity};
-        REQUIRE(cell_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(cell_value_per_edge.numberOfValues() == number_of_values(cell_value_per_edge));
-
-        auto edge_to_cell_matrix = connectivity.edgeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_cell_matrix[edge_id].size() == cell_value_per_edge.numberOfSubValues(edge_id));
-          }
-          REQUIRE(is_correct);
-        }
+          NodeValuePerCell<size_t> copy_node_value_per_cell = copy(node_value_per_cell);
 
-        FaceValuePerEdge<int> face_value_per_edge{connectivity};
-        REQUIRE(face_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(face_value_per_edge.numberOfValues() == number_of_values(face_value_per_edge));
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+            }
 
-        auto edge_to_face_matrix = connectivity.edgeToFaceMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_face_matrix[edge_id].size() == face_value_per_edge.numberOfSubValues(edge_id));
+            REQUIRE(is_same);
           }
-          REQUIRE(is_correct);
-        }
-
-        NodeValuePerEdge<int> node_value_per_edge{connectivity};
-        REQUIRE(node_value_per_edge.numberOfItems() == connectivity.numberOfEdges());
-        REQUIRE(node_value_per_edge.numberOfValues() == number_of_values(node_value_per_edge));
 
-        auto edge_to_node_matrix = connectivity.edgeToNodeMatrix();
-        {
-          bool is_correct = true;
-          for (EdgeId edge_id = 0; edge_id < connectivity.numberOfEdges(); ++edge_id) {
-            is_correct &= (edge_to_node_matrix[edge_id].size() == node_value_per_edge.numberOfSubValues(edge_id));
+          {
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+                node_value_per_cell.itemValues(cell_id)[i_node] = i_node;
+              }
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
 
-      SECTION("per node")
-      {
-        EdgeValuePerNode<int> edge_value_per_node{connectivity};
-        REQUIRE(edge_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(edge_value_per_node.numberOfValues() == number_of_values(edge_value_per_node));
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+            }
 
-        auto node_to_edge_matrix = connectivity.nodeToEdgeMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_edge_matrix[node_id].size() == edge_value_per_node.numberOfSubValues(node_id));
+            REQUIRE(not is_same);
           }
-          REQUIRE(is_correct);
         }
 
-        FaceValuePerNode<int> face_value_per_node{connectivity};
-        REQUIRE(face_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(face_value_per_node.numberOfValues() == number_of_values(face_value_per_node));
-
-        auto node_to_face_matrix = connectivity.nodeToFaceMatrix();
+        SECTION("from weak")
         {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_face_matrix[node_id].size() == face_value_per_node.numberOfSubValues(node_id));
-          }
-          REQUIRE(is_correct);
-        }
+          WeakNodeValuePerCell<size_t> node_value_per_cell{connectivity};
 
-        CellValuePerNode<int> cell_value_per_node{connectivity};
-        REQUIRE(cell_value_per_node.numberOfItems() == connectivity.numberOfNodes());
-        REQUIRE(cell_value_per_node.numberOfValues() == number_of_values(cell_value_per_node));
-
-        auto node_to_cell_matrix = connectivity.nodeToCellMatrix();
-        {
-          bool is_correct = true;
-          for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-            is_correct &= (node_to_cell_matrix[node_id].size() == cell_value_per_node.numberOfSubValues(node_id));
+          {
+            size_t value = 0;
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+                node_value_per_cell.itemValues(cell_id)[i_node] = value++;
+              }
+            }
           }
-          REQUIRE(is_correct);
-        }
-      }
-    }
-  }
 
-  SECTION("array view")
-  {
-    SECTION("1D")
-    {
-      const Mesh<Connectivity<1>>& mesh_1d = *MeshDataBaseForTests::get().cartesianMesh1D();
-      const Connectivity<1>& connectivity  = mesh_1d.connectivity();
+          WeakNodeValuePerCell<const size_t> node_const_value_per_cell = node_value_per_cell;
+          NodeValuePerCell<size_t> copy_node_value_per_cell            = copy(node_const_value_per_cell);
 
-      EdgeValuePerCell<size_t> edge_values_per_cell{connectivity};
-      {
-        size_t value = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge) {
-            edge_values_per_cell(cell_id, i_edge) = value++;
-          }
-        }
-      }
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
-          is_same &= (edge_values_per_cell[i] == i);
-        }
-        REQUIRE(is_same);
-      }
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+            }
 
-      for (size_t i = 0; i < edge_values_per_cell.numberOfValues(); ++i) {
-        edge_values_per_cell[i] = i * i + 1;
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_edge = 0; i_edge < edge_values_per_cell.numberOfSubValues(cell_id); ++i_edge, ++i) {
-            is_same &= (edge_values_per_cell(cell_id, i_edge) == i * i + 1);
+            REQUIRE(is_same);
           }
-        }
-        REQUIRE(is_same);
-      }
-    }
-
-    SECTION("2D")
-    {
-      const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-      const Connectivity<2>& connectivity  = mesh_2d.connectivity();
 
-      CellValuePerFace<size_t> cell_values_per_face{connectivity};
-      {
-        size_t value = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell) {
-            cell_values_per_face(face_id, i_cell) = value++;
-          }
-        }
-      }
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
-          is_same &= (cell_values_per_face[i] == i);
-        }
-        REQUIRE(is_same);
-      }
-      for (size_t i = 0; i < cell_values_per_face.numberOfValues(); ++i) {
-        cell_values_per_face[i] = 3 * i + 1;
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (FaceId face_id = 0; face_id < connectivity.numberOfFaces(); ++face_id) {
-          for (size_t i_cell = 0; i_cell < cell_values_per_face.numberOfSubValues(face_id); ++i_cell, ++i) {
-            is_same &= (cell_values_per_face(face_id, i_cell) == 3 * i + 1);
+          {
+            for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
+              for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
+                node_value_per_cell.itemValues(cell_id)[i_node] = i_node;
+              }
+            }
           }
-        }
-        REQUIRE(is_same);
-      }
-    }
 
-    SECTION("3D")
-    {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+          {
+            bool is_same = true;
+            for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
+              is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+            }
 
-      FaceValuePerNode<size_t> face_values_per_node{connectivity};
-      {
-        size_t value = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face) {
-            face_values_per_node.itemValues(node_id)[i_face] = value++;
+            REQUIRE(not is_same);
           }
         }
       }
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
-          is_same &= (face_values_per_node[i] == i);
-        }
-        REQUIRE(is_same);
-      }
-
-      for (size_t i = 0; i < face_values_per_node.numberOfValues(); ++i) {
-        face_values_per_node[i] = 3 + i * i;
-      }
-      {
-        bool is_same = true;
-        size_t i     = 0;
-        for (NodeId node_id = 0; node_id < connectivity.numberOfNodes(); ++node_id) {
-          for (size_t i_face = 0; i_face < face_values_per_node.numberOfSubValues(node_id); ++i_face, ++i) {
-            is_same &= (face_values_per_node.itemValues(node_id)[i_face] == 3 + i * i);
-          }
-        }
-        REQUIRE(is_same);
-      }
     }
   }
 
-  SECTION("copy")
+  SECTION("WeakSubItemValuePerItem")
   {
-    const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-    const Connectivity<3>& connectivity  = mesh_3d.connectivity();
-
-    SECTION("classic")
-    {
-      NodeValuePerCell<size_t> node_value_per_cell{connectivity};
+    std::array mesh_list = MeshDataBaseForTests::get().all2DMeshes();
 
+    for (auto named_mesh : mesh_list) {
+      SECTION(named_mesh.name())
       {
-        size_t value = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
-            node_value_per_cell.itemValues(cell_id)[i_node] = value++;
-          }
-        }
-      }
-
-      NodeValuePerCell<size_t> copy_node_value_per_cell = copy(node_value_per_cell);
+        auto mesh_2d = named_mesh.mesh();
 
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
-          is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
-        }
-
-        REQUIRE(is_same);
-      }
+        const Connectivity<2>& connectivity = mesh_2d->connectivity();
 
-      {
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
-            node_value_per_cell.itemValues(cell_id)[i_node] = i_node;
-          }
-        }
-      }
+        WeakFaceValuePerCell<int> weak_face_value_per_cell{connectivity};
 
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
-          is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+        for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
+          weak_face_value_per_cell[i] = i;
         }
 
-        REQUIRE(not is_same);
-      }
-    }
-
-    SECTION("from weak")
-    {
-      WeakNodeValuePerCell<size_t> node_value_per_cell{connectivity};
-
-      {
-        size_t value = 0;
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
-            node_value_per_cell.itemValues(cell_id)[i_node] = value++;
-          }
-        }
-      }
+        FaceValuePerCell<const int> face_value_per_cell{weak_face_value_per_cell};
 
-      NodeValuePerCell<size_t> copy_node_value_per_cell = copy(node_value_per_cell);
+        REQUIRE(face_value_per_cell.connectivity_ptr() == weak_face_value_per_cell.connectivity_ptr());
 
-      {
         bool is_same = true;
-        for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
-          is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
+        for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
+          is_same &= (face_value_per_cell[i] == weak_face_value_per_cell[i]);
         }
-
         REQUIRE(is_same);
       }
-
-      {
-        for (CellId cell_id = 0; cell_id < connectivity.numberOfCells(); ++cell_id) {
-          for (size_t i_node = 0; i_node < node_value_per_cell.numberOfSubValues(cell_id); ++i_node) {
-            node_value_per_cell.itemValues(cell_id)[i_node] = i_node;
-          }
-        }
-      }
-
-      {
-        bool is_same = true;
-        for (size_t i = 0; i < copy_node_value_per_cell.numberOfValues(); ++i) {
-          is_same &= (copy_node_value_per_cell[i] == node_value_per_cell[i]);
-        }
-
-        REQUIRE(not is_same);
-      }
     }
   }
 
-  SECTION("WeakSubItemValuePerItem")
-  {
-    const Mesh<Connectivity<2>>& mesh_2d = *MeshDataBaseForTests::get().cartesianMesh2D();
-    const Connectivity<2>& connectivity  = mesh_2d.connectivity();
-
-    WeakFaceValuePerCell<int> weak_face_value_per_cell{connectivity};
-
-    for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
-      weak_face_value_per_cell[i] = i;
-    }
-
-    FaceValuePerCell<const int> face_value_per_cell{weak_face_value_per_cell};
-
-    REQUIRE(face_value_per_cell.connectivity_ptr() == weak_face_value_per_cell.connectivity_ptr());
-
-    bool is_same = true;
-    for (size_t i = 0; i < weak_face_value_per_cell.numberOfValues(); ++i) {
-      is_same &= (face_value_per_cell[i] == weak_face_value_per_cell[i]);
-    }
-    REQUIRE(is_same);
-  }
-
 #ifndef NDEBUG
   SECTION("error")
   {
@@ -758,59 +823,67 @@ TEST_CASE("SubItemValuePerItem", "[mesh]")
 
     SECTION("checking for bounds violation")
     {
-      const Mesh<Connectivity<3>>& mesh_3d = *MeshDataBaseForTests::get().cartesianMesh3D();
-      const Connectivity<3>& connectivity  = mesh_3d.connectivity();
+      std::array mesh_list = MeshDataBaseForTests::get().all3DMeshes();
 
-      CellValuePerFace<int> cell_value_per_face{connectivity};
-      {
-        FaceId invalid_face_id = connectivity.numberOfFaces();
-        REQUIRE_THROWS_AS(cell_value_per_face(invalid_face_id, 0), AssertError);
-      }
-      if (connectivity.numberOfFaces() > 0) {
-        FaceId face_id          = 0;
-        const auto& cell_values = cell_value_per_face.itemValues(face_id);
-        REQUIRE_THROWS_AS(cell_value_per_face(face_id, cell_values.size()), AssertError);
-        REQUIRE_THROWS_AS(cell_values[cell_values.size()], AssertError);
-        REQUIRE_THROWS_AS(cell_value_per_face.itemValues(face_id)[cell_values.size()] = 2, AssertError);
-      }
+      for (auto named_mesh : mesh_list) {
+        SECTION(named_mesh.name())
+        {
+          auto mesh_3d = named_mesh.mesh();
 
-      FaceValuePerNode<int> face_value_per_node{connectivity};
-      {
-        NodeId invalid_node_id = connectivity.numberOfNodes();
-        REQUIRE_THROWS_AS(face_value_per_node(invalid_node_id, 0), AssertError);
-      }
-      if (connectivity.numberOfNodes() > 0) {
-        NodeId node_id          = 0;
-        const auto& face_values = face_value_per_node.itemValues(node_id);
-        REQUIRE_THROWS_AS(face_value_per_node(node_id, face_values.size()), AssertError);
-        REQUIRE_THROWS_AS(face_values[face_values.size()], AssertError);
-        REQUIRE_THROWS_AS(face_value_per_node.itemValues(node_id)[face_values.size()] = 2, AssertError);
-      }
+          const Connectivity<3>& connectivity = mesh_3d->connectivity();
 
-      EdgeValuePerCell<int> edge_value_per_cell{connectivity};
-      {
-        CellId invalid_cell_id = connectivity.numberOfCells();
-        REQUIRE_THROWS_AS(edge_value_per_cell(invalid_cell_id, 0), AssertError);
-      }
-      if (connectivity.numberOfCells() > 0) {
-        CellId cell_id          = 0;
-        const auto& edge_values = edge_value_per_cell.itemValues(cell_id);
-        REQUIRE_THROWS_AS(edge_value_per_cell(cell_id, edge_values.size()), AssertError);
-        REQUIRE_THROWS_AS(edge_values[edge_values.size()], AssertError);
-        REQUIRE_THROWS_AS(edge_value_per_cell.itemValues(cell_id)[edge_values.size()] = 2, AssertError);
-      }
+          CellValuePerFace<int> cell_value_per_face{connectivity};
+          {
+            FaceId invalid_face_id = connectivity.numberOfFaces();
+            REQUIRE_THROWS_AS(cell_value_per_face(invalid_face_id, 0), AssertError);
+          }
+          if (connectivity.numberOfFaces() > 0) {
+            FaceId face_id          = 0;
+            const auto& cell_values = cell_value_per_face.itemValues(face_id);
+            REQUIRE_THROWS_AS(cell_value_per_face(face_id, cell_values.size()), AssertError);
+            REQUIRE_THROWS_AS(cell_values[cell_values.size()], AssertError);
+            REQUIRE_THROWS_AS(cell_value_per_face.itemValues(face_id)[cell_values.size()] = 2, AssertError);
+          }
 
-      NodeValuePerEdge<int> node_value_per_edge{connectivity};
-      {
-        EdgeId invalid_edge_id = connectivity.numberOfEdges();
-        REQUIRE_THROWS_AS(node_value_per_edge(invalid_edge_id, 0), AssertError);
-      }
-      if (connectivity.numberOfEdges() > 0) {
-        EdgeId edge_id          = 0;
-        const auto& node_values = node_value_per_edge.itemValues(edge_id);
-        REQUIRE_THROWS_AS(node_value_per_edge(edge_id, node_values.size()), AssertError);
-        REQUIRE_THROWS_AS(node_values[node_values.size()], AssertError);
-        REQUIRE_THROWS_AS(node_value_per_edge.itemValues(edge_id)[node_values.size()] = 2, AssertError);
+          FaceValuePerNode<int> face_value_per_node{connectivity};
+          {
+            NodeId invalid_node_id = connectivity.numberOfNodes();
+            REQUIRE_THROWS_AS(face_value_per_node(invalid_node_id, 0), AssertError);
+          }
+          if (connectivity.numberOfNodes() > 0) {
+            NodeId node_id          = 0;
+            const auto& face_values = face_value_per_node.itemValues(node_id);
+            REQUIRE_THROWS_AS(face_value_per_node(node_id, face_values.size()), AssertError);
+            REQUIRE_THROWS_AS(face_values[face_values.size()], AssertError);
+            REQUIRE_THROWS_AS(face_value_per_node.itemValues(node_id)[face_values.size()] = 2, AssertError);
+          }
+
+          EdgeValuePerCell<int> edge_value_per_cell{connectivity};
+          {
+            CellId invalid_cell_id = connectivity.numberOfCells();
+            REQUIRE_THROWS_AS(edge_value_per_cell(invalid_cell_id, 0), AssertError);
+          }
+          if (connectivity.numberOfCells() > 0) {
+            CellId cell_id          = 0;
+            const auto& edge_values = edge_value_per_cell.itemValues(cell_id);
+            REQUIRE_THROWS_AS(edge_value_per_cell(cell_id, edge_values.size()), AssertError);
+            REQUIRE_THROWS_AS(edge_values[edge_values.size()], AssertError);
+            REQUIRE_THROWS_AS(edge_value_per_cell.itemValues(cell_id)[edge_values.size()] = 2, AssertError);
+          }
+
+          NodeValuePerEdge<int> node_value_per_edge{connectivity};
+          {
+            EdgeId invalid_edge_id = connectivity.numberOfEdges();
+            REQUIRE_THROWS_AS(node_value_per_edge(invalid_edge_id, 0), AssertError);
+          }
+          if (connectivity.numberOfEdges() > 0) {
+            EdgeId edge_id          = 0;
+            const auto& node_values = node_value_per_edge.itemValues(edge_id);
+            REQUIRE_THROWS_AS(node_value_per_edge(edge_id, node_values.size()), AssertError);
+            REQUIRE_THROWS_AS(node_values[node_values.size()], AssertError);
+            REQUIRE_THROWS_AS(node_value_per_edge.itemValues(edge_id)[node_values.size()] = 2, AssertError);
+          }
+        }
       }
     }
   }
diff --git a/tests/test_Synchronizer.cpp b/tests/test_Synchronizer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6a4a9b52a6ce05a4a0b2758c868a49dc758d3e3d
--- /dev/null
+++ b/tests/test_Synchronizer.cpp
@@ -0,0 +1,450 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <MeshDataBaseForTests.hpp>
+#include <mesh/Connectivity.hpp>
+#include <mesh/Mesh.hpp>
+#include <mesh/Synchronizer.hpp>
+#include <utils/pugs_config.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("Synchronizer", "[mesh]")
+{
+  auto is_same_item_value = [](auto a, auto b) {
+    using IndexT = typename decltype(a)::index_type;
+    bool is_same = true;
+    for (IndexT i = 0; i < a.numberOfItems(); ++i) {
+      is_same &= (a[i] == b[i]);
+    }
+    return parallel::allReduceAnd(is_same);
+  };
+
+  auto is_same_item_array = [](auto a, auto b) {
+    using IndexT = typename decltype(a)::index_type;
+    bool is_same = true;
+    for (IndexT i = 0; i < a.numberOfItems(); ++i) {
+      for (size_t j = 0; j < a.sizeOfArrays(); ++j) {
+        is_same &= (a[i][j] == b[i][j]);
+      }
+    }
+    return parallel::allReduceAnd(is_same);
+  };
+
+  SECTION("1D")
+  {
+    constexpr size_t Dimension = 1;
+    using ConnectivityType     = Connectivity<Dimension>;
+
+    const ConnectivityType& connectivity = MeshDataBaseForTests::get().unordered1DMesh()->connectivity();
+
+    SECTION("synchonize NodeValue")
+    {
+      const auto node_owner  = connectivity.nodeOwner();
+      const auto node_number = connectivity.nodeNumber();
+
+      NodeValue<int> node_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfNodes(),
+        PUGS_LAMBDA(const NodeId node_id) { node_value_ref[node_id] = node_owner[node_id] + node_number[node_id]; });
+
+      NodeValue<int> node_value{connectivity};
+      parallel_for(
+        connectivity.numberOfNodes(),
+        PUGS_LAMBDA(const NodeId node_id) { node_value[node_id] = parallel::rank() + node_number[node_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(node_value, node_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(node_value);
+
+      REQUIRE(is_same_item_value(node_value, node_value_ref));
+    }
+
+    SECTION("synchonize EdgeValue")
+    {
+      const auto edge_owner  = connectivity.edgeOwner();
+      const auto edge_number = connectivity.edgeNumber();
+
+      EdgeValue<int> edge_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfEdges(),
+        PUGS_LAMBDA(const EdgeId edge_id) { edge_value_ref[edge_id] = edge_owner[edge_id] + edge_number[edge_id]; });
+
+      EdgeValue<int> edge_value{connectivity};
+      parallel_for(
+        connectivity.numberOfEdges(),
+        PUGS_LAMBDA(const EdgeId edge_id) { edge_value[edge_id] = parallel::rank() + edge_number[edge_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(edge_value, edge_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(edge_value);
+
+      REQUIRE(is_same_item_value(edge_value, edge_value_ref));
+    }
+
+    SECTION("synchonize FaceValue")
+    {
+      const auto face_owner  = connectivity.faceOwner();
+      const auto face_number = connectivity.faceNumber();
+
+      FaceValue<int> face_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfFaces(),
+        PUGS_LAMBDA(const FaceId face_id) { face_value_ref[face_id] = face_owner[face_id] + face_number[face_id]; });
+
+      FaceValue<int> face_value{connectivity};
+      parallel_for(
+        connectivity.numberOfFaces(),
+        PUGS_LAMBDA(const FaceId face_id) { face_value[face_id] = parallel::rank() + face_number[face_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(face_value, face_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(face_value);
+
+      REQUIRE(is_same_item_value(face_value, face_value_ref));
+    }
+
+    SECTION("synchonize CellValue")
+    {
+      const auto cell_owner  = connectivity.cellOwner();
+      const auto cell_number = connectivity.cellNumber();
+
+      CellValue<int> cell_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfCells(),
+        PUGS_LAMBDA(const CellId cell_id) { cell_value_ref[cell_id] = cell_owner[cell_id] + cell_number[cell_id]; });
+
+      CellValue<int> cell_value{connectivity};
+      parallel_for(
+        connectivity.numberOfCells(),
+        PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = parallel::rank() + cell_number[cell_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(cell_value, cell_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(cell_value);
+
+      REQUIRE(is_same_item_value(cell_value, cell_value_ref));
+    }
+
+    SECTION("synchonize CellArray")
+    {
+      const auto cell_owner  = connectivity.cellOwner();
+      const auto cell_number = connectivity.cellNumber();
+
+      CellArray<int> cell_array_ref{connectivity, 3};
+      parallel_for(
+        connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+          for (size_t i = 0; i < cell_array_ref.sizeOfArrays(); ++i) {
+            cell_array_ref[cell_id][i] = (i + 1) * cell_owner[cell_id] + i + cell_number[cell_id];
+          }
+        });
+
+      CellArray<int> cell_array{connectivity, 3};
+      parallel_for(
+        connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+          for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) {
+            cell_array[cell_id][i] = (i + 1) * parallel::rank() + i + cell_number[cell_id];
+          }
+        });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_array(cell_array, cell_array_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(cell_array);
+
+      REQUIRE(is_same_item_array(cell_array, cell_array_ref));
+    }
+  }
+
+  SECTION("2D")
+  {
+    constexpr size_t Dimension = 2;
+    using ConnectivityType     = Connectivity<Dimension>;
+
+    const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid2DMesh()->connectivity();
+
+    SECTION("synchonize NodeValue")
+    {
+      const auto node_owner  = connectivity.nodeOwner();
+      const auto node_number = connectivity.nodeNumber();
+
+      NodeValue<int> node_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfNodes(),
+        PUGS_LAMBDA(const NodeId node_id) { node_value_ref[node_id] = node_owner[node_id] + node_number[node_id]; });
+
+      NodeValue<int> node_value{connectivity};
+      parallel_for(
+        connectivity.numberOfNodes(),
+        PUGS_LAMBDA(const NodeId node_id) { node_value[node_id] = parallel::rank() + node_number[node_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(node_value, node_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(node_value);
+
+      REQUIRE(is_same_item_value(node_value, node_value_ref));
+    }
+
+    SECTION("synchonize EdgeValue")
+    {
+      const auto edge_owner  = connectivity.edgeOwner();
+      const auto edge_number = connectivity.edgeNumber();
+
+      EdgeValue<int> edge_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfEdges(),
+        PUGS_LAMBDA(const EdgeId edge_id) { edge_value_ref[edge_id] = edge_owner[edge_id] + edge_number[edge_id]; });
+
+      EdgeValue<int> edge_value{connectivity};
+      parallel_for(
+        connectivity.numberOfEdges(),
+        PUGS_LAMBDA(const EdgeId edge_id) { edge_value[edge_id] = parallel::rank() + edge_number[edge_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(edge_value, edge_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(edge_value);
+
+      REQUIRE(is_same_item_value(edge_value, edge_value_ref));
+    }
+
+    SECTION("synchonize FaceValue")
+    {
+      const auto face_owner  = connectivity.faceOwner();
+      const auto face_number = connectivity.faceNumber();
+
+      FaceValue<int> face_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfFaces(),
+        PUGS_LAMBDA(const FaceId face_id) { face_value_ref[face_id] = face_owner[face_id] + face_number[face_id]; });
+
+      FaceValue<int> face_value{connectivity};
+      parallel_for(
+        connectivity.numberOfFaces(),
+        PUGS_LAMBDA(const FaceId face_id) { face_value[face_id] = parallel::rank() + face_number[face_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(face_value, face_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(face_value);
+
+      REQUIRE(is_same_item_value(face_value, face_value_ref));
+    }
+
+    SECTION("synchonize CellValue")
+    {
+      const auto cell_owner  = connectivity.cellOwner();
+      const auto cell_number = connectivity.cellNumber();
+
+      CellValue<int> cell_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfCells(),
+        PUGS_LAMBDA(const CellId cell_id) { cell_value_ref[cell_id] = cell_owner[cell_id] + cell_number[cell_id]; });
+
+      CellValue<int> cell_value{connectivity};
+      parallel_for(
+        connectivity.numberOfCells(),
+        PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = parallel::rank() + cell_number[cell_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(cell_value, cell_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(cell_value);
+
+      REQUIRE(is_same_item_value(cell_value, cell_value_ref));
+    }
+
+    SECTION("synchonize CellArray")
+    {
+      const auto cell_owner  = connectivity.cellOwner();
+      const auto cell_number = connectivity.cellNumber();
+
+      CellArray<int> cell_array_ref{connectivity, 3};
+      parallel_for(
+        connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+          for (size_t i = 0; i < cell_array_ref.sizeOfArrays(); ++i) {
+            cell_array_ref[cell_id][i] = (i + 1) * cell_owner[cell_id] + i + cell_number[cell_id];
+          }
+        });
+
+      CellArray<int> cell_array{connectivity, 3};
+      parallel_for(
+        connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+          for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) {
+            cell_array[cell_id][i] = (i + 1) * parallel::rank() + i + cell_number[cell_id];
+          }
+        });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_array(cell_array, cell_array_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(cell_array);
+
+      REQUIRE(is_same_item_array(cell_array, cell_array_ref));
+    }
+  }
+
+  SECTION("3D")
+  {
+    constexpr size_t Dimension = 3;
+    using ConnectivityType     = Connectivity<Dimension>;
+
+    const ConnectivityType& connectivity = MeshDataBaseForTests::get().hybrid3DMesh()->connectivity();
+
+    SECTION("synchonize NodeValue")
+    {
+      const auto node_owner  = connectivity.nodeOwner();
+      const auto node_number = connectivity.nodeNumber();
+
+      NodeValue<int> node_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfNodes(),
+        PUGS_LAMBDA(const NodeId node_id) { node_value_ref[node_id] = node_owner[node_id] + node_number[node_id]; });
+
+      NodeValue<int> node_value{connectivity};
+      parallel_for(
+        connectivity.numberOfNodes(),
+        PUGS_LAMBDA(const NodeId node_id) { node_value[node_id] = parallel::rank() + node_number[node_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(node_value, node_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(node_value);
+
+      REQUIRE(is_same_item_value(node_value, node_value_ref));
+    }
+
+    SECTION("synchonize EdgeValue")
+    {
+      const auto edge_owner  = connectivity.edgeOwner();
+      const auto edge_number = connectivity.edgeNumber();
+
+      EdgeValue<int> edge_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfEdges(),
+        PUGS_LAMBDA(const EdgeId edge_id) { edge_value_ref[edge_id] = edge_owner[edge_id] + edge_number[edge_id]; });
+
+      EdgeValue<int> edge_value{connectivity};
+      parallel_for(
+        connectivity.numberOfEdges(),
+        PUGS_LAMBDA(const EdgeId edge_id) { edge_value[edge_id] = parallel::rank() + edge_number[edge_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(edge_value, edge_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(edge_value);
+
+      REQUIRE(is_same_item_value(edge_value, edge_value_ref));
+    }
+
+    SECTION("synchonize FaceValue")
+    {
+      const auto face_owner  = connectivity.faceOwner();
+      const auto face_number = connectivity.faceNumber();
+
+      FaceValue<int> face_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfFaces(),
+        PUGS_LAMBDA(const FaceId face_id) { face_value_ref[face_id] = face_owner[face_id] + face_number[face_id]; });
+
+      FaceValue<int> face_value{connectivity};
+      parallel_for(
+        connectivity.numberOfFaces(),
+        PUGS_LAMBDA(const FaceId face_id) { face_value[face_id] = parallel::rank() + face_number[face_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(face_value, face_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(face_value);
+
+      REQUIRE(is_same_item_value(face_value, face_value_ref));
+    }
+
+    SECTION("synchonize CellValue")
+    {
+      const auto cell_owner  = connectivity.cellOwner();
+      const auto cell_number = connectivity.cellNumber();
+
+      CellValue<int> cell_value_ref{connectivity};
+      parallel_for(
+        connectivity.numberOfCells(),
+        PUGS_LAMBDA(const CellId cell_id) { cell_value_ref[cell_id] = cell_owner[cell_id] + cell_number[cell_id]; });
+
+      CellValue<int> cell_value{connectivity};
+      parallel_for(
+        connectivity.numberOfCells(),
+        PUGS_LAMBDA(const CellId cell_id) { cell_value[cell_id] = parallel::rank() + cell_number[cell_id]; });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_value(cell_value, cell_value_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(cell_value);
+
+      REQUIRE(is_same_item_value(cell_value, cell_value_ref));
+    }
+
+    SECTION("synchonize CellArray")
+    {
+      const auto cell_owner  = connectivity.cellOwner();
+      const auto cell_number = connectivity.cellNumber();
+
+      CellArray<int> cell_array_ref{connectivity, 3};
+      parallel_for(
+        connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+          for (size_t i = 0; i < cell_array_ref.sizeOfArrays(); ++i) {
+            cell_array_ref[cell_id][i] = (i + 1) * cell_owner[cell_id] + i + cell_number[cell_id];
+          }
+        });
+
+      CellArray<int> cell_array{connectivity, 3};
+      parallel_for(
+        connectivity.numberOfCells(), PUGS_LAMBDA(const CellId cell_id) {
+          for (size_t i = 0; i < cell_array.sizeOfArrays(); ++i) {
+            cell_array[cell_id][i] = (i + 1) * parallel::rank() + i + cell_number[cell_id];
+          }
+        });
+
+      if (parallel::size() > 1) {
+        REQUIRE(not is_same_item_array(cell_array, cell_array_ref));
+      }
+
+      Synchronizer synchronizer;
+      synchronizer.synchronize(cell_array);
+
+      REQUIRE(is_same_item_array(cell_array, cell_array_ref));
+    }
+  }
+}
diff --git a/tests/test_TensorialGaussLegendreQuadrature.cpp b/tests/test_TensorialGaussLegendreQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..342a4fbaa9ef8c54e274f7ad65800aee30986559
--- /dev/null
+++ b/tests/test_TensorialGaussLegendreQuadrature.cpp
@@ -0,0 +1,658 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussLegendreQuadratureDescriptor.hpp>
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <analysis/TensorialGaussLegendreQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("TensorialGaussLegendreQuadrature", "[analysis]")
+{
+  SECTION("1D")
+  {
+    auto is_symmetric_formula = [](auto quadrature_formula) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      bool is_symmetric = true;
+
+      const size_t middle_index = point_list.size() / 2;
+      for (size_t i = 0; i <= middle_index; ++i) {
+        if (point_list[i] != -point_list[point_list.size() - 1 - i]) {
+          return false;
+        }
+      }
+      for (size_t i = 0; i <= middle_index; ++i) {
+        if (weight_list[i] != weight_list[point_list.size() - 1 - i]) {
+          return false;
+        }
+      }
+
+      return is_symmetric;
+    };
+
+    auto integrate = [](auto f, auto quadrature_formula, const double a, const double b) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      double alpha = 0.5 * (b - a);
+      double beta  = 0.5 * (a + b);
+
+      auto x = [&alpha, &beta](auto x_hat) { return TinyVector<1>{alpha * x_hat[0] + beta}; };
+
+      auto value = weight_list[0] * f(x(point_list[0]));
+      for (size_t i = 1; i < weight_list.size(); ++i) {
+        value += weight_list[i] * f(x(point_list[i]));
+      }
+
+      return alpha * value;
+    };
+
+    auto get_order = [&integrate](auto f, auto quadrature_formula, const double a, const double b,
+                                  const double exact_value) {
+      double int_ab          = integrate(f, quadrature_formula, a, b);
+      double int_first_half  = integrate(f, quadrature_formula, a, 0.5 * (a + b));
+      double int_second_half = integrate(f, quadrature_formula, 0.5 * (a + b), b);
+
+      return -std::log((int_first_half + int_second_half - exact_value) / (int_ab - exact_value)) / std::log(2);
+    };
+
+    auto p0 = [](const TinyVector<1>&) { return 1; };
+    auto p1 = [&p0](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 2 * x + p0(X);
+    };
+    auto p2 = [&p1](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 3 * x * x + p1(X);
+    };
+    auto p3 = [&p2](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 4 * std::pow(x, 3) + p2(X);
+    };
+    auto p4 = [&p3](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 5 * std::pow(x, 4) + p3(X);
+    };
+    auto p5 = [&p4](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 6 * std::pow(x, 5) + p4(X);
+    };
+    auto p6 = [&p5](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 7 * std::pow(x, 6) + p5(X);
+    };
+    auto p7 = [&p6](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 8 * std::pow(x, 7) + p6(X);
+    };
+    auto p8 = [&p7](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 9 * std::pow(x, 8) + p7(X);
+    };
+    auto p9 = [&p8](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 10 * std::pow(x, 9) + p8(X);
+    };
+    auto p10 = [&p9](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 11 * std::pow(x, 10) + p9(X);
+    };
+    auto p11 = [&p10](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 12 * std::pow(x, 11) + p10(X);
+    };
+    auto p12 = [&p11](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 13 * std::pow(x, 12) + p11(X);
+    };
+    auto p13 = [&p12](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 14 * std::pow(x, 13) + p12(X);
+    };
+    auto p14 = [&p13](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 15 * std::pow(x, 14) + p13(X);
+    };
+    auto p15 = [&p14](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 16 * std::pow(x, 15) + p14(X);
+    };
+    auto p16 = [&p15](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 17 * std::pow(x, 16) + p15(X);
+    };
+    auto p17 = [&p16](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 18 * std::pow(x, 17) + p16(X);
+    };
+    auto p18 = [&p17](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 19 * std::pow(x, 18) + p17(X);
+    };
+    auto p19 = [&p18](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 20 * std::pow(x, 19) + p18(X);
+    };
+    auto p20 = [&p19](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 21 * std::pow(x, 20) + p19(X);
+    };
+    auto p21 = [&p20](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 22 * std::pow(x, 21) + p20(X);
+    };
+    auto p22 = [&p21](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 23 * std::pow(x, 22) + p21(X);
+    };
+    auto p23 = [&p22](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 24 * std::pow(x, 23) + p22(X);
+    };
+    auto p24 = [&p23](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 25 * std::pow(x, 24) + p23(X);
+    };
+
+    SECTION("degree 1")
+    {
+      const QuadratureFormula<1>& l1 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(1));
+
+      REQUIRE(is_symmetric_formula(l1));
+
+      REQUIRE(l1.numberOfPoints() == 1);
+
+      REQUIRE(integrate(p0, l1, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l1, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l1, 0, 1) != Catch::Approx(3));
+
+      REQUIRE(get_order(p2, l1, -1, 1, 4) == Catch::Approx(2));
+    }
+
+    SECTION("degree 2 and 3")
+    {
+      const QuadratureFormula<1>& l2 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(2));
+      const QuadratureFormula<1>& l3 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(3));
+
+      REQUIRE(&l2 == &l3);
+      REQUIRE(is_symmetric_formula(l3));
+
+      REQUIRE(l3.numberOfPoints() == 2);
+
+      REQUIRE(integrate(p0, l3, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l3, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l3, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l3, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l3, 0, 1) != Catch::Approx(5));
+
+      REQUIRE(get_order(p4, l3, -1, 1, 6) == Catch::Approx(4));
+    }
+
+    SECTION("degree 4 and 5")
+    {
+      const QuadratureFormula<1>& l4 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(4));
+      const QuadratureFormula<1>& l5 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(5));
+
+      REQUIRE(&l4 == &l5);
+      REQUIRE(is_symmetric_formula(l5));
+
+      REQUIRE(l5.numberOfPoints() == 3);
+
+      REQUIRE(integrate(p0, l5, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l5, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l5, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l5, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l5, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l5, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l5, 0, 1) != Catch::Approx(7));
+
+      REQUIRE(get_order(p6, l5, -1, 1, 8) == Catch::Approx(6));
+    }
+
+    SECTION("degree 6 and 7")
+    {
+      const QuadratureFormula<1>& l6 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(6));
+      const QuadratureFormula<1>& l7 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(7));
+
+      REQUIRE(&l6 == &l7);
+      REQUIRE(is_symmetric_formula(l7));
+
+      REQUIRE(l7.numberOfPoints() == 4);
+
+      REQUIRE(integrate(p0, l7, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l7, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l7, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l7, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l7, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l7, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l7, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l7, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l7, 0, 1) != Catch::Approx(9));
+
+      REQUIRE(get_order(p8, l7, -1, 1, 10) == Catch::Approx(8));
+    }
+
+    SECTION("degree 8 and 9")
+    {
+      const QuadratureFormula<1>& l8 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(8));
+      const QuadratureFormula<1>& l9 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(9));
+
+      REQUIRE(&l8 == &l9);
+      REQUIRE(is_symmetric_formula(l9));
+
+      REQUIRE(l9.numberOfPoints() == 5);
+
+      REQUIRE(integrate(p0, l9, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l9, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l9, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l9, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l9, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l9, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l9, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l9, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l9, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l9, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l9, 0, 1) != Catch::Approx(11).epsilon(1E-13));
+
+      REQUIRE(get_order(p10, l9, -1, 1, 12) == Catch::Approx(10));
+    }
+
+    SECTION("degree 10 and 11")
+    {
+      const QuadratureFormula<1>& l10 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(10));
+      const QuadratureFormula<1>& l11 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(11));
+
+      REQUIRE(&l10 == &l11);
+      REQUIRE(is_symmetric_formula(l11));
+
+      REQUIRE(l11.numberOfPoints() == 6);
+
+      REQUIRE(integrate(p0, l11, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l11, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l11, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l11, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l11, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l11, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l11, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l11, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l11, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l11, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l11, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l11, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l11, 0, 1) != Catch::Approx(13).epsilon(1E-13));
+
+      REQUIRE(get_order(p12, l11, -1, 1, 14) == Catch::Approx(12));
+    }
+
+    SECTION("degree 12 and 13")
+    {
+      const QuadratureFormula<1>& l12 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(12));
+      const QuadratureFormula<1>& l13 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(13));
+
+      REQUIRE(&l12 == &l13);
+      REQUIRE(is_symmetric_formula(l13));
+
+      REQUIRE(l13.numberOfPoints() == 7);
+
+      REQUIRE(integrate(p0, l13, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l13, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l13, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l13, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l13, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l13, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l13, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l13, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l13, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l13, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l13, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l13, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l13, 0, 1) == Catch::Approx(13));
+      REQUIRE(integrate(p13, l13, 0, 1) == Catch::Approx(14));
+      REQUIRE(integrate(p14, l13, 0, 1) != Catch::Approx(15).epsilon(1E-13));
+
+      REQUIRE(get_order(p14, l13, -1, 1, 16) == Catch::Approx(14));
+    }
+
+    SECTION("degree 14 and 15")
+    {
+      const QuadratureFormula<1>& l14 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(14));
+      const QuadratureFormula<1>& l15 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(15));
+
+      REQUIRE(&l14 == &l15);
+      REQUIRE(is_symmetric_formula(l15));
+
+      REQUIRE(l15.numberOfPoints() == 8);
+
+      REQUIRE(integrate(p0, l15, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l15, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l15, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l15, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l15, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l15, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l15, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l15, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l15, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l15, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l15, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l15, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l15, 0, 1) == Catch::Approx(13));
+      REQUIRE(integrate(p13, l15, 0, 1) == Catch::Approx(14));
+      REQUIRE(integrate(p14, l15, 0, 1) == Catch::Approx(15));
+      REQUIRE(integrate(p15, l15, 0, 1) == Catch::Approx(16));
+      REQUIRE(integrate(p16, l15, 0, 1) != Catch::Approx(17).epsilon(1E-13));
+
+      REQUIRE(get_order(p16, l15, -1, 1, 18) == Catch::Approx(16));
+    }
+
+    SECTION("degree 16 and 17")
+    {
+      const QuadratureFormula<1>& l16 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(16));
+      const QuadratureFormula<1>& l17 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(17));
+
+      REQUIRE(&l16 == &l17);
+      REQUIRE(is_symmetric_formula(l17));
+
+      REQUIRE(l17.numberOfPoints() == 9);
+
+      REQUIRE(integrate(p0, l17, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l17, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l17, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l17, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l17, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l17, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l17, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l17, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l17, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l17, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l17, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l17, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l17, 0, 1) == Catch::Approx(13));
+      REQUIRE(integrate(p13, l17, 0, 1) == Catch::Approx(14));
+      REQUIRE(integrate(p14, l17, 0, 1) == Catch::Approx(15));
+      REQUIRE(integrate(p15, l17, 0, 1) == Catch::Approx(16));
+      REQUIRE(integrate(p16, l17, 0, 1) == Catch::Approx(17));
+      REQUIRE(integrate(p17, l17, 0, 1) == Catch::Approx(18));
+      REQUIRE(integrate(p18, l17, 0, 1) != Catch::Approx(19).epsilon(1E-13));
+
+      REQUIRE(get_order(p18, l17, -1, 1, 20) == Catch::Approx(18));
+    }
+
+    SECTION("degree 18 and 19")
+    {
+      const QuadratureFormula<1>& l18 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(18));
+      const QuadratureFormula<1>& l19 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(19));
+
+      REQUIRE(&l18 == &l19);
+      REQUIRE(is_symmetric_formula(l19));
+
+      REQUIRE(l19.numberOfPoints() == 10);
+
+      REQUIRE(integrate(p0, l19, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l19, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l19, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l19, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l19, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l19, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l19, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l19, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l19, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l19, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l19, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l19, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l19, 0, 1) == Catch::Approx(13));
+      REQUIRE(integrate(p13, l19, 0, 1) == Catch::Approx(14));
+      REQUIRE(integrate(p14, l19, 0, 1) == Catch::Approx(15));
+      REQUIRE(integrate(p15, l19, 0, 1) == Catch::Approx(16));
+      REQUIRE(integrate(p16, l19, 0, 1) == Catch::Approx(17));
+      REQUIRE(integrate(p17, l19, 0, 1) == Catch::Approx(18));
+      REQUIRE(integrate(p18, l19, 0, 1) == Catch::Approx(19));
+      REQUIRE(integrate(p19, l19, 0, 1) == Catch::Approx(20));
+      REQUIRE(integrate(p20, l19, 0, 1) != Catch::Approx(21).epsilon(1E-13));
+
+      REQUIRE(get_order(p20, l19, -1, 1, 22) == Catch::Approx(20));
+    }
+
+    SECTION("degree 20 and 21")
+    {
+      const QuadratureFormula<1>& l20 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(20));
+      const QuadratureFormula<1>& l21 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(21));
+
+      REQUIRE(&l20 == &l21);
+      REQUIRE(is_symmetric_formula(l21));
+
+      REQUIRE(l21.numberOfPoints() == 11);
+
+      REQUIRE(integrate(p0, l21, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l21, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l21, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l21, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l21, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l21, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l21, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l21, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l21, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l21, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l21, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l21, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l21, 0, 1) == Catch::Approx(13));
+      REQUIRE(integrate(p13, l21, 0, 1) == Catch::Approx(14));
+      REQUIRE(integrate(p14, l21, 0, 1) == Catch::Approx(15));
+      REQUIRE(integrate(p15, l21, 0, 1) == Catch::Approx(16));
+      REQUIRE(integrate(p16, l21, 0, 1) == Catch::Approx(17));
+      REQUIRE(integrate(p17, l21, 0, 1) == Catch::Approx(18));
+      REQUIRE(integrate(p18, l21, 0, 1) == Catch::Approx(19));
+      REQUIRE(integrate(p19, l21, 0, 1) == Catch::Approx(20));
+      REQUIRE(integrate(p20, l21, 0, 1) == Catch::Approx(21));
+      REQUIRE(integrate(p21, l21, 0, 1) == Catch::Approx(22));
+      REQUIRE(integrate(p22, l21, 0, 1) != Catch::Approx(23).epsilon(1E-14));
+
+      REQUIRE(get_order(p22, l21, -1, 1, 24) == Catch::Approx(22).margin(0.02));
+    }
+
+    SECTION("degree 22 and 23")
+    {
+      const QuadratureFormula<1>& l22 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(22));
+      const QuadratureFormula<1>& l23 =
+        QuadratureManager::instance().getLineFormula(GaussLegendreQuadratureDescriptor(23));
+
+      REQUIRE(&l22 == &l23);
+      REQUIRE(is_symmetric_formula(l23));
+
+      REQUIRE(l23.numberOfPoints() == 12);
+
+      REQUIRE(integrate(p0, l23, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l23, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l23, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l23, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l23, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l23, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l23, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l23, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l23, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l23, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l23, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l23, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l23, 0, 1) == Catch::Approx(13));
+      REQUIRE(integrate(p13, l23, 0, 1) == Catch::Approx(14));
+      REQUIRE(integrate(p14, l23, 0, 1) == Catch::Approx(15));
+      REQUIRE(integrate(p15, l23, 0, 1) == Catch::Approx(16));
+      REQUIRE(integrate(p16, l23, 0, 1) == Catch::Approx(17));
+      REQUIRE(integrate(p17, l23, 0, 1) == Catch::Approx(18));
+      REQUIRE(integrate(p18, l23, 0, 1) == Catch::Approx(19));
+      REQUIRE(integrate(p19, l23, 0, 1) == Catch::Approx(20));
+      REQUIRE(integrate(p20, l23, 0, 1) == Catch::Approx(21));
+      REQUIRE(integrate(p21, l23, 0, 1) == Catch::Approx(22));
+      REQUIRE(integrate(p22, l23, 0, 1) == Catch::Approx(23));
+      REQUIRE(integrate(p23, l23, 0, 1) == Catch::Approx(24));
+      REQUIRE(integrate(p24, l23, 0, 1) != Catch::Approx(25).epsilon(1E-15));
+
+      REQUIRE(get_order(p24, l23, -1, 1, 26) == Catch::Approx(24).margin(0.05));
+    }
+
+    SECTION("max implemented degree")
+    {
+      REQUIRE(QuadratureManager::instance().maxLineDegree(QuadratureType::GaussLegendre) ==
+              TensorialGaussLegendreQuadrature<1>::max_degree);
+      REQUIRE_THROWS_WITH(QuadratureManager::instance().getLineFormula(
+                            GaussLegendreQuadratureDescriptor(TensorialGaussLegendreQuadrature<1>::max_degree + 1)),
+                          "error: Gauss-Legendre quadrature formulae handle degrees up to " +
+                            std::to_string(TensorialGaussLegendreQuadrature<1>::max_degree) + " on lines");
+
+      REQUIRE_THROWS_WITH(QuadratureManager::instance().getLineFormula(
+                            GaussQuadratureDescriptor(TensorialGaussLegendreQuadrature<1>::max_degree + 1)),
+                          "error: Gauss quadrature formulae handle degrees up to " +
+                            std::to_string(TensorialGaussLegendreQuadrature<1>::max_degree) + " on lines");
+    }
+  }
+
+  SECTION("2D")
+  {
+    auto integrate = [](auto f, auto quadrature_formula, const double xa, const double xb, const double ya,
+                        const double yb) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      const double alphax = 0.5 * (xb - xa);
+      const double betax  = 0.5 * (xa + xb);
+
+      const double alphay = 0.5 * (yb - ya);
+      const double betay  = 0.5 * (ya + yb);
+
+      auto x = [&alphax, &betax, &alphay, &betay](auto x_hat) {
+        return TinyVector<2>{alphax * x_hat[0] + betax, alphay * x_hat[1] + betay};
+      };
+
+      auto value = weight_list[0] * f(x(point_list[0]));
+      for (size_t i = 1; i < weight_list.size(); ++i) {
+        value += weight_list[i] * f(x(point_list[i]));
+      }
+
+      return alphax * alphay * value;
+    };
+
+    auto p6 = [](const double x) { return 7 * std::pow(x, 6); };
+    auto p7 = [](const double x) { return 8 * std::pow(x, 7); };
+
+    auto px7y6 = [&p6, &p7](const TinyVector<2>& X) {
+      const double x = X[0];
+      const double y = X[1];
+      return p7(x) * p6(y);
+    };
+
+    SECTION("degree 6 and 7")
+    {
+      const QuadratureFormula<2>& l6 =
+        QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(6));
+      const QuadratureFormula<2>& l7 =
+        QuadratureManager::instance().getSquareFormula(GaussLegendreQuadratureDescriptor(7));
+
+      REQUIRE(&l6 == &l7);
+
+      REQUIRE(l7.numberOfPoints() == 4 * 4);
+
+      REQUIRE(integrate(px7y6, l7, 0, 1, 0.2, 0.8) == Catch::Approx(std::pow(0.8, 7) - std::pow(0.2, 7)));
+    }
+
+    SECTION("max implemented degree")
+    {
+      REQUIRE(QuadratureManager::instance().maxSquareDegree(QuadratureType::GaussLegendre) ==
+              TensorialGaussLegendreQuadrature<2>::max_degree);
+      REQUIRE_THROWS_WITH(QuadratureManager::instance().getSquareFormula(
+                            GaussLegendreQuadratureDescriptor(TensorialGaussLegendreQuadrature<2>::max_degree + 1)),
+                          "error: Gauss-Legendre quadrature formulae handle degrees up to " +
+                            std::to_string(TensorialGaussLegendreQuadrature<2>::max_degree) + " on squares");
+    }
+  }
+
+  SECTION("3D")
+  {
+    auto integrate = [](auto f, auto quadrature_formula, const double xa, const double xb, const double ya,
+                        const double yb, const double za, const double zb) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      const double alphax = 0.5 * (xb - xa);
+      const double betax  = 0.5 * (xa + xb);
+
+      const double alphay = 0.5 * (yb - ya);
+      const double betay  = 0.5 * (ya + yb);
+
+      const double alphaz = 0.5 * (zb - za);
+      const double betaz  = 0.5 * (za + zb);
+
+      auto x = [&alphax, &betax, &alphay, &betay, &alphaz, &betaz](auto x_hat) {
+        return TinyVector<3>{alphax * x_hat[0] + betax, alphay * x_hat[1] + betay, alphaz * x_hat[2] + betaz};
+      };
+
+      auto value = weight_list[0] * f(x(point_list[0]));
+      for (size_t i = 1; i < weight_list.size(); ++i) {
+        value += weight_list[i] * f(x(point_list[i]));
+      }
+
+      return alphax * alphay * alphaz * value;
+    };
+
+    auto p6 = [](const double x) { return 7 * std::pow(x, 6); };
+    auto p7 = [](const double x) { return 8 * std::pow(x, 7); };
+
+    auto px7y6 = [&p6, &p7](const TinyVector<3>& X) {
+      const double x = X[0];
+      const double y = X[1];
+      const double z = X[2];
+      return p7(x) * p6(y) + p7(z);
+    };
+
+    SECTION("degree 6 and 7")
+    {
+      const QuadratureFormula<3>& l6 =
+        QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(6));
+      const QuadratureFormula<3>& l7 =
+        QuadratureManager::instance().getCubeFormula(GaussLegendreQuadratureDescriptor(7));
+
+      REQUIRE(&l6 == &l7);
+
+      REQUIRE(l7.numberOfPoints() == 4 * 4 * 4);
+
+      REQUIRE(integrate(px7y6, l7, 0, 1, 0.2, 0.8, -0.1, 0.7) ==
+              Catch::Approx((std::pow(0.8, 7) - std::pow(0.2, 7)) * (0.7 - -0.1) +
+                            (0.8 - 0.2) * (std::pow(0.7, 8) - std::pow(-0.1, 8))));
+    }
+
+    SECTION("max implemented degree")
+    {
+      REQUIRE(QuadratureManager::instance().maxCubeDegree(QuadratureType::GaussLegendre) ==
+              TensorialGaussLegendreQuadrature<3>::max_degree);
+      REQUIRE_THROWS_WITH(QuadratureManager::instance().getCubeFormula(
+                            GaussLegendreQuadratureDescriptor(TensorialGaussLegendreQuadrature<3>::max_degree + 1)),
+                          "error: Gauss-Legendre quadrature formulae handle degrees up to " +
+                            std::to_string(TensorialGaussLegendreQuadrature<3>::max_degree) + " on cubes");
+    }
+  }
+}
diff --git a/tests/test_TensorialGaussLobattoQuadrature.cpp b/tests/test_TensorialGaussLobattoQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9ee4028cf0aa9b58d9f7478e91f2146f7f77a675
--- /dev/null
+++ b/tests/test_TensorialGaussLobattoQuadrature.cpp
@@ -0,0 +1,444 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussLobattoQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <analysis/TensorialGaussLobattoQuadrature.hpp>
+#include <utils/Exceptions.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("TensorialGaussLobattoQuadrature", "[analysis]")
+{
+  SECTION("1D")
+  {
+    auto is_symmetric_formula = [](auto quadrature_formula) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      bool is_symmetric = true;
+
+      const size_t middle_index = point_list.size() / 2;
+      for (size_t i = 0; i <= middle_index; ++i) {
+        if (point_list[i] != -point_list[point_list.size() - 1 - i]) {
+          return false;
+        }
+      }
+      for (size_t i = 0; i <= middle_index; ++i) {
+        if (weight_list[i] != weight_list[point_list.size() - 1 - i]) {
+          return false;
+        }
+      }
+
+      return is_symmetric;
+    };
+
+    auto integrate = [](auto f, auto quadrature_formula, const double a, const double b) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      double alpha = 0.5 * (b - a);
+      double beta  = 0.5 * (a + b);
+
+      auto x = [&alpha, &beta](auto x_hat) { return TinyVector<1>{alpha * x_hat[0] + beta}; };
+
+      auto value = weight_list[0] * f(x(point_list[0]));
+      for (size_t i = 1; i < weight_list.size(); ++i) {
+        value += weight_list[i] * f(x(point_list[i]));
+      }
+
+      return alpha * value;
+    };
+
+    auto get_order = [&integrate](auto f, auto quadrature_formula, const double a, const double b,
+                                  const double exact_value) {
+      double int_ab          = integrate(f, quadrature_formula, a, b);
+      double int_first_half  = integrate(f, quadrature_formula, a, 0.5 * (a + b));
+      double int_second_half = integrate(f, quadrature_formula, 0.5 * (a + b), b);
+
+      return -std::log((int_first_half + int_second_half - exact_value) / (int_ab - exact_value)) / std::log(2);
+    };
+
+    auto p0 = [](const TinyVector<1>&) { return 1; };
+    auto p1 = [&p0](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 2 * x + p0(X);
+    };
+    auto p2 = [&p1](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 3 * x * x + p1(X);
+    };
+    auto p3 = [&p2](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 4 * std::pow(x, 3) + p2(X);
+    };
+    auto p4 = [&p3](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 5 * std::pow(x, 4) + p3(X);
+    };
+    auto p5 = [&p4](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 6 * std::pow(x, 5) + p4(X);
+    };
+    auto p6 = [&p5](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 7 * std::pow(x, 6) + p5(X);
+    };
+    auto p7 = [&p6](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 8 * std::pow(x, 7) + p6(X);
+    };
+    auto p8 = [&p7](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 9 * std::pow(x, 8) + p7(X);
+    };
+    auto p9 = [&p8](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 10 * std::pow(x, 9) + p8(X);
+    };
+    auto p10 = [&p9](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 11 * std::pow(x, 10) + p9(X);
+    };
+    auto p11 = [&p10](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 12 * std::pow(x, 11) + p10(X);
+    };
+    auto p12 = [&p11](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 13 * std::pow(x, 12) + p11(X);
+    };
+    auto p13 = [&p12](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 14 * std::pow(x, 13) + p12(X);
+    };
+    auto p14 = [&p13](const TinyVector<1>& X) {
+      const double x = X[0];
+      return 15 * std::pow(x, 14) + p13(X);
+    };
+
+    SECTION("degree 1")
+    {
+      const QuadratureFormula<1>& l1 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(1));
+
+      REQUIRE(is_symmetric_formula(l1));
+
+      REQUIRE(l1.numberOfPoints() == 2);
+
+      REQUIRE(integrate(p0, l1, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l1, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l1, 0, 1) != Catch::Approx(3));
+
+      REQUIRE(get_order(p2, l1, -1, 1, 4) == Catch::Approx(2));
+    }
+
+    SECTION("degree 2 and 3")
+    {
+      const QuadratureFormula<1>& l2 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(2));
+      const QuadratureFormula<1>& l3 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(3));
+
+      REQUIRE(&l2 == &l3);
+      REQUIRE(is_symmetric_formula(l3));
+
+      REQUIRE(l3.numberOfPoints() == 3);
+
+      REQUIRE(integrate(p0, l3, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l3, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l3, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l3, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l3, 0, 1) != Catch::Approx(5));
+
+      REQUIRE(get_order(p4, l3, -1, 1, 6) == Catch::Approx(4));
+    }
+
+    SECTION("degree 4 and 5")
+    {
+      const QuadratureFormula<1>& l4 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(4));
+      const QuadratureFormula<1>& l5 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(5));
+
+      REQUIRE(&l4 == &l5);
+      REQUIRE(is_symmetric_formula(l5));
+
+      REQUIRE(l5.numberOfPoints() == 4);
+
+      REQUIRE(integrate(p0, l5, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l5, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l5, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l5, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l5, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l5, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l5, 0, 1) != Catch::Approx(7));
+
+      REQUIRE(get_order(p6, l5, -1, 1, 8) == Catch::Approx(6));
+    }
+
+    SECTION("degree 6 and 7")
+    {
+      const QuadratureFormula<1>& l6 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(6));
+      const QuadratureFormula<1>& l7 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(7));
+
+      REQUIRE(&l6 == &l7);
+      REQUIRE(is_symmetric_formula(l7));
+
+      REQUIRE(l7.numberOfPoints() == 5);
+
+      REQUIRE(integrate(p0, l7, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l7, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l7, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l7, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l7, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l7, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l7, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l7, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l7, 0, 1) != Catch::Approx(9));
+
+      REQUIRE(get_order(p8, l7, -1, 1, 10) == Catch::Approx(8));
+    }
+
+    SECTION("degree 8 and 9")
+    {
+      const QuadratureFormula<1>& l8 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(8));
+      const QuadratureFormula<1>& l9 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(9));
+
+      REQUIRE(&l8 == &l9);
+      REQUIRE(is_symmetric_formula(l9));
+
+      REQUIRE(l9.numberOfPoints() == 6);
+
+      REQUIRE(integrate(p0, l9, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l9, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l9, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l9, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l9, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l9, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l9, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l9, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l9, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l9, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l9, 0, 1) != Catch::Approx(11).epsilon(1E-13));
+
+      REQUIRE(get_order(p10, l9, -1, 1, 12) == Catch::Approx(10));
+    }
+
+    SECTION("degree 10 and 11")
+    {
+      const QuadratureFormula<1>& l10 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(10));
+      const QuadratureFormula<1>& l11 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(11));
+
+      REQUIRE(&l10 == &l11);
+      REQUIRE(is_symmetric_formula(l11));
+
+      REQUIRE(l11.numberOfPoints() == 7);
+
+      REQUIRE(integrate(p0, l11, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l11, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l11, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l11, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l11, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l11, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l11, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l11, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l11, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l11, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l11, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l11, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l11, 0, 1) != Catch::Approx(13).epsilon(1E-13));
+
+      REQUIRE(get_order(p12, l11, -1, 1, 14) == Catch::Approx(12));
+    }
+
+    SECTION("degree 12 and 13")
+    {
+      const QuadratureFormula<1>& l12 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(12));
+      const QuadratureFormula<1>& l13 =
+        QuadratureManager::instance().getLineFormula(GaussLobattoQuadratureDescriptor(13));
+
+      REQUIRE(&l12 == &l13);
+      REQUIRE(is_symmetric_formula(l13));
+
+      REQUIRE(l13.numberOfPoints() == 8);
+
+      REQUIRE(integrate(p0, l13, 0.5, 1) == Catch::Approx(0.5));
+      REQUIRE(integrate(p1, l13, 0, 1) == Catch::Approx(2));
+      REQUIRE(integrate(p2, l13, 0, 1) == Catch::Approx(3));
+      REQUIRE(integrate(p3, l13, 0, 1) == Catch::Approx(4));
+      REQUIRE(integrate(p4, l13, 0, 1) == Catch::Approx(5));
+      REQUIRE(integrate(p5, l13, 0, 1) == Catch::Approx(6));
+      REQUIRE(integrate(p6, l13, 0, 1) == Catch::Approx(7));
+      REQUIRE(integrate(p7, l13, 0, 1) == Catch::Approx(8));
+      REQUIRE(integrate(p8, l13, 0, 1) == Catch::Approx(9));
+      REQUIRE(integrate(p9, l13, 0, 1) == Catch::Approx(10));
+      REQUIRE(integrate(p10, l13, 0, 1) == Catch::Approx(11));
+      REQUIRE(integrate(p11, l13, 0, 1) == Catch::Approx(12));
+      REQUIRE(integrate(p12, l13, 0, 1) == Catch::Approx(13));
+      REQUIRE(integrate(p13, l13, 0, 1) == Catch::Approx(14));
+      REQUIRE(integrate(p14, l13, 0, 1) != Catch::Approx(15).epsilon(1E-13));
+
+      REQUIRE(get_order(p14, l13, -1, 1, 16) == Catch::Approx(14));
+    }
+
+    SECTION("max implemented degree")
+    {
+      REQUIRE(QuadratureManager::instance().maxLineDegree(QuadratureType::GaussLobatto) ==
+              TensorialGaussLobattoQuadrature<1>::max_degree);
+      REQUIRE_THROWS_WITH(QuadratureManager::instance().getLineFormula(
+                            GaussLobattoQuadratureDescriptor(TensorialGaussLobattoQuadrature<1>::max_degree + 1)),
+                          "error: Gauss-Lobatto quadrature formulae handle degrees up to " +
+                            std::to_string(TensorialGaussLobattoQuadrature<1>::max_degree) + " on lines");
+    }
+  }
+
+  SECTION("2D")
+  {
+    auto integrate = [](auto f, auto quadrature_formula, const double xa, const double xb, const double ya,
+                        const double yb) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      const double alphax = 0.5 * (xb - xa);
+      const double betax  = 0.5 * (xa + xb);
+
+      const double alphay = 0.5 * (yb - ya);
+      const double betay  = 0.5 * (ya + yb);
+
+      auto x = [&alphax, &betax, &alphay, &betay](auto x_hat) {
+        return TinyVector<2>{alphax * x_hat[0] + betax, alphay * x_hat[1] + betay};
+      };
+
+      auto value = weight_list[0] * f(x(point_list[0]));
+      for (size_t i = 1; i < weight_list.size(); ++i) {
+        value += weight_list[i] * f(x(point_list[i]));
+      }
+
+      return alphax * alphay * value;
+    };
+
+    auto p6 = [](const double x) { return 7 * std::pow(x, 6); };
+    auto p7 = [](const double x) { return 8 * std::pow(x, 7); };
+
+    auto px7y6 = [&p6, &p7](const TinyVector<2>& X) {
+      const double x = X[0];
+      const double y = X[1];
+      return p7(x) * p6(y);
+    };
+
+    SECTION("degree 6 and 7")
+    {
+      const QuadratureFormula<2>& l6 =
+        QuadratureManager::instance().getSquareFormula(GaussLobattoQuadratureDescriptor(6));
+      const QuadratureFormula<2>& l7 =
+        QuadratureManager::instance().getSquareFormula(GaussLobattoQuadratureDescriptor(7));
+
+      REQUIRE(&l6 == &l7);
+
+      REQUIRE(l7.numberOfPoints() == 5 * 5);
+
+      REQUIRE(integrate(px7y6, l7, 0, 1, 0.2, 0.8) == Catch::Approx(std::pow(0.8, 7) - std::pow(0.2, 7)));
+    }
+
+    SECTION("max implemented degree")
+    {
+      REQUIRE(QuadratureManager::instance().maxSquareDegree(QuadratureType::GaussLobatto) ==
+              TensorialGaussLobattoQuadrature<2>::max_degree);
+      REQUIRE_THROWS_WITH(QuadratureManager::instance().getSquareFormula(
+                            GaussLobattoQuadratureDescriptor(TensorialGaussLobattoQuadrature<2>::max_degree + 1)),
+                          "error: Gauss-Lobatto quadrature formulae handle degrees up to " +
+                            std::to_string(TensorialGaussLobattoQuadrature<2>::max_degree) + " on squares");
+    }
+  }
+
+  SECTION("3D")
+  {
+    auto integrate = [](auto f, auto quadrature_formula, const double xa, const double xb, const double ya,
+                        const double yb, const double za, const double zb) {
+      auto point_list  = quadrature_formula.pointList();
+      auto weight_list = quadrature_formula.weightList();
+
+      const double alphax = 0.5 * (xb - xa);
+      const double betax  = 0.5 * (xa + xb);
+
+      const double alphay = 0.5 * (yb - ya);
+      const double betay  = 0.5 * (ya + yb);
+
+      const double alphaz = 0.5 * (zb - za);
+      const double betaz  = 0.5 * (za + zb);
+
+      auto x = [&alphax, &betax, &alphay, &betay, &alphaz, &betaz](auto x_hat) {
+        return TinyVector<3>{alphax * x_hat[0] + betax, alphay * x_hat[1] + betay, alphaz * x_hat[2] + betaz};
+      };
+
+      auto value = weight_list[0] * f(x(point_list[0]));
+      for (size_t i = 1; i < weight_list.size(); ++i) {
+        value += weight_list[i] * f(x(point_list[i]));
+      }
+
+      return alphax * alphay * alphaz * value;
+    };
+
+    auto p6 = [](const double x) { return 7 * std::pow(x, 6); };
+    auto p7 = [](const double x) { return 8 * std::pow(x, 7); };
+
+    auto px7y6 = [&p6, &p7](const TinyVector<3>& X) {
+      const double x = X[0];
+      const double y = X[1];
+      const double z = X[2];
+      return p7(x) * p6(y) + p7(z);
+    };
+
+    SECTION("degree 6 and 7")
+    {
+      const QuadratureFormula<3>& l6 =
+        QuadratureManager::instance().getCubeFormula(GaussLobattoQuadratureDescriptor(6));
+      const QuadratureFormula<3>& l7 =
+        QuadratureManager::instance().getCubeFormula(GaussLobattoQuadratureDescriptor(7));
+
+      REQUIRE(&l6 == &l7);
+
+      REQUIRE(l7.numberOfPoints() == 5 * 5 * 5);
+
+      REQUIRE(integrate(px7y6, l7, 0, 1, 0.2, 0.8, -0.1, 0.7) ==
+              Catch::Approx((std::pow(0.8, 7) - std::pow(0.2, 7)) * (0.7 - -0.1) +
+                            (0.8 - 0.2) * (std::pow(0.7, 8) - std::pow(-0.1, 8))));
+    }
+
+    SECTION("max implemented degree")
+    {
+      REQUIRE(QuadratureManager::instance().maxCubeDegree(QuadratureType::GaussLobatto) ==
+              TensorialGaussLobattoQuadrature<3>::max_degree);
+      REQUIRE_THROWS_WITH(QuadratureManager::instance().getCubeFormula(
+                            GaussLobattoQuadratureDescriptor(TensorialGaussLobattoQuadrature<3>::max_degree + 1)),
+                          "error: Gauss-Lobatto quadrature formulae handle degrees up to " +
+                            std::to_string(TensorialGaussLobattoQuadrature<3>::max_degree) + " on cubes");
+    }
+  }
+
+  SECTION("Access functions")
+  {
+    const QuadratureFormula<3>& quadrature_formula =
+      QuadratureManager::instance().getCubeFormula(GaussLobattoQuadratureDescriptor(7));
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    REQUIRE(point_list.size() == quadrature_formula.numberOfPoints());
+    REQUIRE(weight_list.size() == quadrature_formula.numberOfPoints());
+
+    for (size_t i = 0; i < quadrature_formula.numberOfPoints(); ++i) {
+      REQUIRE(&point_list[i] == &quadrature_formula.point(i));
+      REQUIRE(&weight_list[i] == &quadrature_formula.weight(i));
+    }
+  }
+}
diff --git a/tests/test_TetrahedronGaussQuadrature.cpp b/tests/test_TetrahedronGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a412c13688596e37916fd50a522b00a6aaae8485
--- /dev/null
+++ b/tests/test_TetrahedronGaussQuadrature.cpp
@@ -0,0 +1,687 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <analysis/TetrahedronGaussQuadrature.hpp>
+#include <geometry/TetrahedronTransformation.hpp>
+#include <utils/Exceptions.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("TetrahedronGaussQuadrature", "[analysis]")
+{
+  auto integrate = [](auto f, auto quadrature_formula) {
+    using R3 = TinyVector<3>;
+
+    const R3 A{-1, -1, -1};
+    const R3 B{+1, -1, -1};
+    const R3 C{-1, +1, -1};
+    const R3 D{-1, -1, +1};
+
+    TetrahedronTransformation t{A, B, C, D};
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(t(point_list[0]));
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(t(point_list[i]));
+    }
+
+    return t.jacobianDeterminant() * value;
+  };
+
+  auto integrate_on_tetra = [](auto f, auto quadrature_formula, const TinyVector<3>& a, const TinyVector<3>& b,
+                               const TinyVector<3>& c, const TinyVector<3>& d) {
+    TetrahedronTransformation t{a, b, c, d};
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(t(point_list[0]));
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(t(point_list[i]));
+    }
+
+    return t.jacobianDeterminant() * value;
+  };
+
+  auto get_order = [&integrate, &integrate_on_tetra](auto f, auto quadrature_formula, const double exact_value) {
+    using R3 = TinyVector<3>;
+
+    const R3 A{-1, -1, -1};
+    const R3 B{+1, -1, -1};
+    const R3 C{-1, +1, -1};
+    const R3 D{-1, -1, +1};
+
+    const R3 M_AB = 0.5 * (A + B);
+    const R3 M_AC = 0.5 * (A + C);
+    const R3 M_AD = 0.5 * (A + D);
+    const R3 M_BC = 0.5 * (B + C);
+    const R3 M_BD = 0.5 * (B + D);
+    const R3 M_CD = 0.5 * (C + D);
+
+    const double int_T_hat = integrate(f, quadrature_formula);
+    const double int_refined   //
+      = integrate_on_tetra(f, quadrature_formula, A, M_AB, M_AC, M_AD) +
+        integrate_on_tetra(f, quadrature_formula, B, M_AB, M_BD, M_BC) +
+        integrate_on_tetra(f, quadrature_formula, C, M_AC, M_BC, M_CD) +
+        integrate_on_tetra(f, quadrature_formula, D, M_AD, M_CD, M_BD) +
+        integrate_on_tetra(f, quadrature_formula, M_BD, M_AC, M_AD, M_CD) +
+        integrate_on_tetra(f, quadrature_formula, M_AB, M_AC, M_AD, M_BD) +
+        integrate_on_tetra(f, quadrature_formula, M_BC, M_AB, M_BD, M_AC) +
+        integrate_on_tetra(f, quadrature_formula, M_BC, M_AC, M_BD, M_CD);
+
+    return -std::log(std::abs(int_refined - exact_value) / std::abs(int_T_hat - exact_value)) / std::log(2);
+  };
+
+  auto p0 = [](const TinyVector<3>&) { return 4; };
+  auto p1 = [](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return 2 * x + 3 * y + z - 1;
+  };
+  auto p2 = [&p1](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p1(X) * (2.5 * x - 3 * y + z + 3);
+  };
+  auto p3 = [&p2](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p2(X) * (3 * x + 2 * y - 3 * z - 1);
+  };
+  auto p4 = [&p3](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p3(X) * (2 * x - 0.5 * y - 1.3 * z + 1);
+  };
+  auto p5 = [&p4](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p4(X) * (-0.1 * x + 1.3 * y - 3 * z + 1);
+  };
+  auto p6 = [&p5](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p5(X) * 7875. / 143443 * (2 * x - y + 4 * z + 1);
+  };
+  auto p7 = [&p6](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p6(X) * (0.7 * x - 2.7 * y + 1.3 * z - 2);
+  };
+  auto p8 = [&p7](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p7(X) * (0.3 * x + 1.2 * y - 0.7 * z + 0.2);
+  };
+  auto p9 = [&p8](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p8(X) * (-0.2 * x + 1.1 * y - 0.5 * z + 0.6);
+  };
+  auto p10 = [&p9](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p9(X) * (0.7 * x - 0.6 * y - 0.7 * z - 0.2);
+  };
+  auto p11 = [&p10](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p10(X) * (-1.3 * x + 0.6 * y - 1.3 * z + 0.7);
+  };
+  auto p12 = [&p11](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p11(X) * (0.3 * x - 0.7 * y + 0.3 * z + 0.7);
+  };
+  auto p13 = [&p12](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p12(X) * (0.9 * x + 0.2 * y - 0.4 * z + 0.5);
+  };
+  auto p14 = [&p13](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p13(X) * (0.6 * x - 1.2 * y + 0.7 * z - 0.4);
+  };
+  auto p15 = [&p14](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p14(X) * (-0.2 * x - 0.7 * y + 0.9 * z + 0.8);
+  };
+  auto p16 = [&p15](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p15(X) * (0.7 * x + 0.2 * y - 0.6 * z + 0.4);
+  };
+  auto p17 = [&p16](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p16(X) * (-0.1 * x + 0.8 * y + 0.3 * z - 0.2);
+  };
+  auto p18 = [&p17](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p17(X) * (0.7 * x - 0.2 * y - 0.3 * z + 0.8);
+  };
+  auto p19 = [&p18](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p18(X) * (-0.7 * x + 1.2 * y + 1.3 * z + 0.8);
+  };
+  auto p20 = [&p19](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p19(X) * (0.7 * x - 1.2 * y + 0.3 * z - 0.6);
+  };
+  auto p21 = [&p20](const TinyVector<3>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    const double z = X[2];
+    return p20(X) * (0.7 * x - 1.2 * y + 0.3 * z - 0.6);
+  };
+
+  SECTION("degree 1")
+  {
+    const QuadratureFormula<3>& l1 = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(1));
+
+    REQUIRE(l1.numberOfPoints() == 1);
+
+    REQUIRE(integrate(p0, l1) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l1) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l1) != Catch::Approx(-47. / 3));
+
+    REQUIRE(get_order(p2, l1, -47. / 3) >= Catch::Approx(2));
+  }
+
+  SECTION("degree 2")
+  {
+    const QuadratureFormula<3>& l2 = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(2));
+
+    REQUIRE(l2.numberOfPoints() == 4);
+
+    REQUIRE(integrate(p0, l2) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l2) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l2) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l2) != Catch::Approx(557. / 15));
+
+    REQUIRE(get_order(p3, l2, 557. / 15) >= Catch::Approx(3));
+  }
+
+  SECTION("degree 3")
+  {
+    const QuadratureFormula<3>& l3 = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(3));
+
+    REQUIRE(l3.numberOfPoints() == 8);
+
+    REQUIRE(integrate(p0, l3) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l3) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l3) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l3) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l3) != Catch::Approx(4499. / 1575));
+  }
+
+  SECTION("degree 4")
+  {
+    const QuadratureFormula<3>& l4 = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(4));
+
+    REQUIRE(l4.numberOfPoints() == 14);
+
+    REQUIRE(integrate(p0, l4) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l4) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l4) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l4) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l4) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l4) != Catch::Approx(143443. / 7875));
+
+    REQUIRE(get_order(p5, l4, 143443. / 7875) >= Catch::Approx(5));
+  }
+
+  SECTION("degree 5")
+  {
+    const QuadratureFormula<3>& l5 = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(5));
+
+    REQUIRE(l5.numberOfPoints() == 14);
+
+    REQUIRE(integrate(p0, l5) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l5) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l5) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l5) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l5) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l5) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l5) != Catch::Approx(-75773. / 2581974));
+  }
+
+  SECTION("degree 6")
+  {
+    const QuadratureFormula<3>& l6 = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(6));
+
+    REQUIRE(l6.numberOfPoints() == 24);
+
+    REQUIRE(integrate(p0, l6) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l6) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l6) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l6) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l6) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l6) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l6) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l6) != Catch::Approx(86951548. / 32274675));
+
+    REQUIRE(get_order(p7, l6, 86951548. / 32274675) >= Catch::Approx(7));
+  }
+
+  SECTION("degree 7")
+  {
+    const QuadratureFormula<3>& l7 = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(7));
+
+    REQUIRE(l7.numberOfPoints() == 35);
+
+    REQUIRE(integrate(p0, l7) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l7) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l7) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l7) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l7) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l7) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l7) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l7) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l7) != Catch::Approx(-863556317. / 322746750));
+
+    REQUIRE(get_order(p8, l7, -863556317. / 322746750) == Catch::Approx(8).margin(0.5));
+  }
+
+  SECTION("degree 8")
+  {
+    const QuadratureFormula<3>& l8 = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(8));
+
+    REQUIRE(l8.numberOfPoints() == 46);
+
+    REQUIRE(integrate(p0, l8) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l8) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l8) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l8) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l8) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l8) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l8) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l8) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l8) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l8) != Catch::Approx(1168568393. / 3227467500));
+
+    // In a weird way, one gets almost 10th order on this test
+    REQUIRE(get_order(p9, l8, 1168568393. / 3227467500) == Catch::Approx(10));
+  }
+
+  SECTION("degree 9")
+  {
+    const QuadratureFormula<3>& l9 = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(9));
+
+    REQUIRE(l9.numberOfPoints() == 59);
+
+    REQUIRE(integrate(p0, l9) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l9) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l9) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l9) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l9) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l9) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l9) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l9) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l9) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l9) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l9) != Catch::Approx(-473611706567. / 461527852500));
+
+    // No order test. Too bad ~4.5 when one expects 10 asymptotically
+  }
+
+  SECTION("degree 10")
+  {
+    const QuadratureFormula<3>& l10 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(10));
+
+    REQUIRE(l10.numberOfPoints() == 81);
+
+    REQUIRE(integrate(p0, l10) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l10) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l10) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l10) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l10) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l10) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l10) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l10) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l10) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l10) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l10) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l10) != Catch::Approx(-340332578501887. / 323069496750000));
+
+    // In a weird way, one gets almost 12th order on this test
+    REQUIRE(get_order(p11, l10, -340332578501887. / 323069496750000) == Catch::Approx(12));
+  }
+
+  SECTION("degree 11")
+  {
+    const QuadratureFormula<3>& l11 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(11));
+
+    REQUIRE(l11.numberOfPoints() == 110);
+
+    REQUIRE(integrate(p0, l11) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l11) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l11) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l11) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l11) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l11) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l11) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l11) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l11) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l11) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l11) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l11) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l11) != Catch::Approx(-9990191769716047. / 16153474837500000));
+
+    // No order test. Too bad ~11 when one expects 12 asymptotically
+  }
+
+  SECTION("degree 12")
+  {
+    const QuadratureFormula<3>& l12 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(12));
+
+    REQUIRE(l12.numberOfPoints() == 168);
+
+    REQUIRE(integrate(p0, l12) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l12) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l12) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l12) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l12) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l12) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l12) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l12) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l12) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l12) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l12) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l12) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l12) == Catch::Approx(-9990191769716047. / 16153474837500000));
+    REQUIRE(integrate(p13, l12) != Catch::Approx(-31229729533861. / 5384491612500000));
+
+    // In a weird way, one gets almost 14th order on this test
+    REQUIRE(get_order(p13, l12, -31229729533861. / 5384491612500000) == Catch::Approx(14).margin(0.01));
+  }
+
+  SECTION("degree 13")
+  {
+    const QuadratureFormula<3>& l13 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(13));
+
+    REQUIRE(l13.numberOfPoints() == 172);
+
+    REQUIRE(integrate(p0, l13) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l13) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l13) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l13) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l13) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l13) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l13) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l13) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l13) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l13) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l13) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l13) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l13) == Catch::Approx(-9990191769716047. / 16153474837500000));
+    REQUIRE(integrate(p13, l13) == Catch::Approx(-31229729533861. / 5384491612500000));
+    REQUIRE(integrate(p14, l13) != Catch::Approx(3758162710897404343. / 13730453611875000000.));
+
+    REQUIRE(get_order(p14, l13, 3758162710897404343. / 13730453611875000000.) == Catch::Approx(14).margin(0.5));
+  }
+
+  SECTION("degree 14")
+  {
+    const QuadratureFormula<3>& l14 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(14));
+
+    REQUIRE(l14.numberOfPoints() == 204);
+
+    REQUIRE(integrate(p0, l14) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l14) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l14) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l14) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l14) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l14) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l14) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l14) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l14) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l14) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l14) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l14) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l14) == Catch::Approx(-9990191769716047. / 16153474837500000));
+    REQUIRE(integrate(p13, l14) == Catch::Approx(-31229729533861. / 5384491612500000));
+    REQUIRE(integrate(p14, l14) == Catch::Approx(3758162710897404343. / 13730453611875000000.));
+    REQUIRE(integrate(p15, l14) != Catch::Approx(49733943385709654587. / 137304536118750000000.));
+
+    // In a weird way, one gets almost 16th order on this test
+    REQUIRE(get_order(p15, l14, 49733943385709654587. / 137304536118750000000.) == Catch::Approx(16).margin(0.01));
+  }
+
+  SECTION("degree 15")
+  {
+    const QuadratureFormula<3>& l15 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(15));
+
+    REQUIRE(l15.numberOfPoints() == 264);
+
+    REQUIRE(integrate(p0, l15) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l15) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l15) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l15) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l15) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l15) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l15) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l15) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l15) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l15) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l15) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l15) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l15) == Catch::Approx(-9990191769716047. / 16153474837500000));
+    REQUIRE(integrate(p13, l15) == Catch::Approx(-31229729533861. / 5384491612500000));
+    REQUIRE(integrate(p14, l15) == Catch::Approx(3758162710897404343. / 13730453611875000000.));
+    REQUIRE(integrate(p15, l15) == Catch::Approx(49733943385709654587. / 137304536118750000000.));
+    REQUIRE(integrate(p16, l15) != Catch::Approx(-7270030713465096260897. / 26087861862562500000000.));
+
+    REQUIRE(get_order(p16, l15, -7270030713465096260897. / 26087861862562500000000.) ==
+            Catch::Approx(16.5).margin(0.2));
+  }
+
+  SECTION("degree 16")
+  {
+    const QuadratureFormula<3>& l16 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(16));
+
+    REQUIRE(l16.numberOfPoints() == 304);
+
+    REQUIRE(integrate(p0, l16) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l16) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l16) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l16) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l16) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l16) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l16) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l16) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l16) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l16) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l16) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l16) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l16) == Catch::Approx(-9990191769716047. / 16153474837500000));
+    REQUIRE(integrate(p13, l16) == Catch::Approx(-31229729533861. / 5384491612500000));
+    REQUIRE(integrate(p14, l16) == Catch::Approx(3758162710897404343. / 13730453611875000000.));
+    REQUIRE(integrate(p15, l16) == Catch::Approx(49733943385709654587. / 137304536118750000000.));
+    REQUIRE(integrate(p16, l16) == Catch::Approx(-7270030713465096260897. / 26087861862562500000000.));
+    REQUIRE(integrate(p17, l16) != Catch::Approx(34859214238288098805889. / 195658963969218750000000.));
+
+    // In a weird way, one gets almost 18th order on this test
+    REQUIRE(get_order(p17, l16, 34859214238288098805889. / 195658963969218750000000.) == Catch::Approx(18).margin(0.1));
+  }
+
+  SECTION("degree 17")
+  {
+    const QuadratureFormula<3>& l17 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(17));
+
+    REQUIRE(l17.numberOfPoints() == 364);
+
+    REQUIRE(integrate(p0, l17) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l17) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l17) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l17) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l17) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l17) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l17) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l17) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l17) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l17) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l17) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l17) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l17) == Catch::Approx(-9990191769716047. / 16153474837500000));
+    REQUIRE(integrate(p13, l17) == Catch::Approx(-31229729533861. / 5384491612500000));
+    REQUIRE(integrate(p14, l17) == Catch::Approx(3758162710897404343. / 13730453611875000000.));
+    REQUIRE(integrate(p15, l17) == Catch::Approx(49733943385709654587. / 137304536118750000000.));
+    REQUIRE(integrate(p16, l17) == Catch::Approx(-7270030713465096260897. / 26087861862562500000000.));
+    REQUIRE(integrate(p17, l17) == Catch::Approx(34859214238288098805889. / 195658963969218750000000.));
+    REQUIRE(integrate(p18, l17) != Catch::Approx(115510477527288033058991. / 13696127477845312500000000.));
+
+    REQUIRE(get_order(p18, l17, 115510477527288033058991. / 13696127477845312500000000.) ==
+            Catch::Approx(18).margin(0.2));
+  }
+
+  SECTION("degree 18")
+  {
+    const QuadratureFormula<3>& l18 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(18));
+
+    REQUIRE(l18.numberOfPoints() == 436);
+
+    REQUIRE(integrate(p0, l18) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l18) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l18) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l18) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l18) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l18) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l18) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l18) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l18) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l18) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l18) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l18) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l18) == Catch::Approx(-9990191769716047. / 16153474837500000));
+    REQUIRE(integrate(p13, l18) == Catch::Approx(-31229729533861. / 5384491612500000));
+    REQUIRE(integrate(p14, l18) == Catch::Approx(3758162710897404343. / 13730453611875000000.));
+    REQUIRE(integrate(p15, l18) == Catch::Approx(49733943385709654587. / 137304536118750000000.));
+    REQUIRE(integrate(p16, l18) == Catch::Approx(-7270030713465096260897. / 26087861862562500000000.));
+    REQUIRE(integrate(p17, l18) == Catch::Approx(34859214238288098805889. / 195658963969218750000000.));
+    REQUIRE(integrate(p18, l18) == Catch::Approx(115510477527288033058991. / 13696127477845312500000000.));
+    REQUIRE(integrate(p19, l18) !=
+            Catch::Approx(2328326230028547427138903. / 57945154713960937500000000.).epsilon(1E-10));
+
+    // In a weird way, one gets almost 20th order on this test
+    REQUIRE(get_order(p19, l18, 2328326230028547427138903. / 57945154713960937500000000.) ==
+            Catch::Approx(20).margin(0.01));
+  }
+
+  SECTION("degree 19")
+  {
+    const QuadratureFormula<3>& l19 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(19));
+
+    REQUIRE(l19.numberOfPoints() == 487);
+
+    REQUIRE(integrate(p0, l19) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l19) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l19) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l19) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l19) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l19) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l19) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l19) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l19) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l19) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l19) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l19) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l19) == Catch::Approx(-9990191769716047. / 16153474837500000));
+    REQUIRE(integrate(p13, l19) == Catch::Approx(-31229729533861. / 5384491612500000));
+    REQUIRE(integrate(p14, l19) == Catch::Approx(3758162710897404343. / 13730453611875000000.));
+    REQUIRE(integrate(p15, l19) == Catch::Approx(49733943385709654587. / 137304536118750000000.));
+    REQUIRE(integrate(p16, l19) == Catch::Approx(-7270030713465096260897. / 26087861862562500000000.));
+    REQUIRE(integrate(p17, l19) == Catch::Approx(34859214238288098805889. / 195658963969218750000000.));
+    REQUIRE(integrate(p18, l19) == Catch::Approx(115510477527288033058991. / 13696127477845312500000000.));
+    REQUIRE(integrate(p19, l19) == Catch::Approx(2328326230028547427138903. / 57945154713960937500000000.));
+    REQUIRE(integrate(p20, l19) !=
+            Catch::Approx(-20113634155270986416106151. / 19250668066082578125000000000.).epsilon(1E-10));
+
+    // No order test. Too bad ~16 when one expects 20 asymptotically
+  }
+
+  SECTION("degree 20")
+  {
+    const QuadratureFormula<3>& l20 =
+      QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(20));
+
+    REQUIRE(l20.numberOfPoints() == 552);
+
+    REQUIRE(integrate(p0, l20) == Catch::Approx(16. / 3));
+    REQUIRE(integrate(p1, l20) == Catch::Approx(-16. / 3));
+    REQUIRE(integrate(p2, l20) == Catch::Approx(-47. / 3));
+    REQUIRE(integrate(p3, l20) == Catch::Approx(557. / 15));
+    REQUIRE(integrate(p4, l20) == Catch::Approx(4499. / 1575));
+    REQUIRE(integrate(p5, l20) == Catch::Approx(143443. / 7875));
+    REQUIRE(integrate(p6, l20) == Catch::Approx(-75773. / 2581974));
+    REQUIRE(integrate(p7, l20) == Catch::Approx(86951548. / 32274675));
+    REQUIRE(integrate(p8, l20) == Catch::Approx(-863556317. / 322746750));
+    REQUIRE(integrate(p9, l20) == Catch::Approx(1168568393. / 3227467500));
+    REQUIRE(integrate(p10, l20) == Catch::Approx(-473611706567. / 461527852500));
+    REQUIRE(integrate(p11, l20) == Catch::Approx(-340332578501887. / 323069496750000));
+    REQUIRE(integrate(p12, l20) == Catch::Approx(-9990191769716047. / 16153474837500000));
+    REQUIRE(integrate(p13, l20) == Catch::Approx(-31229729533861. / 5384491612500000));
+    REQUIRE(integrate(p14, l20) == Catch::Approx(3758162710897404343. / 13730453611875000000.));
+    REQUIRE(integrate(p15, l20) == Catch::Approx(49733943385709654587. / 137304536118750000000.));
+    REQUIRE(integrate(p16, l20) == Catch::Approx(-7270030713465096260897. / 26087861862562500000000.));
+    REQUIRE(integrate(p17, l20) == Catch::Approx(34859214238288098805889. / 195658963969218750000000.));
+    REQUIRE(integrate(p18, l20) == Catch::Approx(115510477527288033058991. / 13696127477845312500000000.));
+    REQUIRE(integrate(p19, l20) == Catch::Approx(2328326230028547427138903. / 57945154713960937500000000.));
+    REQUIRE(integrate(p20, l20) == Catch::Approx(-20113634155270986416106151. / 19250668066082578125000000000.));
+    REQUIRE(integrate(p21, l20) != Catch::Approx(16760093568330570728566871. / 7598947920822070312500000000.));
+
+    // In a weird way, one gets almost 22th order on this test
+    REQUIRE(get_order(p21, l20, 16760093568330570728566871. / 7598947920822070312500000000.) ==
+            Catch::Approx(22).margin(0.01));
+  }
+
+  SECTION("max implemented degree")
+  {
+    REQUIRE(QuadratureManager::instance().maxTetrahedronDegree(QuadratureType::Gauss) ==
+            TetrahedronGaussQuadrature::max_degree);
+    REQUIRE_THROWS_WITH(QuadratureManager::instance().getTetrahedronFormula(
+                          GaussQuadratureDescriptor(TetrahedronGaussQuadrature ::max_degree + 1)),
+                        "error: Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(TetrahedronGaussQuadrature ::max_degree) + " on tetrahedra");
+  }
+}
diff --git a/tests/test_TetrahedronTransformation.cpp b/tests/test_TetrahedronTransformation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..504f48b34fabcae986f2e24000366066548a5c6c
--- /dev/null
+++ b/tests/test_TetrahedronTransformation.cpp
@@ -0,0 +1,61 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/TetrahedronTransformation.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("TetrahedronTransformation", "[geometry]")
+{
+  using R3 = TinyVector<3>;
+
+  const R3 a{1, 2, 1};
+  const R3 b{3, 1, 3};
+  const R3 c{2, 5, 2};
+  const R3 d{2, 3, 4};
+
+  const TetrahedronTransformation t(a, b, c, d);
+
+  REQUIRE(t(R3{0, 0, 0})[0] == Catch::Approx(1));
+  REQUIRE(t(R3{0, 0, 0})[1] == Catch::Approx(2));
+  REQUIRE(t(R3{0, 0, 0})[2] == Catch::Approx(1));
+
+  REQUIRE(t(R3{1, 0, 0})[0] == Catch::Approx(3));
+  REQUIRE(t(R3{1, 0, 0})[1] == Catch::Approx(1));
+  REQUIRE(t(R3{1, 0, 0})[2] == Catch::Approx(3));
+
+  REQUIRE(t(R3{0, 1, 0})[0] == Catch::Approx(2));
+  REQUIRE(t(R3{0, 1, 0})[1] == Catch::Approx(5));
+  REQUIRE(t(R3{0, 1, 0})[2] == Catch::Approx(2));
+
+  REQUIRE(t(R3{0, 0, 1})[0] == Catch::Approx(2));
+  REQUIRE(t(R3{0, 0, 1})[1] == Catch::Approx(3));
+  REQUIRE(t(R3{0, 0, 1})[2] == Catch::Approx(4));
+
+  REQUIRE(t(R3{0.25, 0.25, 0.25})[0] == Catch::Approx(2));
+  REQUIRE(t(R3{0.25, 0.25, 0.25})[1] == Catch::Approx(11. / 4));
+  REQUIRE(t(R3{0.25, 0.25, 0.25})[2] == Catch::Approx(2.5));
+
+  REQUIRE(t.jacobianDeterminant() == Catch::Approx(14));
+
+  SECTION("Polynomial integral")
+  {
+    auto p = [](const R3& X) {
+      const double x = X[0];
+      const double y = X[1];
+      const double z = X[2];
+      return 2 * x * x + 3 * x * y + y * y + 3 * y - z * z + 2 * x * z + 2 * z;
+    };
+
+    QuadratureFormula<3> qf = QuadratureManager::instance().getTetrahedronFormula(GaussQuadratureDescriptor(2));
+
+    double sum = 0;
+    for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+      sum += qf.weight(i) * t.jacobianDeterminant() * p(t(qf.point(i)));
+    }
+
+    REQUIRE(sum == Catch::Approx(231. / 2));
+  }
+}
diff --git a/tests/test_TinyMatrix.cpp b/tests/test_TinyMatrix.cpp
index c9b72d5de9ecb6b047ae8c16ad832f8b29b38157..81c187dc2f5c7663fb7491cfa5c68fc2fc2d7eb6 100644
--- a/tests/test_TinyMatrix.cpp
+++ b/tests/test_TinyMatrix.cpp
@@ -21,6 +21,16 @@ template class TinyMatrix<4, 4, double>;
 
 TEST_CASE("TinyMatrix", "[algebra]")
 {
+  REQUIRE(TinyMatrix<1, 1, int>::Dimension == 1);
+  REQUIRE(TinyMatrix<1, 1, int>::NumberOfRows == 1);
+  REQUIRE(TinyMatrix<1, 1, int>::NumberOfColumns == 1);
+  REQUIRE(TinyMatrix<2, 3, int>::Dimension == 6);
+  REQUIRE(TinyMatrix<2, 3, int>::NumberOfRows == 2);
+  REQUIRE(TinyMatrix<2, 3, int>::NumberOfColumns == 3);
+  REQUIRE(TinyMatrix<5, 4, int>::Dimension == 20);
+  REQUIRE(TinyMatrix<5, 4, int>::NumberOfRows == 5);
+  REQUIRE(TinyMatrix<5, 4, int>::NumberOfColumns == 4);
+
   TinyMatrix<3, 4, int> A(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12);
   REQUIRE(((A(0, 0) == 1) and (A(0, 1) == 2) and (A(0, 2) == 3) and (A(0, 3) == 4) and   //
            (A(1, 0) == 5) and (A(1, 1) == 6) and (A(1, 2) == 7) and (A(1, 3) == 8) and   //
diff --git a/tests/test_TinyVector.cpp b/tests/test_TinyVector.cpp
index cf77a22325ee19137367ab68eff4f2f5a817ef6a..0613e721cbce46386e6b1c86c3bc3e1123fdd1aa 100644
--- a/tests/test_TinyVector.cpp
+++ b/tests/test_TinyVector.cpp
@@ -15,6 +15,11 @@ template class TinyVector<3, int>;
 
 TEST_CASE("TinyVector", "[algebra]")
 {
+  REQUIRE(TinyVector<1, int>::Dimension == 1);
+  REQUIRE(TinyVector<2, int>::Dimension == 2);
+  REQUIRE(TinyVector<3, int>::Dimension == 3);
+  REQUIRE(TinyVector<4, int>::Dimension == 4);
+
   TinyVector<3, int> v(1, 2, 3);
   REQUIRE(((v[0] == 1) and (v[1] == 2) and (v[2] == 3)));
   REQUIRE(-v == TinyVector<3, int>(-1, -2, -3));
diff --git a/tests/test_TriangleGaussQuadrature.cpp b/tests/test_TriangleGaussQuadrature.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4ed18d60284861241861c45dd744a8e1b38c794b
--- /dev/null
+++ b/tests/test_TriangleGaussQuadrature.cpp
@@ -0,0 +1,632 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <analysis/TriangleGaussQuadrature.hpp>
+#include <geometry/TriangleTransformation.hpp>
+#include <utils/Exceptions.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("TriangleGaussQuadrature", "[analysis]")
+{
+  auto integrate = [](auto f, auto quadrature_formula) {
+    using R2 = TinyVector<2>;
+
+    const R2 A{-1, -1};
+    const R2 B{+1, -1};
+    const R2 C{-1, +1};
+
+    TriangleTransformation<2> t{A, B, C};
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(t(point_list[0]));
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(t(point_list[i]));
+    }
+
+    return t.jacobianDeterminant() * value;
+  };
+
+  auto integrate_on_triangle = [](auto f, auto quadrature_formula, const TinyVector<2>& a, const TinyVector<2>& b,
+                                  const TinyVector<2>& c) {
+    TriangleTransformation<2> t(a, b, c);
+
+    auto point_list  = quadrature_formula.pointList();
+    auto weight_list = quadrature_formula.weightList();
+
+    auto value = weight_list[0] * f(t(point_list[0]));
+    for (size_t i = 1; i < weight_list.size(); ++i) {
+      value += weight_list[i] * f(t(point_list[i]));
+    }
+
+    return t.jacobianDeterminant() * value;
+  };
+
+  auto get_order = [&integrate, &integrate_on_triangle](auto f, auto quadrature_formula, const double exact_value) {
+    using R2               = TinyVector<2>;
+    const double int_T_hat = integrate(f, quadrature_formula);
+    const double int_refined   //
+      = integrate_on_triangle(f, quadrature_formula, R2{-1, -1}, R2{0, -1}, R2{-1, 0}) +
+        integrate_on_triangle(f, quadrature_formula, R2{0, -1}, R2{0, 0}, R2{-1, 0}) +
+        integrate_on_triangle(f, quadrature_formula, R2{0, -1}, R2{1, -1}, R2{0, 0}) +
+        integrate_on_triangle(f, quadrature_formula, R2{-1, 0}, R2{0, 0}, R2{-1, 1});
+
+    return -std::log(std::abs(int_refined - exact_value) / std::abs(int_T_hat - exact_value)) / std::log(2);
+  };
+
+  auto p0 = [](const TinyVector<2>&) { return 2; };
+  auto p1 = [](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return 2 * x + 3 * y + 1;
+  };
+  auto p2 = [&p1](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p1(X) * (2.5 * x - 3 * y + 3);
+  };
+  auto p3 = [&p2](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p2(X) * (-1.5 * x + 3 * y - 3);
+  };
+  auto p4 = [&p3](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p3(X) * (x + y + 1);
+  };
+  auto p5 = [&p4](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p4(X) * (-0.2 * x - 1.3 * y - 0.7);
+  };
+  auto p6 = [&p5](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p5(X) * (3 * x - 2 * y + 3);
+  };
+  auto p7 = [&p6](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p6(X) * (-2 * x + 4 * y - 7);
+  };
+  auto p8 = [&p7](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p7(X) * (0.2 * x - 0.3 * y - 0.3);
+  };
+  auto p9 = [&p8](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p8(X) * (0.9 * x + 0.6 * y - 0.4);
+  };
+  auto p10 = [&p9](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p9(X) * (-0.1 * x + 0.3 * y + 0.6);
+  };
+  auto p11 = [&p10](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p10(X) * (0.7 * x - 0.6 * y + 0.2);
+  };
+  auto p12 = [&p11](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p11(X) * (0.2 * x + 0.3 * y - 0.4);
+  };
+  auto p13 = [&p12](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p12(X) * (-0.9 * x + 0.4 * y + 0.3);
+  };
+  auto p14 = [&p13](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p13(X) * (0.7 * x - 0.8 * y - 0.9);
+  };
+  auto p15 = [&p14](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p14(X) * (-0.9 * x + 0.4 * y + 0.2);
+  };
+  auto p16 = [&p15](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p15(X) * (-1.3 * x - 1.2 * y - 0.9);
+  };
+  auto p17 = [&p16](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p16(X) * (0.7 * x + 0.6 * y - 0.2);
+  };
+  auto p18 = [&p17](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p17(X) * (-0.2 * x + 0.5 * y + 0.7);
+  };
+  auto p19 = [&p18](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p18(X) * (0.4 * x - 0.7 * y - 0.3);
+  };
+  auto p20 = [&p19](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p19(X) * (-0.1 * x + 0.8 * y - 0.7);
+  };
+  auto p21 = [&p20](const TinyVector<2>& X) {
+    const double x = X[0];
+    const double y = X[1];
+    return p20(X) * (0.5 * x - 0.2 * y + 0.3);
+  };
+
+  SECTION("degree 1")
+  {
+    const QuadratureFormula<2>& l1 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(1));
+
+    REQUIRE(l1.numberOfPoints() == 1);
+
+    REQUIRE(integrate(p0, l1) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l1) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l1) != Catch::Approx(-19. / 3));
+
+    REQUIRE(get_order(p2, l1, -19. / 3) == Catch::Approx(2));
+  }
+
+  SECTION("degree 2")
+  {
+    const QuadratureFormula<2>& l2 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(2));
+
+    REQUIRE(l2.numberOfPoints() == 3);
+
+    REQUIRE(integrate(p0, l2) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l2) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l2) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l2) != Catch::Approx(146. / 5));
+
+    // In a weird way, the formula achieves 4th order on this test
+    REQUIRE(get_order(p3, l2, 146. / 5) == Catch::Approx(4));
+  }
+
+  SECTION("degree 3 and 4")
+  {
+    const QuadratureFormula<2>& l3 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(3));
+    const QuadratureFormula<2>& l4 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(4));
+
+    bool is_same = true;
+    is_same &= (l3.numberOfPoints() == l4.numberOfPoints());
+    for (size_t i = 0; i < l3.pointList().size(); ++i) {
+      is_same &= (l3.point(i) == l3.point(i));
+      is_same &= (l3.weight(i) == l3.weight(i));
+    }
+
+    REQUIRE(is_same);
+
+    REQUIRE(l4.numberOfPoints() == 6);
+
+    REQUIRE(integrate(p0, l4) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l4) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l4) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l4) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l4) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l4) != Catch::Approx(-17. / 10));
+
+    // In a weird way, the formula achieves 6th order on this test
+    REQUIRE(get_order(p5, l4, -17. / 10) == Catch::Approx(6));
+  }
+
+  SECTION("degree 5")
+  {
+    const QuadratureFormula<2>& l5 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(5));
+
+    REQUIRE(l5.numberOfPoints() == 7);
+
+    REQUIRE(integrate(p0, l5) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l5) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l5) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l5) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l5) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l5) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l5) != Catch::Approx(-197. / 175));
+
+    REQUIRE(get_order(p6, l5, -197. / 175) == Catch::Approx(6));
+  }
+
+  SECTION("degree 6")
+  {
+    const QuadratureFormula<2>& l6 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(6));
+
+    REQUIRE(l6.numberOfPoints() == 12);
+
+    REQUIRE(integrate(p0, l6) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l6) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l6) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l6) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l6) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l6) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l6) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l6) != Catch::Approx(-4507. / 1575));
+
+    REQUIRE(get_order(p7, l6, -4507. / 1575) == Catch::Approx(8));
+  }
+
+  SECTION("degree 7")
+  {
+    const QuadratureFormula<2>& l7 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(7));
+
+    REQUIRE(l7.numberOfPoints() == 15);
+
+    REQUIRE(integrate(p0, l7) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l7) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l7) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l7) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l7) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l7) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l7) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l7) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l7) != Catch::Approx(-23867. / 1575));
+
+    REQUIRE(get_order(p8, l7, -23867. / 1575) == Catch::Approx(8));
+  }
+
+  SECTION("degree 8")
+  {
+    const QuadratureFormula<2>& l8 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(8));
+
+    REQUIRE(l8.numberOfPoints() == 16);
+
+    REQUIRE(integrate(p0, l8) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l8) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l8) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l8) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l8) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l8) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l8) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l8) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l8) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l8) != Catch::Approx(7782251. / 346500));
+
+    // In a weird way, the formula achieves 10th order on this test
+    REQUIRE(get_order(p9, l8, 7782251. / 346500) == Catch::Approx(10));
+  }
+
+  SECTION("degree 9")
+  {
+    const QuadratureFormula<2>& l9 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(9));
+
+    REQUIRE(l9.numberOfPoints() == 19);
+
+    REQUIRE(integrate(p0, l9) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l9) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l9) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l9) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l9) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l9) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l9) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l9) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l9) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l9) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l9) != Catch::Approx(126885809. / 14437500));
+
+    REQUIRE(get_order(p10, l9, 126885809. / 14437500) == Catch::Approx(10));
+  }
+
+  SECTION("degree 10")
+  {
+    const QuadratureFormula<2>& l10 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(10));
+
+    REQUIRE(l10.numberOfPoints() == 25);
+
+    REQUIRE(integrate(p0, l10) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l10) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l10) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l10) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l10) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l10) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l10) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l10) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l10) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l10) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l10) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l10) != Catch::Approx(22133453663. / 11261250000));
+
+    // In a weird way, the formula achieves 12th order on this test
+    REQUIRE(get_order(p11, l10, 22133453663. / 11261250000) == Catch::Approx(12));
+  }
+
+  SECTION("degree 11")
+  {
+    const QuadratureFormula<2>& l11 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(11));
+
+    REQUIRE(l11.numberOfPoints() == 28);
+
+    REQUIRE(integrate(p0, l11) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l11) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l11) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l11) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l11) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l11) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l11) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l11) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l11) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l11) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l11) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l11) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l11) != Catch::Approx(-419453736959. / 262762500000));
+
+    REQUIRE(get_order(p12, l11, -419453736959. / 262762500000) == Catch::Approx(12));
+  }
+
+  SECTION("degree 12")
+  {
+    const QuadratureFormula<2>& l12 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(12));
+
+    REQUIRE(l12.numberOfPoints() == 33);
+
+    REQUIRE(integrate(p0, l12) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l12) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l12) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l12) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l12) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l12) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l12) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l12) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l12) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l12) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l12) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l12) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l12) == Catch::Approx(-419453736959. / 262762500000));
+    REQUIRE(integrate(p13, l12) != Catch::Approx(-3625092349117. / 7882875000000));
+
+    // In a weird way, the formula achieves 14th order on this test
+    REQUIRE(get_order(p13, l12, -3625092349117. / 7882875000000) == Catch::Approx(14));
+  }
+
+  SECTION("degree 13")
+  {
+    const QuadratureFormula<2>& l13 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(13));
+
+    REQUIRE(l13.numberOfPoints() == 37);
+
+    REQUIRE(integrate(p0, l13) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l13) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l13) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l13) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l13) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l13) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l13) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l13) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l13) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l13) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l13) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l13) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l13) == Catch::Approx(-419453736959. / 262762500000));
+    REQUIRE(integrate(p13, l13) == Catch::Approx(-3625092349117. / 7882875000000));
+    REQUIRE(integrate(p14, l13) != Catch::Approx(541632196607. / 1642265625000));
+
+    REQUIRE(get_order(p14, l13, 541632196607. / 1642265625000) == Catch::Approx(14));
+  }
+
+  SECTION("degree 14")
+  {
+    const QuadratureFormula<2>& l14 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(14));
+
+    REQUIRE(l14.numberOfPoints() == 42);
+
+    REQUIRE(integrate(p0, l14) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l14) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l14) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l14) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l14) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l14) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l14) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l14) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l14) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l14) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l14) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l14) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l14) == Catch::Approx(-419453736959. / 262762500000));
+    REQUIRE(integrate(p13, l14) == Catch::Approx(-3625092349117. / 7882875000000));
+    REQUIRE(integrate(p14, l14) == Catch::Approx(541632196607. / 1642265625000));
+    REQUIRE(integrate(p15, l14) != Catch::Approx(-287809833862769. / 3350221875000000));
+
+    // In a weird way, the formula achieves 16th order on this test
+    REQUIRE(get_order(p15, l14, -287809833862769. / 3350221875000000) == Catch::Approx(16));
+  }
+
+  SECTION("degree 15")
+  {
+    const QuadratureFormula<2>& l15 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(15));
+
+    REQUIRE(l15.numberOfPoints() == 49);
+
+    REQUIRE(integrate(p0, l15) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l15) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l15) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l15) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l15) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l15) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l15) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l15) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l15) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l15) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l15) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l15) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l15) == Catch::Approx(-419453736959. / 262762500000));
+    REQUIRE(integrate(p13, l15) == Catch::Approx(-3625092349117. / 7882875000000));
+    REQUIRE(integrate(p14, l15) == Catch::Approx(541632196607. / 1642265625000));
+    REQUIRE(integrate(p15, l15) == Catch::Approx(-287809833862769. / 3350221875000000));
+    REQUIRE(integrate(p16, l15) != Catch::Approx(3340311405172793. / 6700443750000000).epsilon(1E-10));
+
+    REQUIRE(get_order(p16, l15, 3340311405172793. / 6700443750000000) == Catch::Approx(16));
+  }
+
+  SECTION("degree 16")
+  {
+    const QuadratureFormula<2>& l16 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(16));
+
+    REQUIRE(l16.numberOfPoints() == 55);
+
+    REQUIRE(integrate(p0, l16) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l16) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l16) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l16) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l16) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l16) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l16) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l16) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l16) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l16) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l16) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l16) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l16) == Catch::Approx(-419453736959. / 262762500000));
+    REQUIRE(integrate(p13, l16) == Catch::Approx(-3625092349117. / 7882875000000));
+    REQUIRE(integrate(p14, l16) == Catch::Approx(541632196607. / 1642265625000));
+    REQUIRE(integrate(p15, l16) == Catch::Approx(-287809833862769. / 3350221875000000));
+    REQUIRE(integrate(p16, l16) == Catch::Approx(3340311405172793. / 6700443750000000));
+    REQUIRE(integrate(p17, l16) != Catch::Approx(-8386984372282772827. / 19096264687500000000.).epsilon(1E-10));
+
+    // In a weird way, the formula achieves ~18th order on this test
+    REQUIRE(get_order(p17, l16, -8386984372282772827. / 19096264687500000000.) == Catch::Approx(18).margin(1E-2));
+  }
+
+  SECTION("degree 17")
+  {
+    const QuadratureFormula<2>& l17 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(17));
+
+    REQUIRE(l17.numberOfPoints() == 60);
+
+    REQUIRE(integrate(p0, l17) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l17) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l17) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l17) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l17) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l17) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l17) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l17) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l17) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l17) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l17) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l17) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l17) == Catch::Approx(-419453736959. / 262762500000));
+    REQUIRE(integrate(p13, l17) == Catch::Approx(-3625092349117. / 7882875000000));
+    REQUIRE(integrate(p14, l17) == Catch::Approx(541632196607. / 1642265625000));
+    REQUIRE(integrate(p15, l17) == Catch::Approx(-287809833862769. / 3350221875000000));
+    REQUIRE(integrate(p16, l17) == Catch::Approx(3340311405172793. / 6700443750000000));
+    REQUIRE(integrate(p17, l17) == Catch::Approx(-8386984372282772827. / 19096264687500000000.));
+    REQUIRE(integrate(p18, l17) != Catch::Approx(-1599289493784003137. / 6820094531250000000.).epsilon(1E-10));
+
+    REQUIRE(get_order(p18, l17, -1599289493784003137. / 6820094531250000000.) == Catch::Approx(18).margin(1E-2));
+  }
+
+  SECTION("degree 18")
+  {
+    const QuadratureFormula<2>& l18 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(18));
+
+    REQUIRE(l18.numberOfPoints() == 67);
+
+    REQUIRE(integrate(p0, l18) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l18) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l18) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l18) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l18) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l18) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l18) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l18) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l18) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l18) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l18) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l18) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l18) == Catch::Approx(-419453736959. / 262762500000));
+    REQUIRE(integrate(p13, l18) == Catch::Approx(-3625092349117. / 7882875000000));
+    REQUIRE(integrate(p14, l18) == Catch::Approx(541632196607. / 1642265625000));
+    REQUIRE(integrate(p15, l18) == Catch::Approx(-287809833862769. / 3350221875000000));
+    REQUIRE(integrate(p16, l18) == Catch::Approx(3340311405172793. / 6700443750000000));
+    REQUIRE(integrate(p17, l18) == Catch::Approx(-8386984372282772827. / 19096264687500000000.));
+    REQUIRE(integrate(p18, l18) == Catch::Approx(-1599289493784003137. / 6820094531250000000.));
+    REQUIRE(integrate(p19, l18) != Catch::Approx(5280879341958226453. / 47740661718750000000.).epsilon(1E-10));
+
+    // In a weird way, the formula achieves ~20th order on this test
+    REQUIRE(get_order(p19, l18, 5280879341958226453. / 47740661718750000000.) == Catch::Approx(20).margin(0.1));
+  }
+
+  SECTION("degree 19")
+  {
+    const QuadratureFormula<2>& l19 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(19));
+
+    REQUIRE(l19.numberOfPoints() == 73);
+
+    REQUIRE(integrate(p0, l19) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l19) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l19) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l19) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l19) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l19) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l19) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l19) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l19) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l19) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l19) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l19) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l19) == Catch::Approx(-419453736959. / 262762500000));
+    REQUIRE(integrate(p13, l19) == Catch::Approx(-3625092349117. / 7882875000000));
+    REQUIRE(integrate(p14, l19) == Catch::Approx(541632196607. / 1642265625000));
+    REQUIRE(integrate(p15, l19) == Catch::Approx(-287809833862769. / 3350221875000000));
+    REQUIRE(integrate(p16, l19) == Catch::Approx(3340311405172793. / 6700443750000000));
+    REQUIRE(integrate(p17, l19) == Catch::Approx(-8386984372282772827. / 19096264687500000000.));
+    REQUIRE(integrate(p18, l19) == Catch::Approx(-1599289493784003137. / 6820094531250000000.));
+    REQUIRE(integrate(p19, l19) == Catch::Approx(5280879341958226453. / 47740661718750000000.));
+    REQUIRE(integrate(p20, l19) != Catch::Approx(1390615013183923183. / 85251181640625000000.).epsilon(1E-10));
+
+    REQUIRE(get_order(p20, l19, 1390615013183923183. / 85251181640625000000.) == Catch::Approx(20).margin(0.1));
+  }
+
+  SECTION("degree 20")
+  {
+    const QuadratureFormula<2>& l20 = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(20));
+
+    REQUIRE(l20.numberOfPoints() == 79);
+
+    REQUIRE(integrate(p0, l20) == Catch::Approx(4));
+    REQUIRE(integrate(p1, l20) == Catch::Approx(-4. / 3));
+    REQUIRE(integrate(p2, l20) == Catch::Approx(-19. / 3));
+    REQUIRE(integrate(p3, l20) == Catch::Approx(146. / 5));
+    REQUIRE(integrate(p4, l20) == Catch::Approx(-25. / 6));
+    REQUIRE(integrate(p5, l20) == Catch::Approx(-17. / 10));
+    REQUIRE(integrate(p6, l20) == Catch::Approx(-197. / 175));
+    REQUIRE(integrate(p7, l20) == Catch::Approx(-4507. / 1575));
+    REQUIRE(integrate(p8, l20) == Catch::Approx(-23867. / 1575));
+    REQUIRE(integrate(p9, l20) == Catch::Approx(7782251. / 346500));
+    REQUIRE(integrate(p10, l20) == Catch::Approx(126885809. / 14437500));
+    REQUIRE(integrate(p11, l20) == Catch::Approx(22133453663. / 11261250000));
+    REQUIRE(integrate(p12, l20) == Catch::Approx(-419453736959. / 262762500000));
+    REQUIRE(integrate(p13, l20) == Catch::Approx(-3625092349117. / 7882875000000));
+    REQUIRE(integrate(p14, l20) == Catch::Approx(541632196607. / 1642265625000));
+    REQUIRE(integrate(p15, l20) == Catch::Approx(-287809833862769. / 3350221875000000));
+    REQUIRE(integrate(p16, l20) == Catch::Approx(3340311405172793. / 6700443750000000));
+    REQUIRE(integrate(p17, l20) == Catch::Approx(-8386984372282772827. / 19096264687500000000.));
+    REQUIRE(integrate(p18, l20) == Catch::Approx(-1599289493784003137. / 6820094531250000000.));
+    REQUIRE(integrate(p19, l20) == Catch::Approx(5280879341958226453. / 47740661718750000000.));
+    REQUIRE(integrate(p20, l20) == Catch::Approx(1390615013183923183. / 85251181640625000000.));
+    REQUIRE(integrate(p21, l20) != Catch::Approx(-520205676970121316953. / 215685489550781250000000.).epsilon(1E-10));
+
+    // In a weird way, the formula achieves ~22th order on this test
+    REQUIRE(get_order(p21, l20, -520205676970121316953. / 215685489550781250000000.) == Catch::Approx(22).margin(0.5));
+  }
+
+  SECTION("max implemented degree")
+  {
+    REQUIRE(QuadratureManager::instance().maxTriangleDegree(QuadratureType::Gauss) ==
+            TriangleGaussQuadrature::max_degree);
+    REQUIRE_THROWS_WITH(QuadratureManager::instance().getTriangleFormula(
+                          GaussQuadratureDescriptor(TriangleGaussQuadrature::max_degree + 1)),
+                        "error: Gauss quadrature formulae handle degrees up to " +
+                          std::to_string(TriangleGaussQuadrature::max_degree) + " on triangles");
+  }
+}
diff --git a/tests/test_TriangleTransformation.cpp b/tests/test_TriangleTransformation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..69803b7081a4ddbd529ce9288b2c14973832ed48
--- /dev/null
+++ b/tests/test_TriangleTransformation.cpp
@@ -0,0 +1,142 @@
+#include <catch2/catch_approx.hpp>
+#include <catch2/catch_test_macros.hpp>
+
+#include <analysis/GaussQuadratureDescriptor.hpp>
+#include <analysis/QuadratureManager.hpp>
+#include <geometry/TriangleTransformation.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("TriangleTransformation", "[geometry]")
+{
+  SECTION("2D")
+  {
+    using R2 = TinyVector<2>;
+
+    const R2 a{1, 2};
+    const R2 b{3, 1};
+    const R2 c{2, 5};
+
+    const TriangleTransformation<2> t(a, b, c);
+
+    REQUIRE(t(R2{0, 0})[0] == Catch::Approx(1));
+    REQUIRE(t(R2{0, 0})[1] == Catch::Approx(2));
+
+    REQUIRE(t(R2{1, 0})[0] == Catch::Approx(3));
+    REQUIRE(t(R2{1, 0})[1] == Catch::Approx(1));
+
+    REQUIRE(t(R2{0, 1})[0] == Catch::Approx(2));
+    REQUIRE(t(R2{0, 1})[1] == Catch::Approx(5));
+
+    REQUIRE(t(R2{1. / 3, 1. / 3})[0] == Catch::Approx(2));
+    REQUIRE(t(R2{1. / 3, 1. / 3})[1] == Catch::Approx(8. / 3));
+
+    REQUIRE(t.jacobianDeterminant() == Catch::Approx(7));
+
+    SECTION("Polynomial integral")
+    {
+      auto p = [](const R2& X) {
+        const double x = X[0];
+        const double y = X[1];
+        return 2 * x * x + 3 * x * y + y * y + 3 * y + 1;
+      };
+
+      QuadratureFormula<2> qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(2));
+
+      double sum = 0;
+      for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+        sum += qf.weight(i) * t.jacobianDeterminant() * p(t(qf.point(i)));
+      }
+
+      REQUIRE(sum == Catch::Approx(3437. / 24));
+    }
+  }
+
+  SECTION("3D")
+  {
+    SECTION("Data")
+    {
+      using R2 = TinyVector<2>;
+      using R3 = TinyVector<3>;
+
+      const R3 a{1, 2, 2};
+      const R3 b{4, 1, 3};
+      const R3 c{2, 5, 1};
+
+      const TriangleTransformation<3> t(a, b, c);
+
+      REQUIRE(t(R2{0, 0})[0] == Catch::Approx(1));
+      REQUIRE(t(R2{0, 0})[1] == Catch::Approx(2));
+      REQUIRE(t(R2{0, 0})[2] == Catch::Approx(2));
+
+      REQUIRE(t(R2{1, 0})[0] == Catch::Approx(4));
+      REQUIRE(t(R2{1, 0})[1] == Catch::Approx(1));
+      REQUIRE(t(R2{1, 0})[2] == Catch::Approx(3));
+
+      REQUIRE(t(R2{0, 1})[0] == Catch::Approx(2));
+      REQUIRE(t(R2{0, 1})[1] == Catch::Approx(5));
+      REQUIRE(t(R2{0, 1})[2] == Catch::Approx(1));
+
+      REQUIRE(t(R2{1. / 3, 1. / 3})[0] == Catch::Approx(7. / 3));
+      REQUIRE(t(R2{1. / 3, 1. / 3})[1] == Catch::Approx(8. / 3));
+      REQUIRE(t(R2{1. / 3, 1. / 3})[2] == Catch::Approx(2));
+
+      REQUIRE(t.areaVariationNorm() == Catch::Approx(2 * std::sqrt(30)));
+    }
+
+    SECTION("Area modulus")
+    {
+      using R3 = TinyVector<3>;
+
+      const R3 a_hat{0, 0, 0};
+      const R3 b_hat{1, 0, 0};
+      const R3 c_hat{0, 1, 0};
+
+      const double theta = 2.3;
+      TinyMatrix<3> r_theta(1, 0, 0,                               //
+                            0, std::cos(theta), std::sin(theta),   //
+                            0, -std::sin(theta), std::cos(theta));
+
+      const double phi = 0.7;
+      TinyMatrix<3> r_phi(std::cos(phi), std::sin(phi), 0,    //
+                          -std::sin(phi), std::cos(phi), 0,   //
+                          0, 0, 1);
+
+      const R3 a = r_phi * r_theta * a_hat;
+      const R3 b = r_phi * r_theta * b_hat;
+      const R3 c = r_phi * r_theta * c_hat;
+
+      const TriangleTransformation<3> t(a, b, c);
+
+      // The triangle (a,b,c) is just a rotation of the initial one
+      REQUIRE(t.areaVariationNorm() == Catch::Approx(1));
+    }
+
+    SECTION("Polynomial integral")
+    {
+      using R3 = TinyVector<3>;
+
+      const R3 a{1, 2, 2};
+      const R3 b{4, 1, 3};
+      const R3 c{2, 5, 1};
+
+      const TriangleTransformation<3> t(a, b, c);
+
+      auto p = [](const R3& X) {
+        const double x = X[0];
+        const double y = X[1];
+        const double z = X[2];
+        return 2 * x * x + 3 * x - 3 * y * y + y + 2 * z * z - 0.5 * z + 2;
+      };
+
+      QuadratureFormula<2> qf = QuadratureManager::instance().getTriangleFormula(GaussQuadratureDescriptor(2));
+
+      double sum = 0;
+      for (size_t i = 0; i < qf.numberOfPoints(); ++i) {
+        sum += qf.weight(i) * t.areaVariationNorm() * p(t(qf.point(i)));
+      }
+
+      REQUIRE(sum == Catch::Approx(39.2534499545369));
+    }
+  }
+}
diff --git a/tests/test_UnaryExpressionProcessor.cpp b/tests/test_UnaryExpressionProcessor.cpp
index f63e45d1fa9912f1d007f83a2759bfc5b7316b42..2c3f6acb8eb7f8280e8717cdf495abd341dec5d0 100644
--- a/tests/test_UnaryExpressionProcessor.cpp
+++ b/tests/test_UnaryExpressionProcessor.cpp
@@ -1,6 +1,8 @@
 #include <catch2/catch_test_macros.hpp>
 #include <catch2/matchers/catch_matchers_all.hpp>
 
+#include <FixturesForBuiltinT.hpp>
+
 #include <language/ast/ASTBuilder.hpp>
 #include <language/ast/ASTModulesImporter.hpp>
 #include <language/ast/ASTNodeDataTypeBuilder.hpp>
@@ -8,6 +10,14 @@
 #include <language/ast/ASTNodeExpressionBuilder.hpp>
 #include <language/ast/ASTNodeTypeCleaner.hpp>
 #include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/node_processor/UnaryExpressionProcessor.hpp>
+#include <language/utils/ASTNodeDataTypeTraits.hpp>
+#include <language/utils/AffectationProcessorBuilder.hpp>
+#include <language/utils/BasicAffectationRegistrerFor.hpp>
+#include <language/utils/DataHandler.hpp>
+#include <language/utils/OperatorRepository.hpp>
+#include <language/utils/TypeDescriptor.hpp>
+#include <language/utils/UnaryOperatorProcessorBuilder.hpp>
 #include <utils/Demangle.hpp>
 
 #include <pegtl/string_input.hpp>
@@ -66,37 +76,98 @@ TEST_CASE("UnaryExpressionProcessor", "[language]")
     CHECK_UNARY_EXPRESSION_RESULT(R"(let r:R, r = 2; r = -r;)", "r", -2.);
   }
 
+  SECTION("unary minus [builtin]")
+  {
+    std::string_view data = R"(let r:builtin_t, r = -bt;)";
+
+    TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"};
+    auto ast = ASTBuilder::build(input);
+
+    ASTModulesImporter{*ast};
+
+    BasicAffectationRegisterFor<EmbeddedData>{ASTNodeDataType::build<ASTNodeDataType::type_id_t>("builtin_t")};
+
+    OperatorRepository& repository = OperatorRepository::instance();
+
+    repository.addUnaryOperator<language::unary_minus>(
+      std::make_shared<UnaryOperatorProcessorBuilder<language::unary_minus, std::shared_ptr<const double>,
+                                                     std::shared_ptr<const double>>>());
+
+    SymbolTable& symbol_table = *ast->m_symbol_table;
+    auto [i_symbol, success]  = symbol_table.add(builtin_data_type.nameOfTypeId(), ast->begin());
+    if (not success) {
+      throw UnexpectedError("cannot add '" + builtin_data_type.nameOfTypeId() + "' type for testing");
+    }
+
+    i_symbol->attributes().setDataType(ASTNodeDataType::build<ASTNodeDataType::type_name_id_t>());
+    i_symbol->attributes().setIsInitialized();
+    i_symbol->attributes().value() = symbol_table.typeEmbedderTable().size();
+    symbol_table.typeEmbedderTable().add(std::make_shared<TypeDescriptor>(builtin_data_type.nameOfTypeId()));
+
+    auto [i_symbol_bt, success_bt] = symbol_table.add("bt", ast->begin());
+    if (not success_bt) {
+      throw UnexpectedError("cannot add 'bt' of type builtin_t for testing");
+    }
+    i_symbol_bt->attributes().setDataType(ast_node_data_type_from<std::shared_ptr<const double>>);
+    i_symbol_bt->attributes().setIsInitialized();
+    i_symbol_bt->attributes().value() =
+      EmbeddedData(std::make_shared<DataHandler<const double>>(std::make_shared<double>(3.2)));
+
+    ASTNodeTypeCleaner<language::import_instruction>{*ast};
+
+    ASTSymbolTableBuilder{*ast};
+    ASTNodeDataTypeBuilder{*ast};
+
+    ASTNodeDeclarationToAffectationConverter{*ast};
+    ASTNodeTypeCleaner<language::var_declaration>{*ast};
+
+    ASTNodeExpressionBuilder{*ast};
+    ExecutionPolicy exec_policy;
+    ast->execute(exec_policy);
+
+    using namespace TAO_PEGTL_NAMESPACE;
+    position use_position{internal::iterator{"fixture"}, "fixture"};
+    use_position.byte    = 10000;
+    auto [symbol, found] = symbol_table.find("r", use_position);
+
+    auto attributes     = symbol->attributes();
+    auto embedded_value = std::get<EmbeddedData>(attributes.value());
+
+    double value = *dynamic_cast<const DataHandler<const double>&>(embedded_value.get()).data_ptr();
+    REQUIRE(value == double{-3.2});
+  }
+
   SECTION("unary not")
   {
     CHECK_UNARY_EXPRESSION_RESULT(R"(let b:B, b = false; b = not b;)", "b", true);
     CHECK_UNARY_EXPRESSION_RESULT(R"(let b:B, b = true; b = not b;)", "b", false);
+  }
 
-    SECTION("errors")
+  SECTION("errors")
+  {
+    SECTION("undefined not operator")
     {
-      SECTION("undefined not operator")
-      {
-        auto error_message = [](std::string type_name) {
-          return std::string{R"(undefined unary operator
+      auto error_message = [](std::string type_name) {
+        return std::string{R"(undefined unary operator
 note: unexpected operand type )"} +
-                 type_name;
-        };
-
-        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(let n:N, n = 0; not n;)", error_message("N"));
-        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1;)", error_message("Z"));
-        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1.3;)", error_message("R"));
-        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not "foo";)", error_message("string"));
-      }
-
-      SECTION("undefined unary minus operator")
-      {
-        auto error_message = [](std::string type_name) {
-          return std::string{R"(undefined unary operator
+               type_name;
+      };
+
+      CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(let n:N, n = 0; not n;)", error_message("N"));
+      CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1;)", error_message("Z"));
+      CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not 1.3;)", error_message("R"));
+      CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(not "foo";)", error_message("string"));
+    }
+
+    SECTION("undefined unary minus operator")
+    {
+      auto error_message = [](std::string type_name) {
+        return std::string{R"(undefined unary operator
 note: unexpected operand type )"} +
-                 type_name;
-        };
+               type_name;
+      };
 
-        CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(-"foo";)", error_message("string"));
-      }
+      CHECK_UNARY_EXPRESSION_THROWS_WITH(R"(-"foo";)", error_message("string"));
     }
   }
 }
diff --git a/tests/test_UnaryOperatorMangler.cpp b/tests/test_UnaryOperatorMangler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c533e882a445818923e0b7bd3719051eb5db8867
--- /dev/null
+++ b/tests/test_UnaryOperatorMangler.cpp
@@ -0,0 +1,17 @@
+#include <catch2/catch_test_macros.hpp>
+#include <catch2/matchers/catch_matchers_all.hpp>
+
+#include <language/utils/UnaryOperatorMangler.hpp>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("UnaryOperatorMangler", "[language]")
+{
+  SECTION("unary operators")
+  {
+    const ASTNodeDataType Z = ASTNodeDataType::build<ASTNodeDataType::int_t>();
+
+    REQUIRE(unaryOperatorMangler<language::unary_minus>(Z) == "- Z");
+    REQUIRE(unaryOperatorMangler<language::unary_not>(Z) == "not Z");
+  }
+}
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index 9c03ba9213ece8947688036ca7d5554c8e38a279..91d9a080ae7fc9957a2088bd9213217b1d3d52f5 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -2,9 +2,10 @@
 
 #include <Kokkos_Core.hpp>
 
+#include <analysis/QuadratureManager.hpp>
 #include <language/utils/OperatorRepository.hpp>
-#include <mesh/DiamondDualConnectivityManager.hpp>
-#include <mesh/DiamondDualMeshManager.hpp>
+#include <mesh/DualConnectivityManager.hpp>
+#include <mesh/DualMeshManager.hpp>
 #include <mesh/MeshDataManager.hpp>
 #include <mesh/SynchronizerManager.hpp>
 #include <utils/Messenger.hpp>
@@ -36,9 +37,10 @@ main(int argc, char* argv[])
 
       SynchronizerManager::create();
       RandomEngine::create();
+      QuadratureManager::create();
       MeshDataManager::create();
-      DiamondDualConnectivityManager::create();
-      DiamondDualMeshManager::create();
+      DualConnectivityManager::create();
+      DualMeshManager::create();
 
       MeshDataBaseForTests::create();
 
@@ -50,9 +52,10 @@ main(int argc, char* argv[])
 
       MeshDataBaseForTests::destroy();
 
-      DiamondDualMeshManager::destroy();
-      DiamondDualConnectivityManager::destroy();
+      DualMeshManager::destroy();
+      DualConnectivityManager::destroy();
       MeshDataManager::destroy();
+      QuadratureManager::destroy();
       RandomEngine::destroy();
       SynchronizerManager::destroy();
     }
diff --git a/tools/docker-pugs.sh b/tools/docker-pugs.sh
index af54df67c963f16e3d3931aeaeba66b536da3d7a..4a5b0d6611b2e62b6bf84adcfdd0805add61e7b0 100755
--- a/tools/docker-pugs.sh
+++ b/tools/docker-pugs.sh
@@ -35,30 +35,28 @@ mkdir -p ${DOCKERFILE_DIR}
 DOCKERFILE="${DOCKERFILE_DIR}/Dockerfile"
 
 cat > ${DOCKERFILE} <<EOF
-FROM ubuntu:bionic
+FROM ubuntu:focal
 
+ARG DEBIAN_FRONTEND=noninteractive
 ENV USER="${USER}" USER_ID="${USER_ID}" USER_GID="${USER_GID}" HOSTNAME="${DOCKER_HOSTNAME}"
-
+ENV TZ=Europe/Paris
 RUN echo "${DOCKER_HOSTNAME}" > /etc/hostname
 RUN groupadd --gid "${USER_GID}" "${USER}"
 RUN useradd --uid "${USER_ID}" --gid "${USER_GID}" --create-home --shell /bin/bash "${USER}"
 
-RUN apt-get update && apt-get -y upgrade && apt-get -y remove g++ gcc  && apt-get -y install cmake git make lcov bc gnupg gnupg2 gnupg1 wget
-
-RUN echo 'deb http://apt.llvm.org/bionic/ llvm-toolchain-bionic-8 main' > /etc/apt/sources.list.d/backports.list
-RUN wget -O - http://apt.llvm.org/llvm-snapshot.gpg.key | apt-key add -
+RUN apt-get update && apt-get -y upgrade && apt-get -y remove g++ gcc && apt-get -y install apt-utils gnupg gnupg2 gnupg1 wget cmake git make lcov bc pkg-config sudo doxygen
 
-RUN apt-get update && apt-get -y upgrade && apt-get -y install clang-8 clang-tools-8 clang-8-doc libclang-common-8-dev libclang-8-dev libclang1-8 clang-format-8 python-clang-8
+RUN apt-get -y install clang-10 clang-format-10
 
-RUN apt-get -y install libparmetis-dev sudo
+RUN apt-get -y install libparmetis-dev petsc-dev slepc-dev
 RUN apt-get clean
 
 RUN rm /usr/bin/cc
 RUN echo "${USER} ALL=(ALL:ALL) NOPASSWD:ALL" > "/etc/sudoers.d/${USER}"
 
-RUN ln -s /usr/bin/clang-format-8 /usr/bin/clang-format
+RUN ln -s /usr/bin/clang-format-10 /usr/bin/clang-format
 
-ENV CC="clang-8" CXX="clang++-8"
+ENV CC="clang-10" CXX="clang++-10"
 
 EOF